feat(core-audit): TraceIdEnrichingAuditLog decorator for OTel correlation
Decorator implementing IAuditLog that auto-populates AuditEntry.correlationId
from the active OTel span via currentTraceId(). Caller-supplied correlationId
always wins (explicit > implicit). eraseSubject passes through unmodified.
Adds @opentelemetry/{api,sdk-trace-base,context-async-hooks} as devDeps for
test infrastructure.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
30
packages/core-audit/src/trace-id-enriching-audit-log.ts
Normal file
30
packages/core-audit/src/trace-id-enriching-audit-log.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import type { AuditEntry } from "@repo/core-shared/audit";
|
||||
import { currentTraceId } from "@repo/core-shared/instrumentation";
|
||||
import type { IAuditLog } from "./audit-log.interface";
|
||||
|
||||
/**
|
||||
* Decorates any IAuditLog by auto-populating AuditEntry.correlationId from
|
||||
* the active OTel span (when present and the caller didn't supply a value).
|
||||
* Caller-supplied correlationId always wins — explicit > implicit.
|
||||
*
|
||||
* Applied at bind time by bindAudit so all sinks see entries with
|
||||
* correlationId already set. Single source of truth for the OTel-audit bridge.
|
||||
*/
|
||||
export class TraceIdEnrichingAuditLog implements IAuditLog {
|
||||
constructor(readonly inner: IAuditLog) {}
|
||||
|
||||
async record(entry: AuditEntry): Promise<void> {
|
||||
if (entry.correlationId) {
|
||||
return this.inner.record(entry);
|
||||
}
|
||||
const traceId = currentTraceId();
|
||||
if (!traceId) {
|
||||
return this.inner.record(entry);
|
||||
}
|
||||
return this.inner.record({ ...entry, correlationId: traceId });
|
||||
}
|
||||
|
||||
eraseSubject(actorId: string, mode: "pseudonymize" | "delete"): Promise<void> {
|
||||
return this.inner.eraseSubject(actorId, mode);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user