Skip to content

Commit 84d322c

Browse files
author
farhadzand
committed
feat: enhance audit logging with user tracking and metadata
- Added sections to README.md detailing authenticated user tracking, including examples for logging user actions and retrieving audit logs with user information. - Introduced advanced user tracking examples in the documentation, demonstrating how to capture user context in various scenarios (HTTP requests, console commands). - Implemented a custom causer resolver for more complex user tracking scenarios. - Created a new test suite to validate user authentication tracking in audit logs, ensuring accurate logging of user actions and metadata. - Enhanced the audit log structure to include user context and metadata for better traceability.
1 parent af1099a commit 84d322c

File tree

2 files changed

+624
-0
lines changed

2 files changed

+624
-0
lines changed

README.md

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,275 @@ final class Order extends Model
256256

257257
Once the trait is added, any changes to the model (create, update, delete, restore) will be automatically logged to a dedicated audit table (e.g., `audit_orders_logs`).
258258

259+
### Authenticated User Tracking
260+
261+
The audit logger automatically tracks WHO made the change and WHERE it came from:
262+
263+
```php
264+
// When a user makes a change via HTTP request
265+
Auth::loginUsingId(1);
266+
$order = Order::create([
267+
'customer_id' => 123,
268+
'total' => 99.99,
269+
'status' => 'pending'
270+
]);
271+
272+
// Audit log will contain:
273+
// - causer_type: "App\Models\User"
274+
// - causer_id: 1
275+
// - source: "App\Http\Controllers\OrderController@store"
276+
```
277+
278+
### Retrieving Audit Logs with User Information
279+
280+
You can easily retrieve audit logs with user information:
281+
282+
```php
283+
// Get audit logs with causer information
284+
$auditLogs = Order::find(1)->auditLogs()
285+
->where('causer_type', 'App\Models\User')
286+
->where('causer_id', 1)
287+
->get();
288+
289+
// Using the EloquentAuditLog model directly
290+
use iamfarhad\LaravelAuditLog\Models\EloquentAuditLog;
291+
292+
$logs = EloquentAuditLog::forEntity(Order::class)
293+
->forCauser('App\Models\User')
294+
->forCauserId(1)
295+
->get();
296+
297+
// Get logs from HTTP requests only
298+
$httpLogs = EloquentAuditLog::forEntity(Order::class)
299+
->fromHttp()
300+
->get();
301+
302+
// Get logs from console commands only
303+
$consoleLogs = EloquentAuditLog::forEntity(Order::class)
304+
->fromConsole()
305+
->get();
306+
```
307+
308+
### Advanced User Tracking Examples
309+
310+
#### Example 1: Track User Changes in Controller
311+
312+
```php
313+
<?php
314+
315+
declare(strict_types=1);
316+
317+
namespace App\Http\Controllers;
318+
319+
use App\Models\Order;
320+
use Illuminate\Http\Request;
321+
322+
final class OrderController extends Controller
323+
{
324+
public function update(Request $request, Order $order): JsonResponse
325+
{
326+
// User is already authenticated via middleware
327+
// Audit log will automatically capture:
328+
// - causer_type: "App\Models\User"
329+
// - causer_id: Auth::id()
330+
// - source: "App\Http\Controllers\OrderController@update"
331+
332+
$order->update($request->validated());
333+
334+
return response()->json(['success' => true]);
335+
}
336+
}
337+
```
338+
339+
#### Example 2: Track System Changes in Console Commands
340+
341+
```php
342+
<?php
343+
344+
declare(strict_types=1);
345+
346+
namespace App\Console\Commands;
347+
348+
use App\Models\Order;
349+
use Illuminate\Console\Command;
350+
351+
final class ProcessOrdersCommand extends Command
352+
{
353+
protected $signature = 'orders:process';
354+
355+
public function handle(): void
356+
{
357+
// For console commands, causer_type and causer_id will be null
358+
// But source will be: "orders:process"
359+
360+
Order::where('status', 'pending')
361+
->each(function ($order) {
362+
$order->update(['status' => 'processed']);
363+
});
364+
}
365+
}
366+
```
367+
368+
#### Example 3: Custom Metadata with User Context
369+
370+
```php
371+
<?php
372+
373+
declare(strict_types=1);
374+
375+
namespace App\Models;
376+
377+
use Illuminate\Database\Eloquent\Model;
378+
use iamfarhad\LaravelAuditLog\Traits\Auditable;
379+
380+
final class Order extends Model
381+
{
382+
use Auditable;
383+
384+
protected $fillable = ['customer_id', 'total', 'status'];
385+
386+
/**
387+
* Add user context to audit metadata
388+
*/
389+
public function getAuditMetadata(): array
390+
{
391+
return [
392+
'ip_address' => request()->ip() ?? 'unknown',
393+
'user_agent' => request()->userAgent() ?? 'unknown',
394+
'user_email' => auth()->user()?->email ?? 'system',
395+
'session_id' => session()->getId() ?? null,
396+
'request_id' => request()->header('X-Request-Id', 'n/a'),
397+
];
398+
}
399+
}
400+
```
401+
402+
#### Example 4: Querying Audit Logs by User and Source
403+
404+
```php
405+
<?php
406+
407+
declare(strict_types=1);
408+
409+
namespace App\Services;
410+
411+
use App\Models\User;
412+
use iamfarhad\LaravelAuditLog\Models\EloquentAuditLog;
413+
414+
final class AuditReportService
415+
{
416+
public function getUserActivity(User $user, string $action = null): array
417+
{
418+
$query = EloquentAuditLog::forCauser(User::class)
419+
->forCauserId($user->id)
420+
->orderBy('created_at', 'desc');
421+
422+
if ($action) {
423+
$query->forAction($action);
424+
}
425+
426+
return $query->get()->map(function ($log) {
427+
return [
428+
'entity_type' => $log->entity_type,
429+
'entity_id' => $log->entity_id,
430+
'action' => $log->action,
431+
'source' => $log->source,
432+
'created_at' => $log->created_at,
433+
'metadata' => $log->metadata,
434+
];
435+
})->toArray();
436+
}
437+
438+
public function getHttpRequestChanges(string $controller = null): array
439+
{
440+
$query = EloquentAuditLog::fromHttp();
441+
442+
if ($controller) {
443+
$query->fromController($controller);
444+
}
445+
446+
return $query->get()->toArray();
447+
}
448+
449+
public function getConsoleChanges(string $command = null): array
450+
{
451+
$query = EloquentAuditLog::fromConsole();
452+
453+
if ($command) {
454+
$query->fromCommand($command);
455+
}
456+
457+
return $query->get()->toArray();
458+
}
459+
}
460+
```
461+
462+
### Configuration for User Tracking
463+
464+
The causer (user) resolution can be configured in the `config/audit-logger.php` file:
465+
466+
```php
467+
'causer' => [
468+
'guard' => null, // null means use default guard, or specify 'api', 'web', etc.
469+
'model' => null, // null means auto-detect, or specify 'App\Models\CustomUser'
470+
'resolver' => null, // null means use default resolver, or specify custom class
471+
],
472+
```
473+
474+
#### Custom Causer Resolver
475+
476+
You can create a custom causer resolver for complex scenarios:
477+
478+
```php
479+
<?php
480+
481+
declare(strict_types=1);
482+
483+
namespace App\Services;
484+
485+
use iamfarhad\LaravelAuditLog\Contracts\CauserResolverInterface;
486+
use Illuminate\Support\Facades\Auth;
487+
488+
final class CustomCauserResolver implements CauserResolverInterface
489+
{
490+
public function resolve(): array
491+
{
492+
// Custom logic for resolving the causer
493+
if (Auth::guard('api')->check()) {
494+
$user = Auth::guard('api')->user();
495+
return [
496+
'type' => get_class($user),
497+
'id' => $user->id,
498+
];
499+
}
500+
501+
if (Auth::guard('web')->check()) {
502+
$user = Auth::guard('web')->user();
503+
return [
504+
'type' => get_class($user),
505+
'id' => $user->id,
506+
];
507+
}
508+
509+
// For system operations, you might want to return a system user
510+
return [
511+
'type' => 'System',
512+
'id' => 'system',
513+
];
514+
}
515+
}
516+
```
517+
518+
Register your custom resolver in a service provider:
519+
520+
```php
521+
// In AppServiceProvider or custom service provider
522+
$this->app->bind(
523+
\iamfarhad\LaravelAuditLog\Contracts\CauserResolverInterface::class,
524+
\App\Services\CustomCauserResolver::class
525+
);
526+
```
527+
259528
#### Excluding Fields
260529

261530
To exclude specific fields from being audited, define the `$auditExclude` property:

0 commit comments

Comments
 (0)