PHPackages                             dineshstack/laravel-audit - 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. dineshstack/laravel-audit

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

dineshstack/laravel-audit
=========================

Activity log &amp; audit trail for Laravel 12/13 — free core on Packagist, pro dashboard on CodeCanyon

v1.0.2(2mo ago)02MITPHPPHP ^8.2

Since Apr 24Pushed 2mo agoCompare

[ Source](https://github.com/dineshstack/laravel-audit)[ Packagist](https://packagist.org/packages/dineshstack/laravel-audit)[ RSS](/packages/dineshstack-laravel-audit/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (11)Versions (3)Used By (0)

dineshstack/laravel-audit
=========================

[](#dineshstacklaravel-audit)

**Activity log &amp; audit trail for Laravel 13.** Auto-log every Eloquent model change — with field-level diffs, IP capture, user attribution, batch grouping, data masking, and a full REST API — in one `composer require`.

[![Latest Version on Packagist](https://camo.githubusercontent.com/df974645965ff097c2a5ec886d0039faa8a11c66f8bf9ba1d2457c1e80205fc4/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f64696e657368737461636b2f6c61726176656c2d61756469742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dineshstack/laravel-audit)[![Total Downloads](https://camo.githubusercontent.com/7b340bfb05695649ca4fcdf596f2d4105883706a8877e72d556fd02cc15c5455/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f64696e657368737461636b2f6c61726176656c2d61756469742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dineshstack/laravel-audit)[![PHP Version](https://camo.githubusercontent.com/ccf0c1ca319dcf6d84fc412c70fcf6ec7d982ed218aee65ac87dace8d03c1a16/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e332d626c75653f7374796c653d666c61742d737175617265)](https://php.net)[![Laravel](https://camo.githubusercontent.com/fe100d91b690e802365121204052bc34d949b6b807ab32056b8685dbe549673a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d31332d7265643f7374796c653d666c61742d737175617265)](https://laravel.com)[![License: MIT](https://camo.githubusercontent.com/458425f8985b0b0c8a736cffe75e05a098e3d77906acddbcad2bfc54492a4e02/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE)[![Tests](https://camo.githubusercontent.com/24124f835ef652afe6ea3c83413fdef34c14c3cc8e3674e294cf6bcc53718ed9/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f74657374732d70617373696e672d627269676874677265656e3f7374796c653d666c61742d737175617265)](#testing)

---

Why this package?
-----------------

[](#why-this-package)

Most Laravel audit packages store a flat JSON blob and call it done. `laravel-audit` goes further:

- **Field-level diffs** — exactly which columns changed, from what value, to what value
- **Sensitive-field masking** — `password`, `api_key`, `card_number`, and any custom field are stored as `[REDACTED]` automatically
- **Batch grouping** — wrap multiple operations in `AuditLog::batch()` and every entry shares a `batch_id` so you can replay a full transaction
- **Alert rules** — define threshold rules (e.g. "more than 10 deletes per minute") that fire Mailgun emails or Slack webhooks
- **Retention pruning** — auto-scheduled daily cleanup via `audit:prune`; configurable retention window
- **REST API** — nine endpoints covering feed, timeline, stats, export (CSV + PDF), and alert rule management, ready to power any dashboard or SIEM integration

---

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

[](#requirements)

DependencyVersionPHP^8.3Laravel^13.0`league/csv`^9.15`dompdf/dompdf`^2.0`guzzlehttp/guzzle`^7.8---

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

[](#installation)

```
composer require dineshstack/laravel-audit
php artisan audit:install
```

`audit:install` publishes `config/audit.php`, publishes and optionally runs the migration, and appends the required environment variable stubs to your `.env` file.

If you prefer to publish assets manually:

```
php artisan vendor:publish --tag=audit-config
php artisan vendor:publish --tag=audit-migrations
php artisan migrate
```

---

Quick start
-----------

[](#quick-start)

### 1. Auto-log any Eloquent model

[](#1-auto-log-any-eloquent-model)

Add the `LogsActivity` trait to the models you want to audit:

```
use Dineshstack\LaravelAudit\Traits\LogsActivity;

class Invoice extends Model
{
    use LogsActivity;
}
```

That's it. Every `create`, `update`, and `delete` on `Invoice` is now logged with the changed attributes, old and new values, the authenticated user, their IP address, user agent, and the full request URL.

### 2. Manual logging

[](#2-manual-logging)

Use the fluent builder for events that aren't model-driven:

```
use Dineshstack\LaravelAudit\Facades\AuditLog;

AuditLog::log('payment.processed')
    ->on($invoice)
    ->with(['amount' => 500, 'currency' => 'USD'])
    ->by($user)
    ->description('Stripe charge succeeded')
    ->save();
```

All methods except `log()` and `save()` are optional:

MethodDescription`->on(Model $subject)`The model being acted upon`->with(array $data)`Arbitrary context stored in `properties``->by(?Model $causer)`Who did it — defaults to `auth()->user()``->description(string $desc)`Human-readable label stored alongside the event key### 3. Batch logging

[](#3-batch-logging)

Wrap multiple operations in `AuditLog::batch()` to link them under a shared `batch_id`. This lets you query or replay an entire multi-step transaction as a unit:

```
AuditLog::batch(function () use ($order) {
    $order->update(['status' => 'shipped']);
    $order->items()->each->update(['dispatched' => true]);
});
```

All entries created inside the closure share the same UUID `batch_id`.

---

Field-level diffs
-----------------

[](#field-level-diffs)

When a model is updated, the package computes a precise diff of only the changed fields:

```
{
  "old": { "status": "pending", "total": 450 },
  "new": { "status": "shipped", "total": 500 },
  "diff": {
    "status": { "old": "pending", "new": "shipped", "type": "changed" },
    "total": { "old": 450, "new": 500, "type": "changed" }
  }
}
```

Unchanged fields are never stored. Added fields report `"type": "added"` with `old: null`; removed fields report `"type": "removed"` with `new: null`.

### Controlling which fields are logged

[](#controlling-which-fields-are-logged)

Override these two methods in your model:

```
class User extends Model
{
    use LogsActivity;

    // Never log these columns
    protected function getAuditExclude(): array
    {
        return ['updated_at', 'created_at', 'remember_token', 'two_factor_secret'];
    }

    // Log ONLY these columns (overrides exclude; empty = log all minus excluded)
    protected function getAuditInclude(): array
    {
        return ['name', 'email', 'role'];
    }
}
```

---

Data masking
------------

[](#data-masking)

Any field whose name contains a masked keyword is stored as `[REDACTED]` in both the `old`/`new` snapshots and the diff. The default masked keywords are:

```
password, token, secret, api_key, card_number, cvv, ssn, remember_token

```

Customize via `.env` (comma-separated, case-insensitive substring matching):

```
AUDIT_MASKED_FIELDS=password,token,secret,api_key,card_number,pin,ssn
```

Or publish and edit `config/audit.php` directly.

---

REST API
--------

[](#rest-api)

All endpoints are prefixed with `/api/audit` and optionally protected by a token set in `AUDIT_TOKEN`. Pass the token as either:

- `X-Audit-Token: your_token` header
- `Authorization: Bearer your_token` header

If `AUDIT_TOKEN` is empty, all endpoints are unauthenticated (suitable for internal networks).

### Endpoints

[](#endpoints)

#### `GET /api/audit/feed`

[](#get-apiauditfeed)

Paginated activity feed with filtering.

**Query parameters:**

ParameterTypeDescription`event`stringFilter by event name (e.g. `update`, `payment.processed`)`causer_id`integerFilter by user ID`subject_type`stringFilter by model class (e.g. `App\Models\Invoice`)`ip_address`stringFilter by IP address`date_from`dateFilter entries on or after this date`date_to`dateFilter entries on or before this date`search`stringFull-text search across description and properties`batch_id`stringShow all entries in a specific batch`per_page`integerResults per page (default: 30, max: 100)`page`integerPage number**Response:**

```
{
  "data": [
    {
      "id": "01hwxyz...",
      "event": "update",
      "description": "Updated Invoice #42",
      "causer_id": 7,
      "causer_name": "Jane Smith",
      "subject_type": "App\\Models\\Invoice",
      "subject_id": "42",
      "ip_address": "192.168.1.1",
      "properties": {
        "old": { "status": "pending" },
        "new": { "status": "paid" },
        "diff": {
          "status": { "old": "pending", "new": "paid", "type": "changed" }
        }
      },
      "created_at": "2025-04-24T09:30:00.000000Z"
    }
  ],
  "meta": { "total": 1420, "per_page": 30, "current_page": 1 },
  "next_page_url": "/api/audit/feed?page=2"
}
```

#### `GET /api/audit/entry/{id}`

[](#get-apiauditentryid)

Single audit entry by ULID — includes full `properties` payload.

#### `GET /api/audit/timeline`

[](#get-apiaudittimeline)

Chronological activity for a specific user, ordered by most recent.

ParameterDescription`causer_id`**Required.** The user's ID`causer_type`Defaults to `App\Models\User``per_page`Default: 30#### `GET /api/audit/stats`

[](#get-apiauditstats)

Aggregated statistics for a date range.

**Response shape:**

```
{
  "total": 12400,
  "by_event": { "create": 3200, "update": 7100, "delete": 400 },
  "top_causers": [
    { "causer_id": 7, "causer_name": "Jane Smith", "count": 840 }
  ],
  "top_subjects": [{ "subject_type": "App\\Models\\Invoice", "count": 2100 }],
  "hourly": { "0": 12, "1": 8, "9": 210, "14": 340 },
  "daily": [{ "day": "2025-04-24", "count": 480 }]
}
```

#### `GET /api/audit/causers`

[](#get-apiauditcausers)

Distinct causers with activity counts — for populating filter dropdowns. Returns the most recently seen name per `causer_id`, so renamed users appear only once.

#### `GET /api/audit/export/csv`

[](#get-apiauditexportcsv)

Exports the filtered log as a `.csv` file. Accepts the same filter parameters as `/feed`. Maximum 10,000 rows per export; exports are tracked for alert rate-limiting.

#### `GET /api/audit/export/pdf`

[](#get-apiauditexportpdf)

Exports a formatted PDF audit trail report. Accepts the same filters as `/feed`. Powered by `dompdf/dompdf`.

#### Alert rule endpoints

[](#alert-rule-endpoints)

MethodEndpointDescription`GET``/api/audit/alerts`List all rules and default thresholds`POST``/api/audit/alerts`Create an alert rule`PUT``/api/audit/alerts/{id}`Update or enable/disable a rule`DELETE``/api/audit/alerts/{id}`Delete a rule`GET``/api/audit/alerts/history`Recent fired-alert history**Create rule request body:**

```
{
  "name": "Bulk delete guard",
  "event_pattern": "delete",
  "metric": "deletes_per_min",
  "threshold": 10,
  "channels": ["email", "slack"]
}
```

---

Alert rules
-----------

[](#alert-rules)

Alert rules fire when a threshold is exceeded. The package checks patterns synchronously after every `AuditEntry` is saved.

### Default thresholds (configurable via `.env`)

[](#default-thresholds-configurable-via-env)

RuleDefaultEnvironment variableDeletes per minute10`AUDIT_ALERT_DELETES_PER_MIN`Logins per minute20`AUDIT_ALERT_LOGINS_PER_MIN`Updates per minute50`AUDIT_ALERT_UPDATES_PER_MIN`Exports per hour5`AUDIT_ALERT_EXPORTS_PER_HOUR`Custom rules created via the API override these defaults for matching event patterns.

### Notification channels

[](#notification-channels)

**Mailgun (email):**

```
AUDIT_MAILGUN_API_KEY=key-xxx
AUDIT_MAILGUN_DOMAIN=mg.yourdomain.com
AUDIT_ALERT_FROM=audit@yourdomain.com
AUDIT_ALERT_TO=security@yourdomain.com
```

**Slack:**

```
AUDIT_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz
```

---

Retention pruning
-----------------

[](#retention-pruning)

Logs are automatically pruned daily by a scheduled command registered in the service provider. The retention window defaults to 90 days:

```
AUDIT_RETENTION_DAYS=90
```

Run pruning manually at any time:

```
php artisan audit:prune --days=90
```

Ensure your Laravel scheduler is running:

```
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
```

---

Database schema
---------------

[](#database-schema)

Three tables are created by the package migration:

### `audit_logs`

[](#audit_logs)

ColumnTypeDescription`id`ULID (PK)Universally unique, lexicographically sortable`event`stringEvent key, e.g. `update`, `payment.processed``description`stringHuman-readable label`batch_id`stringGroups entries from one `AuditLog::batch()` call`subject_type`stringAudited model class`subject_id`stringAudited model primary key`causer_type`stringActor model class (usually `App\Models\User`)`causer_id`integerActor primary key`causer_name`stringSnapshot of the actor's name at time of action`properties`JSON`old`, `new`, `diff`, and any custom `with()` data`ip_address`stringRequest IP (supports IPv6)`user_agent`textFull user-agent string`url`textFull request URL`method`stringHTTP method`created_at`timestampIndexed for fast date-range queriesIndexes on: `event`, `batch_id`, `ip_address`, `created_at`, and composite morphs indexes on `(subject_type, subject_id)` and `(causer_type, causer_id)`.

### `audit_alert_rules`

[](#audit_alert_rules)

Stores custom threshold rules created via the API.

### `audit_alert_history`

[](#audit_alert_history)

Records every fired alert with the rule name, metric value, threshold breached, and the channels notified.

---

Configuration reference
-----------------------

[](#configuration-reference)

After publishing with `php artisan vendor:publish --tag=audit-config`, your `config/audit.php` exposes:

```
return [
    // Bearer token protecting the REST API (leave empty to disable auth)
    'token' => env('AUDIT_TOKEN', ''),

    // Prune logs older than this many days
    'retention_days' => env('AUDIT_RETENTION_DAYS', 90),

    // Fields redacted in stored diffs (case-insensitive substring match)
    'masked_fields' => ['password', 'token', 'secret', 'api_key',
                        'card_number', 'cvv', 'ssn', 'remember_token'],

    // Default alert thresholds (overridden by DB rules per event pattern)
    'alert_thresholds' => [
        'deletes_per_min'  => 10,
        'logins_per_min'   => 20,
        'exports_per_hour' => 5,
        'updates_per_min'  => 50,
    ],

    // Active notification channels
    'alert_channels' => ['email', 'slack'],

    // Mailgun + Slack credentials
    'alerts' => [
        'email' => [
            'mailgun_key'    => env('AUDIT_MAILGUN_API_KEY'),
            'mailgun_domain' => env('AUDIT_MAILGUN_DOMAIN'),
            'from'           => env('AUDIT_ALERT_FROM', 'audit@yourdomain.com'),
            'to'             => env('AUDIT_ALERT_TO'),
        ],
        'slack' => [
            'webhook_url' => env('AUDIT_SLACK_WEBHOOK_URL'),
        ],
    ],
];
```

---

Testing
-------

[](#testing)

The package ships with a PHPUnit test suite covering `DiffService` and `MaskingService`:

```
composer install
vendor/bin/phpunit
```

**Test coverage includes:**

- Detecting changed, added, and removed fields in diffs
- Returning an empty diff for identical attribute sets
- Redacting password and token fields (including nested arrays)
- Case-insensitive masking of fields like `Password` and `API_KEY`

To run tests against a specific Laravel application using Testbench:

```
composer require --dev orchestra/testbench
vendor/bin/phpunit
```

---

Pro Dashboard
-------------

[](#pro-dashboard)

The free package exposes the REST API. To get a full visual interface — activity feed, user timeline, statistics charts, field diff viewer, CSV/PDF export, and alert rule management — pick up the **Pro Dashboard**:

👉 **[dineshstack.com/laravel-audit](https://dineshstack.com/laravel-audit)**

Built with Next.js 16, React 19, Recharts, and TanStack Query. Self-hosted, dark/light mode, Docker-ready.

---

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for recent changes.

Contributing
------------

[](#contributing)

Pull requests are welcome. Please open an issue first to discuss significant changes. All contributions must include tests.

Security
--------

[](#security)

If you discover a security vulnerability, please email **** instead of opening a public issue. All disclosures are reviewed within 48 hours.

License
-------

[](#license)

MIT. See [LICENSE](LICENSE) for the full text.

Author
------

[](#author)

**Dinesh Wijethunga** — [dineshwijethunga.me](https://dineshwijethunga.me)

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance83

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 54.5% of commits — single point of failure

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

Every ~3 days

Total

2

Last Release

88d ago

PHP version history (2 changes)v1.0.0PHP ^8.3

v1.0.2PHP ^8.2

### Community

Maintainers

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

---

Top Contributors

[![dineshstack](https://avatars.githubusercontent.com/u/236544910?v=4)](https://github.com/dineshstack "dineshstack (6 commits)")[![dlwije](https://avatars.githubusercontent.com/u/16048132?v=4)](https://github.com/dlwije "dlwije (3 commits)")[![DishStack-dev](https://avatars.githubusercontent.com/u/194072078?v=4)](https://github.com/DishStack-dev "DishStack-dev (2 commits)")

---

Tags

laravelAuditaudit-trailactivity-logcompliance

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

###  Alternatives

[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M133](/packages/roots-acorn)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M136](/packages/laravel-pulse)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[spatie/laravel-health

Monitor the health of a Laravel application

87912.0M177](/packages/spatie-laravel-health)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

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