nuewire / logs
Platform audit trails, HTTP request logs, and Laravel system log viewer for Nuewire.
Requires
- php: ^8.2
- illuminate/auth: ^11.0|^12.0|^13.0
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/filesystem: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/pagination: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- illuminate/translation: ^11.0|^12.0|^13.0
- illuminate/view: ^11.0|^12.0|^13.0
- livewire/livewire: ^3.6|^4.0.3
- nuewire/support: ^1.0
- spatie/laravel-activitylog: ^4.10.2|^5.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^11.0|^12.0
Suggests
- nuewire/acl: Adds granular permissions for each logs page and destructive log actions.
- nuewire/platform: Adds System Logs, Request Logs, and Audit Trails under Settings > Platform.
README
Platform logging package for the Nuewire Laravel suite.
Panduan implementasi berbahasa Indonesia tersedia di docs/PANDUAN.md.
It registers three pages under Settings → Platform when nuewire/platform 2 is installed:
- Audit Trails — user actions and model changes through
spatie/laravel-activitylog. - Request Logs — HTTP method, path, route, status, duration, user, IP, and optional sanitized request data.
- System Logs — a bounded, read-only-by-default viewer for Laravel
.logfiles.
Compatibility
PHP Laravel spatie/laravel-activitylog
8.2–8.3 11–12 v4
8.4+ 11 v4
8.4+ 12 v4 or v5
8.4+ 13 v5
The package constraint is ^4.10.2|^5.0. Composer resolves the compatible Activitylog major for the application's Laravel and PHP versions.
Install
composer require nuewire/logs php artisan nuewire:logs:install --migrate php artisan optimize:clear
The install command publishes Spatie's version-specific config and migration. Nuewire's request-log migration is loaded automatically by the package.
Without --migrate, run migrations separately:
php artisan nuewire:logs:install php artisan migrate
Components
<livewire:nuewire-audit-trails /> <livewire:nuewire-request-logs /> <livewire:nuewire-system-logs />
With nuewire/platform 2, these components appear automatically as:
Settings
└── Platform
├── System Logs
├── Request Logs
└── Audit Trails
Recommended audit API
Use Nuewire\Logs\Support\AuditLogger inside controllers, actions, services, jobs, listeners, or Livewire actions. This wrapper uses Spatie's stable activity() API and sanitizes sensitive property keys configured in nuewire.logs.audit.sensitive_keys.
Record an update in a controller or service
Capture the old state before persistence, write the change, then log only after the operation succeeds.
use App\Models\User; use Illuminate\Http\Request; use Nuewire\Logs\Support\AuditLogger; final class UpdateUserProfile { public function __construct( private readonly AuditLogger $audit, ) { } public function handle(Request $request, User $user, array $validated): User { $before = $user->only(['name', 'email']); $user->fill($validated)->save(); $this->audit->record( description: 'user profile updated', subject: $user, properties: [ 'old' => $before, 'attributes' => $user->only(['name', 'email']), 'request_id' => $request->header('X-Request-Id'), ], causer: $request->user(), event: 'updated', logName: 'users', ); return $user; } }
Record a domain action
Not every audit record represents a CRUD event. Use a specific event and description for approvals, exports, impersonation, permission changes, or configuration changes.
$audit->record( description: 'purchase order approved', subject: $purchaseOrder, properties: [ 'attributes' => [ 'status' => 'approved', 'approved_at' => now()->toIso8601String(), ], 'old' => ['status' => $previousStatus], ], causer: auth()->user(), event: 'approved', logName: 'purchase-orders', );
Record a change without an Eloquent subject
This is useful for platform settings, exports, maintenance actions, or external resources.
$audit->record( description: 'mail provider changed', properties: [ 'old' => ['driver' => $oldDriver], 'attributes' => ['driver' => $newDriver], ], causer: auth()->user(), event: 'settings.updated', logName: 'platform-settings', );
Insert audit logging into a Livewire action
Laravel's container can inject AuditLogger into a public Livewire action. Keep the log after the successful state change.
use Illuminate\Support\Facades\Auth; use Nuewire\Logs\Support\AuditLogger; public function save(AuditLogger $audit): void { $this->validate(); $before = [ 'timezone' => $this->settings->timezone, ]; $this->settings->update([ 'timezone' => $this->timezone, ]); $audit->record( description: 'platform timezone updated', subject: $this->settings, properties: [ 'old' => $before, 'attributes' => ['timezone' => $this->timezone], ], causer: Auth::user(), event: 'updated', logName: 'platform-settings', ); }
For a database transaction, log inside the transaction after the write, or after commit when the audit row must never outlive a rolled-back domain change.
use Illuminate\Support\Facades\DB; DB::transaction(function () use ($audit, $order): void { $order->update(['status' => 'cancelled']); $audit->record( description: 'order cancelled', subject: $order, causer: auth()->user(), event: 'cancelled', logName: 'orders', ); });
Convenience methods
$audit->created($model, ['attributes' => $model->only(['name'])], auth()->user()); $audit->updated($model, ['old' => $old, 'attributes' => $new], auth()->user()); $audit->deleted($model, ['old' => $snapshot], auth()->user());
Use Spatie directly
The underlying package remains available when advanced Activitylog features are needed.
activity('orders') ->performedOn($order) ->causedBy(auth()->user()) ->event('approved') ->withProperties([ 'old' => ['status' => 'pending'], 'attributes' => ['status' => 'approved'], ]) ->log('order approved');
Direct Spatie calls do not pass through Nuewire's AuditLogger sanitizer. Never include passwords, tokens, cookies, authorization headers, API secrets, payment secrets, or unnecessary personal data.
Automatic Eloquent model logging
Use automatic model logging when ordinary Eloquent create/update/delete events are the audit boundary. Prefer manual domain logs for semantic actions such as approved, published, refunded, or permission.granted.
Activitylog v4
use Illuminate\Database\Eloquent\Model; use Spatie\Activitylog\LogOptions; use Spatie\Activitylog\Traits\LogsActivity; final class Product extends Model { use LogsActivity; public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->useLogName('products') ->logOnly(['name', 'price', 'status']) ->logOnlyDirty() ->dontSubmitEmptyLogs(); } }
Activitylog v5
use Illuminate\Database\Eloquent\Model; use Spatie\Activitylog\Models\Concerns\LogsActivity; use Spatie\Activitylog\Support\LogOptions; final class Product extends Model { use LogsActivity; public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->useLogName('products') ->logOnly(['name', 'price', 'status']) ->logOnlyDirty() ->dontLogEmptyChanges(); } }
Do not use logAll() blindly on models containing credentials, internal tokens, encrypted payloads, large JSON documents, or high-volume counters. Explicit allowlists are safer and produce more useful audit records.
Request Logs
RecordRequestLog is appended to Laravel's global HTTP middleware stack by default. It records after the response has been produced and never replaces the application response when log persistence fails.
Default fields:
- UUID request ID, also returned as
X-Request-Id. - HTTP method, path, route name, status code, and duration.
- IP address, user agent, authenticated user type and ID.
- Query string after sensitive-key redaction.
- Exception class when a request terminates with an exception.
Request payloads and headers are disabled by default. Enable them only after reviewing privacy and retention requirements:
NUEWIRE_REQUEST_LOG_ENABLED=true NUEWIRE_REQUEST_LOG_AUTO_MIDDLEWARE=true NUEWIRE_REQUEST_LOG_RETENTION_DAYS=30 NUEWIRE_REQUEST_LOG_SLOW_MS=1000
Publish the config to change capture policy, exclusions, allowlisted headers, or sensitive keys:
php artisan vendor:publish --tag=nuewire-logs-config
To register the middleware manually:
use Nuewire\Logs\Http\Middleware\RecordRequestLog; // bootstrap/app.php ->withMiddleware(function ($middleware): void { $middleware->append(RecordRequestLog::class); })
Set request.auto_register_middleware to false when registering it manually, otherwise requests can be logged twice.
System Logs
System Logs discovers only configured extensions beneath configured root directories. File identifiers are hashes; clients cannot submit an arbitrary filesystem path. Symlinks resolving outside a configured root are rejected.
Default root:
'paths' => [storage_path('logs')], 'extensions' => ['log'],
The reader tails a bounded number of lines rather than loading the entire file. Clearing a selected file requires logs.system.delete.
Keep log directories outside public web roots, restrict the page with ACL, and avoid writing secrets to Laravel logs in the first place.
Permissions
When nuewire/acl is enabled, the package registers:
logs.audit.view
logs.audit.delete
logs.requests.view
logs.requests.delete
logs.system.view
logs.system.delete
Without ACL, the package still requires an authenticated user by default. A custom gate can be configured:
NUEWIRE_LOGS_GATE=manage-platform-logs NUEWIRE_LOGS_GUARD=web
Retention
Prune expired database logs manually:
php artisan nuewire:logs:prune php artisan nuewire:logs:prune --audit-days=180 --request-days=14
Schedule pruning in routes/console.php:
use Illuminate\Support\Facades\Schedule; Schedule::command('nuewire:logs:prune')->dailyAt('02:30');
A retention value of 0 disables pruning for that log type.
Publish
php artisan vendor:publish --tag=nuewire-logs-config php artisan vendor:publish --tag=nuewire-logs-views php artisan vendor:publish --tag=nuewire-logs-translations
Published paths:
config/nuewire/logs.php
resources/views/vendor/nuewire/logs
lang/vendor/nuewire/logs
Operational guidance
- Treat audit trails as append-only operational evidence; restrict deletion permissions.
- Log domain outcomes, not merely button clicks.
- Use explicit attribute allowlists and record both
oldandattributesonly when they add investigative value. - Avoid request payload capture unless it is necessary and legally justified.
- Apply retention based on security, privacy, incident-response, and regulatory requirements.
- For high traffic, move request-log persistence to an asynchronous pipeline or an observability backend rather than growing the primary application database without bounds.