PHPackages                             mxnwire/laravel-audit-log - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Logging &amp; Monitoring](/categories/logging)
4. /
5. mxnwire/laravel-audit-log

ActiveLibrary[Logging &amp; Monitoring](/categories/logging)

mxnwire/laravel-audit-log
=========================

Activity log viewer layered on top of spatie/laravel-activitylog

1.0.4(yesterday)012↑2650%MITPHPPHP ^8.1

Since Jun 13Pushed yesterdayCompare

[ Source](https://github.com/mhxnahid/laravel-audit-log)[ Packagist](https://packagist.org/packages/mxnwire/laravel-audit-log)[ RSS](/packages/mxnwire-laravel-audit-log/feed)WikiDiscussions master Synced today

READMEChangelogDependencies (4)Versions (2)Used By (0)

mxnwire/laravel-audit-log
=========================

[](#mxnwirelaravel-audit-log)

Activity log viewer and logger layered on top of [spatie/laravel-activitylog](https://github.com/spatie/laravel-activitylog).

Spatie owns the storage (`activity_log` table, polymorphic causer/subject, and the `activitylog:clean` prune command). This package adds:

- A `activity_log()` helper and `ActivityLogService` that freeze actor identity and request context at write time.
- A built-in viewer UI (filterable table, no JS build step required) protected by a permission gate.
- `ActivityMetadata` / `Change` value objects for structured before/after diffs in the `properties` column.

---

Requirements
------------

[](#requirements)

DependencyVersionPHP^8.1Laravel10 or 11spatie/laravel-activitylog^4.0---

Installation
------------

[](#installation)

**1. Install the package via Composer:**

```
composer require mxnwire/laravel-audit-log
```

The service provider is auto-discovered — no manual registration needed.

**2. Install and run spatie's migration:**

If you have not already done so for `spatie/laravel-activitylog`:

```
php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations"
php artisan migrate
```

**3. Publish the package assets (optional but recommended):**

```
php artisan vendor:publish --tag="audit-log"
```

This copies two things:

SourceDestination`config/audit-log.php``config/audit-log.php``resources/views/``resources/views/vendor/audit-log/`You only need to publish if you want to customise views or the config file. The package works out of the box without publishing.

---

Configuration
-------------

[](#configuration)

After publishing, edit `config/audit-log.php`:

```
return [

    // The ability string checked by the route middleware `can:X`.
    // Must be a permission defined in your host app (e.g. via spatie/laravel-permission).
    // Can also be set via the AUDIT_LOG_GATE environment variable.
    'gate' => env('AUDIT_LOG_GATE', 'ACTIVITY_LOGS_ALL'),

    // A callable that resolves the actor's role label from a User model instance.
    // null = falls back to $user->role ?? $user->urole ?? null.
    'role_resolver' => null,

    // URL prefix for the viewer routes.
    // Changing this also renames the named routes `audit-log.index` and `audit-log.data`.
    'route_prefix' => 'mxn/audit-logs',

    // Eloquent model used to populate the "User" filter dropdown.
    // The model must have `id` and `name` columns.
    // Set to null to hide the user filter entirely.
    'user_model' => 'App\\Models\\User',

    // Blade layout the viewer page extends.
    // Override to wrap the viewer inside your own app shell.
    // The layout must @yield('content') and @yield('script').
    'layout' => 'audit-log::layouts.app',

];
```

### Environment variable

[](#environment-variable)

VariableDefaultDescription`AUDIT_LOG_GATE``ACTIVITY_LOGS_ALL`Permission gate for the viewer routes---

Usage
-----

[](#usage)

### Logging an activity

[](#logging-an-activity)

Use the global helper (available automatically — no import needed):

```
activity_log('user.login');
```

Or inject the service directly:

```
use Mxnwire\AuditLog\Services\ActivityLogService;

class AuthController extends Controller
{
    public function __construct(private ActivityLogService $auditLog) {}

    public function login(Request $request)
    {
        // ... authenticate ...
        $this->auditLog->log('user.login');
    }
}
```

#### Signature

[](#signature)

```
activity_log(
    string $action,                      // dotted verb, e.g. 'broadsheet.viewed'
    ?Model $subject = null,              // the record acted on
    ?ActivityMetadata $metadata = null,  // structured diff / extra fields
    ?string $description = null          // optional human-readable label
): void
```

The dotted action maps to spatie's two indexable columns:

```
'broadsheet.updated'  →  log_name = 'broadsheet'
                         event    = 'updated'

'login'               →  log_name = 'login'
                         event    = null

```

The `__actor` and `__request` context is always merged into spatie's `properties` JSON column automatically:

```
{
  "__actor":   { "name": "Jane Smith", "role": "admin" },
  "__request": { "method": "POST", "route": "posts.store", "url": "...", "ip": "...", "user_agent": "..." }
}
```

---

### Adding structured metadata

[](#adding-structured-metadata)

Use `ActivityMetadata` to attach typed field-level detail to a log entry.

**Arbitrary fields:**

```
use Mxnwire\AuditLog\Activity\Metadata\ActivityMetadata;

activity_log(
    'user.login',
    subject: $user,
    metadata: ActivityMetadata::make(['via' => 'password'])
);
```

**Before/after diff for a specific field:**

```
use Mxnwire\AuditLog\Activity\Metadata\ActivityMetadata;
use Mxnwire\AuditLog\Activity\Metadata\Change;

activity_log(
    'subscription.updated',
    subject: $subscription,
    metadata: ActivityMetadata::make([
        'level' => Change::make($old->level, $new->level),
    ])
);
```

Stored in `properties` as:

```
{ "level": { "old": 2, "new": 5 } }
```

**Auto-diff two arrays (keeps only changed keys):**

```
$metadata = ActivityMetadata::diff(
    $record->getOriginal(),  // before
    $record->getAttributes() // after
);

activity_log('post.updated', subject: $record, metadata: $metadata);
```

You can restrict which keys are diffed with the optional third argument:

```
ActivityMetadata::diff($before, $after, keys: ['title', 'status', 'published_at']);
```

**Fluent chaining:**

```
ActivityMetadata::diff($before, $after)
    ->with('trigger', 'bulk-import')
    ->with('row_count', 500);
```

---

### Viewing logs

[](#viewing-logs)

The package registers two routes automatically:

RouteNamed routeDescription`GET /mxn/audit-logs``audit-log.index`Viewer page`GET /mxn/audit-logs/data``audit-log.data`JSON data endpoint for the tableBoth routes are protected by `auth` and `can:{gate}` middleware. The gate defaults to `ACTIVITY_LOGS_ALL`; change it via `AUDIT_LOG_GATE` in your `.env` or in the published config.

---

Customisation
-------------

[](#customisation)

### Custom viewer layout

[](#custom-viewer-layout)

To embed the viewer inside your own app shell, set `layout` in the config to any Blade layout that yields `content` and `script`:

```
'layout' => 'layouts.app',
```

Your layout must contain:

```
@yield('content')
@yield('script')
```

### Custom route prefix

[](#custom-route-prefix)

```
'route_prefix' => 'admin/audit-logs',
```

The named routes (`audit-log.index`, `audit-log.data`) follow the prefix automatically.

### Custom role resolver

[](#custom-role-resolver)

If your app does not use a `role` or `urole` attribute directly on the User model:

```
'role_resolver' => fn ($user) => $user->roles->first()?->name,
```

### Custom activity type registry

[](#custom-activity-type-registry)

The package resolves filter options (log names, events, subject types) from the `activity_log` table at runtime via `ActivityTypeRegistry`. To provide a static list instead — or to customise the queries — bind your own implementation in a service provider:

```
use Mxnwire\AuditLog\Contracts\ActivityTypeRegistryContract;

$this->app->bind(ActivityTypeRegistryContract::class, MyCustomRegistry::class);
```

Your class must implement `logNames(): array`, `events(): array`, and `subjectTypes(): array`.

---

Development
-----------

[](#development)

### Running the tests

[](#running-the-tests)

The test suite uses [Orchestra Testbench](https://github.com/orchestral/testbench) and an in-memory SQLite database — no external database or running Laravel app is required.

**1. Install dev dependencies (first time only):**

```
composer install
```

**2. Run the full suite:**

```
composer test
```

Or call PHPUnit directly:

```
./vendor/bin/phpunit
```

**3. Run a single suite:**

```
./vendor/bin/phpunit --testsuite Unit
./vendor/bin/phpunit --testsuite Feature
```

**4. Run a single test file:**

```
./vendor/bin/phpunit tests/Unit/ActivityMetadataTest.php
./vendor/bin/phpunit tests/Unit/ChangeTest.php
./vendor/bin/phpunit tests/Feature/ActivityLogServiceTest.php
```

**5. Run a single test by name:**

```
./vendor/bin/phpunit --filter "test_name_here"
```

### Test structure

[](#test-structure)

PathWhat it covers`tests/Unit/ActivityMetadataTest.php``ActivityMetadata` value object — make, diff, with`tests/Unit/ChangeTest.php``Change` value object — serialisation`tests/Feature/ActivityLogServiceTest.php``ActivityLogService` end-to-end against a real SQLite DB`tests/TestCase.php`Base case — boots Testbench, runs spatie migrations in-memory---

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/45322767?v=4)[mhxnahid](/maintainers/mhxnahid)[@mhxnahid](https://github.com/mhxnahid)

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/mxnwire-laravel-audit-log/health.svg)

```
[![Health](https://phpackages.com/badges/mxnwire-laravel-audit-log/health.svg)](https://phpackages.com/packages/mxnwire-laravel-audit-log)
```

###  Alternatives

[spatie/laravel-health

Monitor the health of a Laravel application

87311.3M150](/packages/spatie-laravel-health)[illuminate/log

The Illuminate Log package.

6225.0M597](/packages/illuminate-log)[spatie/laravel-flare

Send Laravel errors to Flare

101.2M6](/packages/spatie-laravel-flare)[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1232.2k16](/packages/fleetbase-core-api)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
