Audit Trail¶
The Audit Trail module provides opt-in audit logging for dispatch handlers and a standard interface for querying audit entries.
Quick Start¶
using Deepstaging.Dispatch;
using Deepstaging.Effects;
using Deepstaging.Audit;
public static class OrderCommands
{
[CommandHandler(Audited = true)]
public static Eff<AppRuntime, OrderUpdated> Handle(UpdateOrder cmd) => ...
}
[EffectsModule(typeof(IAuditStore))]
public sealed partial class AuditEffects;
When Audited = true, the dispatch pipeline records an AuditEntry with actor, action, entity snapshots (before/after), metadata, and timestamp.
Pipeline order: authorize → validate → handler → auto-commit → audit.
Interface¶
| Method | Returns | Description |
|---|---|---|
SaveAsync(entry) |
Task |
Record an audit entry |
GetByEntityAsync(entityType, entityId, offset?, limit?) |
Task<IReadOnlyList<AuditEntry>> |
Query by entity |
GetByActorAsync(actor, offset?, limit?) |
Task<IReadOnlyList<AuditEntry>> |
Query by actor |
GetByActionAsync(action, from, to, offset?, limit?) |
Task<IReadOnlyList<AuditEntry>> |
Query by action in time range |
Usage¶
// Query audit history for an entity
from entries in AuditEffects.AuditStore.GetByEntityAsync<AppRuntime>(
"Order", orderId, offset: 0, limit: 50)
select entries;
Templating¶
Use Scriban templates to format human-readable audit trail messages from structured AuditEntry data. Templates keep audit message formatting consistent and centralized.
{{- # Templates/Audit/EntityChanged.scriban-txt -}}
{{ actor }} {{ action | string.downcase }} {{ entity_type }} {{ entity_id }}
{{- if changes }}:
{{ for change in changes }}
{{ change.field }}: {{ change.old_value }} → {{ change.new_value }}
{{ end }}
{{- end }}
This produces messages like: admin@example.com updated Order ORD-123: Status: Pending → Shipped
For syntax reference and best practices, see Scriban Templating.
Automatic vs. manual audit
Set Audited = true on dispatch handlers for automatic audit trail. For manual entries outside the dispatch pipeline, call AuditEffects.AuditStore.SaveAsync directly.