Skip to main content

User action audit

In admin-gui-backend, user action audit is sent through the ensi/action-audit-event-producer package to the central action-audit service over Kafka.

When to add audit

Add audit for any request that changes data state:

  • create, update, delete, replace;
  • status changes and other significant flags;
  • attaching and removing files or relations;
  • bulk operations;
  • authentication actions that matter for traceability, such as login and logout;
  • settings changes.

Do not add audit for read-only endpoints:

  • search, list, get, and meta endpoints;
  • enum value lookups;
  • helper endpoints that do not change state.

In this service, audit calls live in HTTP controllers, not in domain actions.

How the package works

  • AuditEvent::log() adds an event to an in-memory queue.
  • The queue is automatically flushed in Laravel terminating.
  • Sending happens after the response has already been returned to the client.
  • If AUDIT_EVENTS_ENABLED=false, AuditEvent::log() is a no-op.

Do not call flush() manually in service code.

What is already configured

The package is already wired into the service:

  • AppServiceProvider binds AuditResolversInterface to App\Domain\Audit\AuditResolver;
  • config/audit-event-producer.php reads:
    • AUDIT_EVENTS_ENABLED
    • AUDIT_EVENTS_SYSTEM_CODE
    • AUDIT_EVENTS_KAFKA_TOPIC
    • AUDIT_EVENTS_CACHE_STORE
  • in config/kafka.php, the audit-events key is mapped to the action-audit Kafka topic.

If you add a new environment or deployment profile, make sure these settings are present there as well.

How to call audit

Use the facade:

use Ensi\ActionAuditEventProducer\AuditEvent;
use Ensi\ActionAuditEventProducer\DTOs\PerformerDto;

AuditEvent::log(
actionType: AuditActions::ORDERS_PATCH->value,
entityType: AuditEntityType::ORDER->value,
entityId: (string) $id,
entityTitle: fn () => $order->order->getNumber(),
context: fn () => $request->validated(),
);

Parameter rules:

  • actionType is required and must come from App\Domain\Audit\Enums\AuditActions;
  • entityType is optional and should come from App\Domain\Audit\Enums\AuditEntityType;
  • entityId should be a string;
  • entityTitle is optional and should return a short human-readable label;
  • context is optional and should return a small array with the important change details;
  • performer is optional and should be used only when the default authenticated user is not the real performer.

entityTitle and context are Closures. They are evaluated only at flush time, so keep them side-effect free and lightweight.

When to pass performer explicitly

For normal authenticated requests, do not pass performer. By default, the package resolves the performer through App\Domain\Audit\AuditResolver.

Use an explicit PerformerDto only when the action is performed by someone other than the current request user. The current example in the service is login:

AuditEvent::log(
AuditActions::AUTH_LOGIN->value,
context: fn () => [
'ip' => $request->ip(),
'user-agent' => $request->userAgent(),
],
performer: new PerformerDto($request->getLogin()),
);

Adding new actions and entities

If you add a new auditable action or a new entity type:

  1. Add a new case to App\Domain\Audit\Enums\AuditActions or AuditEntityType.
  2. Add translations in:
    • resources/lang/en/audit-actions.php
    • resources/lang/ru/audit-actions.php
    • resources/lang/en/audit-entities.php
    • resources/lang/ru/audit-entities.php
  3. Use the new enum value in the controller.

AuditResolver reads those translation files and sends localized names to the audit service.

Where to place the call

Place AuditEvent::log() next to the state-changing action in the controller. The common sequence is:

  1. execute the domain action;
  2. log audit using request data or the action result;
  3. return the response.

Typical examples in this service:

  • customer create, update, and delete flows;
  • order patch and order item or file operations;
  • CMS and catalog mutations;
  • user and role management changes;
  • audit settings updates.

What to put in payload

Keep the payload focused and useful:

  • include only what helps explain what changed;
  • prefer IDs, codes, numbers, and short labels;
  • do not send passwords, tokens, secrets, or oversized structures.