Series MapLesson 13 / 62
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Build CoreOrdered learning track

Audit Logging

Audit Logging for Business, Security, and Compliance Evidence

Audit log purpose, business/security/compliance audit, who/what/when/where/why, before-after values, actor, target entity, immutability, retention, and production review checklist.

19 min read3674 words
PrevNext
Lesson 1362 lesson track13–34 Build Core
#observability#audit-log#compliance#security-audit+5 more

Part 013 — Audit Logging

1. Core Idea

Audit logging is evidence logging.

Normal application logs answer operational questions:

  • why did this request fail?
  • which dependency was slow?
  • which exception happened?
  • which pod processed this request?
  • which trace contains the failure path?

Audit logs answer accountability questions:

  • who did what?
  • to which business object?
  • when did it happen?
  • from where did it happen?
  • why was it allowed?
  • what changed?
  • what was the previous value?
  • what was the new value?
  • was the action successful or rejected?
  • was the action performed directly, by delegation, by impersonation, by a system actor, or by a background job?

For CPQ and order-management systems, audit logging is not decoration. It is part of production defensibility.

When a quote price changes, an approval is overridden, an order is cancelled, or a fulfillment fallout is manually resolved, the system must preserve a durable explanation of the action.

The rule is simple:

Operational logs explain runtime behavior. Audit logs preserve accountable business and security facts.


2. Audit Log vs Application Log

Audit logs and application logs may share fields such as timestamp, request ID, trace ID, tenant, actor, and service name. But they are not the same signal.

DimensionApplication logAudit log
Main purposeDebugging and operationsAccountability and evidence
AudienceEngineers, SRE, supportEngineering, support, security, compliance, business operations
Typical retentionShorter, cost-sensitiveLonger, policy-driven
MutabilityOften append-only in log backend, but not always controlled as evidenceShould be append-only, access-controlled, and tamper-resistant
ContentRuntime event, error, state, dependency, latencyActor, action, target, before/after, reason, outcome
Failure questionWhy did the system fail?Who changed what and under what authority?
Query patternCorrelation ID, trace ID, error code, service, podActor, target entity, business key, action, timestamp, tenant
Privacy riskHighVery high because audit often references business actions and actors
Alerting useOperational alertingSecurity/compliance anomaly detection, sometimes operational escalation

A common mistake is treating audit as logger.info("user updated quote"). That is not sufficient.

An audit event must be structured, durable, queryable, and semantically stable.


3. What Makes an Event Auditable

A useful audit event needs enough information to reconstruct accountability.

The minimum model is:

actor + action + target + outcome + time + context

A stronger model is:

who + did what + to what + when + where + why + before + after + outcome + correlation

Audit events should usually include:

FieldPurpose
audit_event_idUnique ID of the audit event
timestampWhen the event was recorded
occurred_atWhen the business action occurred, if different
service.nameService producing the audit event
environmentdev/test/stage/prod
tenant.idTenant/account boundary, if applicable
actor.typeuser, service, system, batch, support, delegated user
actor.idStable actor identifier
actor.display_nameOptional, if allowed by policy
actor.rolesOptional; can be sensitive and high-volume
actionStable action name
target.typequote, order, approval, user, entitlement, config
target.idStable target identifier
business_keyquote ID, order ID, external order reference, process ID
outcomesuccess, failure, rejected, no-op, partial
reason_codeWhy action was allowed/rejected, when applicable
beforePrevious value or safe diff
afterNew value or safe diff
source.ipClient IP, if allowed and normalized
user_agentOptional; may be noisy but useful for security audit
request.idRequest ID
correlation.idEnd-to-end correlation
trace.idDistributed trace ID
span.idSpan ID, if available
process_instance_idWorkflow/process context
message.idAsync message context
integrity_hashOptional tamper-evidence field

Do not blindly include all fields. Audit design must respect privacy, policy, retention, cost, and query needs.


4. Business Audit, Security Audit, and Compliance Audit

Audit logging has different purposes. Do not mix them into one vague event type.

Business Audit

Business audit captures accountable business actions.

Examples:

  • quote created
  • quote priced
  • discount overridden
  • approval requested
  • approval approved
  • approval rejected
  • order submitted
  • order decomposed
  • fulfillment fallout assigned
  • cancellation requested
  • amendment accepted
  • order manually corrected
  • reconciliation mismatch resolved

Business audit explains lifecycle decisions.

Security Audit

Security audit captures security-sensitive actions.

Examples:

  • login success/failure
  • token refresh
  • password reset
  • permission changed
  • role assigned
  • access denied
  • privileged action attempted
  • support impersonation started
  • support impersonation ended
  • API key created
  • secret accessed
  • sensitive export downloaded

Security audit explains access and authority.

Compliance Audit

Compliance audit captures evidence required by regulation, contract, internal policy, or customer commitment.

Examples:

  • customer data export
  • commercial terms changed
  • price override justification recorded
  • contract-related field changed
  • regulated workflow approved
  • retention policy applied
  • deletion/anonymization requested
  • data access reviewed

Compliance audit explains defensibility.

Internal CSG/team policy decides which events are formally required. Do not invent internal compliance requirements. Mark unknown requirements as internal verification items.


5. Audit Logging in CPQ and Order Management

In CPQ/order systems, not every technical event is audit-worthy. Audit-worthy events are usually actions that affect business state, security, commercial terms, customer commitments, or operational accountability.

Examples:

Domain areaAudit-worthy action
Quote lifecyclecreate, update, price, submit, approve, reject, expire, accept
Pricingoverride price, change discount, apply promotion, recalculate price
Approvalrequest approval, approve, reject, delegate, escalate, override
Order lifecyclecreate, validate, submit, amend, cancel, complete
Fulfillmentdecompose order, send fulfillment request, mark fallout, resolve fallout
Reconciliationdetect mismatch, acknowledge mismatch, resolve mismatch
Account/customerchange customer reference, contact, billing/account mapping
Configurationchange rules, catalog version, eligibility policy, pricing rule
Support operationsimpersonate user, force transition, replay message, manually retry

Audit logs should preserve business intent, not only technical method names.

Bad:

{
  "message": "updateQuote called",
  "quoteId": "Q-10001"
}

Better:

{
  "audit_event_id": "aud-9f5b8d",
  "event_type": "business_audit",
  "action": "quote.discount_override.applied",
  "outcome": "success",
  "actor": {
    "type": "user",
    "id": "u-12345"
  },
  "target": {
    "type": "quote",
    "id": "Q-10001"
  },
  "business_key": {
    "quote_id": "Q-10001",
    "account_id": "A-7788"
  },
  "change": {
    "field": "discount_percent",
    "before": 10,
    "after": 15
  },
  "reason_code": "MANAGER_APPROVAL",
  "request": {
    "id": "req-7a31"
  },
  "trace": {
    "id": "4bf92f3577b34da6a3ce929d0e0e4736"
  },
  "timestamp": "2026-07-11T13:45:21.442Z"
}

The better event says what happened, to what, by whom, under what reason, and how to correlate it.


6. Audit Action Naming

Audit action names should be stable and business-readable.

Recommended pattern:

<entity>.<business_action>.<result_or_variant>

Examples:

quote.created
quote.priced
quote.discount_override.applied
quote.approval.requested
quote.approval.approved
quote.approval.rejected
order.created
order.submitted
order.cancelled
order.amendment.requested
order.fallout.created
order.fallout.resolved
security.role.assigned
security.access.denied
support.impersonation.started
support.impersonation.ended

Avoid action names tied to implementation details:

QuoteService.update
OrderController.post
saveQuote
executeCommand
handleMessage

Those names may be useful in application logs, but audit events should remain stable even if code is refactored.


7. Actor Model

The actor is the accountable initiator.

Common actor types:

Actor typeMeaning
userHuman end user
serviceAnother service acting through integration
systemAutomated internal system behavior
batchScheduled/background job
supportSupport/admin user
delegated_userUser acting on behalf of another user
impersonated_userSupport/admin impersonating a user, if allowed

Do not assume user_id is always available. A Kafka consumer, scheduler, reconciliation job, or Camunda worker may not have a human actor.

In those cases, model the actor explicitly:

{
  "actor": {
    "type": "batch",
    "id": "order-reconciliation-job",
    "run_id": "jobrun-20260711-0200"
  }
}

For delegated or impersonated actions, record both parties if policy allows:

{
  "actor": {
    "type": "support",
    "id": "support-42"
  },
  "on_behalf_of": {
    "type": "user",
    "id": "u-12345"
  },
  "delegation_reason": "CUSTOMER_SUPPORT_CASE"
}

Impersonation/delegation is high-risk. Verify internal policy before logging display names, emails, roles, or support case IDs.


8. Target Entity Model

The target is the thing being acted on.

Examples:

{
  "target": {
    "type": "order",
    "id": "O-88001"
  }
}

For nested targets:

{
  "target": {
    "type": "quote_line_item",
    "id": "QLI-44",
    "parent": {
      "type": "quote",
      "id": "Q-10001"
    }
  }
}

For configuration changes:

{
  "target": {
    "type": "pricing_rule",
    "id": "PRICING-RULE-ENTERPRISE-DISCOUNT"
  }
}

Be careful with IDs. Some IDs are safe business identifiers. Some IDs may reveal customer information or commercial structure. Internal policy decides what can be stored long-term.


9. Before and After Values

Before/after values are powerful evidence. They are also high risk.

Use them when they are necessary to explain accountability. Do not dump entire objects.

Bad:

{
  "before": { "entireQuoteObject": "...huge payload..." },
  "after": { "entireQuoteObject": "...huge payload..." }
}

Better:

{
  "change": {
    "field": "quote.status",
    "before": "DRAFT",
    "after": "SUBMITTED"
  }
}

For multiple changes:

{
  "changes": [
    { "field": "quote.status", "before": "DRAFT", "after": "SUBMITTED" },
    { "field": "approval.required", "before": false, "after": true }
  ]
}

Use redaction or classification for sensitive fields:

{
  "changes": [
    {
      "field": "customer.contact_email",
      "before": "[REDACTED]",
      "after": "[REDACTED]",
      "classification": "personal_data"
    }
  ]
}

Never assume before/after is safe merely because it is useful.


10. Outcome and Failure Audit

Audit logs should not only record successful actions. Rejected or failed attempts may be important evidence.

Examples:

  • approval rejected
  • access denied
  • invalid transition attempted
  • price override rejected
  • cancellation rejected due to fulfillment status
  • user attempted privileged action without permission
  • support impersonation denied
  • message replay rejected because idempotency key already consumed

Represent outcome explicitly:

{
  "action": "order.cancel.requested",
  "outcome": "rejected",
  "reason_code": "ORDER_ALREADY_IN_FULFILLMENT",
  "target": {
    "type": "order",
    "id": "O-88001"
  }
}

Do not hide rejected actions inside generic error logs. A rejected action can be a valid business outcome and still be audit-worthy.


11. Audit Logging Lifecycle

A typical audit pipeline:

flowchart TD A[User / Service / Job Action] --> B[Application Authorization and Validation] B --> C[Business State Change] C --> D[Transaction Commit] D --> E[Audit Event Created] E --> F[Audit Store / Audit Topic / Audit Log Backend] F --> G[Query, Investigation, Compliance, Support]

The hard question is when to write the audit event.

Options:

StrategyBenefitRisk
Write before business commitCaptures attempt earlyMay audit action that never committed
Write after business commitMatches durable stateAudit write may fail after business success
Write inside same DB transactionStrong consistency with stateCouples audit storage to application DB
Transactional outboxDurable and decoupledMore moving parts
Emit to message broker onlyScalable and decoupledRisk of lost evidence unless delivery is guaranteed and monitored

For business-critical audit, the audit event and business state must have a clear consistency model.

If business state commits but audit event is lost, accountability is broken. If audit says success but business state rolled back, evidence is misleading.


12. Audit Consistency Pattern: Transactional Outbox

A common production pattern is writing audit events into an outbox table in the same transaction as the business change. A separate publisher then exports the event to a log/audit/event platform.

flowchart LR A[JAX-RS Request] --> B[Service Layer] B --> C[Business Tables] B --> D[Audit Outbox Table] C --> E[Commit] D --> E[Commit] E --> F[Outbox Publisher] F --> G[Audit Topic / Audit Backend]

Benefits:

  • audit event is committed with business state
  • publisher can retry safely
  • failed exports are observable
  • audit loss risk is reduced

Failure modes:

  • outbox publisher down
  • outbox backlog growing
  • duplicate publish
  • poison audit event
  • audit backend unavailable
  • missing idempotency in audit consumer
  • retention mismatch between DB outbox and audit backend

Metrics to consider:

  • audit outbox pending count
  • audit outbox oldest event age
  • audit publish success/failure count
  • audit publish retry count
  • audit publish latency
  • audit consumer duplicate count

13. Java/JAX-RS Placement

Do not put all audit logic in a generic request filter.

A JAX-RS filter can capture request metadata:

  • request ID
  • correlation ID
  • trace ID
  • actor context
  • source IP
  • user agent
  • endpoint

But it cannot reliably know business meaning:

  • quote approved
  • discount overridden
  • order cancellation rejected
  • workflow task completed
  • fulfillment fallout resolved

Audit should usually be emitted from the service/application layer where business intent is known.

Recommended layering:

LayerAudit role
JAX-RS filterExtract request/security/correlation context
Authentication/authorizationProvide actor and authority context
Resource methodAvoid detailed audit unless action is simple
Application serviceBest place for business audit event construction
Domain service/state machineBest place for transition audit semantics
Repository/DB layerUsually not enough business context
Async consumerEmits audit for message-driven actions
Scheduler/jobEmits audit for automated actions

14. Example Java Audit Event Model

public record AuditEvent(
    String auditEventId,
    Instant timestamp,
    String eventType,
    String action,
    String outcome,
    Actor actor,
    Target target,
    Map<String, Object> businessKey,
    List<Change> changes,
    String reasonCode,
    AuditContext context
) {}

public record Actor(
    String type,
    String id
) {}

public record Target(
    String type,
    String id
) {}

public record Change(
    String field,
    Object before,
    Object after,
    String classification
) {}

public record AuditContext(
    String requestId,
    String correlationId,
    String traceId,
    String spanId,
    String tenantId,
    String sourceIp,
    String userAgent
) {}

This is not a required implementation. It is a shape to reason about. Internal standards may require different field names.


15. Example Service-Layer Audit Emission

public Quote approveQuote(ApproveQuoteCommand command, RequestContext ctx) {
    Quote quote = quoteRepository.findForUpdate(command.quoteId())
        .orElseThrow(() -> new QuoteNotFoundException(command.quoteId()));

    QuoteStatus beforeStatus = quote.status();

    quote.approve(command.approvalReason(), ctx.actorId());
    quoteRepository.save(quote);

    auditService.record(AuditEvent.builder()
        .eventType("business_audit")
        .action("quote.approval.approved")
        .outcome("success")
        .actor(Actor.user(ctx.actorId()))
        .target(Target.of("quote", quote.id()))
        .businessKey(Map.of("quote_id", quote.id()))
        .change("quote.status", beforeStatus, quote.status(), "business_state")
        .reasonCode(command.approvalReason())
        .context(ctx.auditContext())
        .build());

    return quote;
}

Important concerns:

  • emit only after the domain operation has succeeded
  • ensure event commit semantics are clear
  • avoid raw command payload logging
  • sanitize reason fields if user-entered
  • include actor and business key
  • include correlation/trace context

16. Audit Logging for State Transitions

For lifecycle-heavy systems, every important transition should be auditable.

Example:

{
  "event_type": "business_audit",
  "action": "order.state.transitioned",
  "outcome": "success",
  "target": {
    "type": "order",
    "id": "O-88001"
  },
  "transition": {
    "from": "VALIDATED",
    "to": "SUBMITTED",
    "trigger": "USER_SUBMIT",
    "guard": "ORDER_VALIDATION_PASSED"
  },
  "actor": {
    "type": "user",
    "id": "u-12345"
  },
  "correlation_id": "corr-123"
}

Invalid transition attempts may also be audit-worthy:

{
  "event_type": "business_audit",
  "action": "order.state.transition_attempted",
  "outcome": "rejected",
  "target": {
    "type": "order",
    "id": "O-88001"
  },
  "transition": {
    "from": "COMPLETED",
    "to": "CANCELLED",
    "trigger": "USER_CANCEL"
  },
  "reason_code": "TERMINAL_STATE_CANNOT_CANCEL"
}

This matters in regulated or contract-sensitive workflows.


17. Audit Logging for Async Consumers

Async consumers perform actions too.

A Kafka/RabbitMQ consumer may:

  • receive order submitted event
  • validate downstream state
  • create fulfillment request
  • update order state
  • mark fallout
  • retry failed action
  • publish downstream event

Audit context for async flow should include:

  • message ID
  • event ID
  • causation ID
  • correlation ID
  • trace ID
  • producer service
  • topic/queue/exchange
  • consumer group / consumer service
  • business key
  • processing outcome

Example:

{
  "event_type": "business_audit",
  "action": "order.fulfillment.requested",
  "outcome": "success",
  "actor": {
    "type": "service",
    "id": "order-orchestration-service"
  },
  "target": {
    "type": "order",
    "id": "O-88001"
  },
  "message": {
    "id": "msg-445",
    "event_id": "evt-991",
    "causation_id": "evt-990"
  },
  "correlation_id": "corr-123"
}

Do not require a human actor for automated business actions. Represent automation honestly.


18. Audit Logging for Camunda / Workflow

Workflow engines need auditability because they often model business process state.

Audit-worthy workflow actions:

  • process instance started
  • process instance cancelled
  • user task claimed
  • user task completed
  • user task delegated
  • timer fired
  • message correlated
  • escalation triggered
  • incident created
  • incident resolved
  • manual retry executed
  • process variable changed, if sensitive to business meaning

Example:

{
  "event_type": "business_audit",
  "action": "approval.task.completed",
  "outcome": "success",
  "actor": {
    "type": "user",
    "id": "u-approver-1"
  },
  "target": {
    "type": "approval_task",
    "id": "task-77"
  },
  "workflow": {
    "process_instance_id": "proc-123",
    "process_definition_key": "quoteApproval",
    "activity_id": "managerApproval"
  },
  "business_key": {
    "quote_id": "Q-10001"
  }
}

Verify whether the workflow engine already emits history/audit data. Do not duplicate blindly. But also do not assume engine history equals business audit.


19. Audit Immutability and Tamper Evidence

Audit logs lose value if they can be silently modified or deleted.

Possible controls:

  • append-only storage
  • restricted write access
  • restricted read access
  • write-once archive
  • signed events
  • hash chaining
  • separate audit database/schema
  • separate audit topic
  • retention lock
  • immutable object storage
  • access logging for audit reads
  • alerting on audit pipeline failure

Example integrity fields:

{
  "audit_event_id": "aud-9f5b8d",
  "previous_hash": "...",
  "integrity_hash": "...",
  "signature_key_id": "audit-signing-key-v3"
}

Not every system needs cryptographic audit chains. But every production system should know its audit integrity requirement.

Internal verification is mandatory here. Do not invent compliance controls.


20. Audit Storage and Query Design

Audit storage must support investigation.

Common query dimensions:

  • timestamp range
  • tenant/account
  • actor ID
  • actor type
  • target type
  • target ID
  • business key
  • action
  • outcome
  • reason code
  • request ID
  • correlation ID
  • trace ID
  • process instance ID
  • source IP

Query examples:

show all changes to quote Q-10001
show all actions by user u-12345 yesterday
show all rejected discount overrides this week
show all support impersonation actions for account A-7788
show all order cancellations after fulfillment started
show all audit events correlated with incident INC-2026-0711

Design audit schema around investigation questions, not only write convenience.


21. Metrics for Audit Pipeline Health

Audit events themselves are data. The audit pipeline also needs observability.

Useful metrics:

  • audit events emitted count
  • audit events emitted by action/outcome
  • audit event write failure count
  • audit outbox pending count
  • audit outbox oldest age
  • audit publish latency
  • audit publish retry count
  • audit backend rejection count
  • audit consumer duplicate count
  • audit schema validation failure count
  • audit storage write latency
  • audit read/query latency

Avoid high-cardinality labels:

Bad metric label:

audit_events_total{actor_id="u-12345", target_id="Q-10001"}

Better:

audit_events_total{action="quote.approval.approved", outcome="success", actor_type="user"}

Use logs/audit store for actor and target IDs. Use metrics for bounded aggregation.


22. Tracing and Audit Logs

Audit logs should correlate with traces, but audit logs should not depend on traces for existence.

Trace sampling may drop a trace. Audit evidence must still exist.

Good practice:

  • include trace.id and span.id in audit event when available
  • include request.id and correlation.id
  • do not store full trace payload in audit event
  • do not put sensitive audit details into span attributes without review
  • keep audit retention independent from trace retention

Trace is diagnostic evidence. Audit is accountability evidence. They should link, not replace each other.


23. Audit Logging Failure Modes

Common failure modes:

Failure modeConsequence
Audit event missingNo accountability evidence
Audit event duplicatedInvestigation confusion
Audit event emitted before rollbackEvidence contradicts durable state
Audit event emitted after business commit but write failedBusiness state changed without audit
Actor missingCannot prove who acted
Target missingCannot prove what changed
Before/after missingCannot explain change
Sensitive data includedPrivacy/security incident
Action name unstableQueries break over time
Audit stored only in short-retention log backendEvidence disappears too soon
Async audit publisher backlog invisibleSilent evidence delay/loss
Audit read access too broadSensitive evidence exposure
Audit read access too narrowSupport/compliance cannot investigate

Audit logging itself needs production readiness.


24. Security and Privacy Concerns

Audit logs often contain sensitive metadata.

Risks:

  • actor identity leakage
  • customer/account data leakage
  • commercial terms leakage
  • reason text containing PII
  • before/after values containing personal data
  • source IP considered personal data in some contexts
  • support impersonation records exposing sensitive cases
  • long retention increasing breach impact
  • broad audit-reader access

Control ideas:

  • use stable IDs instead of display names where possible
  • redact or classify sensitive fields
  • store sensitive diffs separately if required
  • restrict audit access
  • log audit reads
  • define retention per event type
  • avoid free-text reason fields where controlled reason codes are enough
  • sanitize user-provided reason/comment fields

Audit evidence must be useful, but not careless.


25. Performance and Cost Concerns

Audit logging can add latency and storage cost.

Performance concerns:

  • synchronous audit writes on critical path
  • large before/after diffs
  • serializing huge objects
  • remote audit backend calls during request handling
  • audit DB contention
  • outbox table growth
  • slow audit queries on hot database

Cost concerns:

  • long retention
  • high-volume low-value events
  • repeated full-object snapshots
  • duplicate audit in app logs + audit store + events
  • indexing every field
  • storing sensitive data that requires expensive controls

Design principles:

  • audit only meaningful actions
  • keep event schema compact
  • store diffs, not full payloads
  • use bounded vocabularies
  • prefer durable async publishing via outbox for heavy pipelines
  • monitor backlog and failures
  • define retention by event class

26. Internal Verification Checklist

Use this checklist in the codebase and team context.

Audit Requirements

  • Which actions are formally audit-required?
  • Which events are business audit vs security audit vs compliance audit?
  • Are requirements documented by product, security, compliance, or customer contracts?
  • Are quote/order/approval/fulfillment/fallout actions covered?
  • Are rejected/failed attempts covered where needed?

Audit Schema

  • Is there a standard audit event schema?
  • Are action names stable and documented?
  • Are actor, target, outcome, reason, timestamp, and correlation fields mandatory?
  • Are before/after values allowed?
  • Are sensitive fields classified?
  • Are free-text fields sanitized?

Java/JAX-RS Implementation

  • Where is actor context created?
  • Where is audit context propagated?
  • Is audit emitted at resource, service, domain, repository, consumer, or job layer?
  • Are transaction boundaries clear?
  • Does audit emission happen before or after commit?
  • Is there an outbox pattern?
  • Are audit failures visible?

Database / Messaging / Workflow

  • Is audit stored in DB, log backend, event topic, or dedicated audit system?
  • If using outbox, is backlog monitored?
  • Are duplicate audit events deduplicated?
  • Are Kafka/RabbitMQ consumer actions audited?
  • Are Camunda workflow actions audited?
  • Are process instance IDs and business keys included?

Security / Privacy / Retention

  • Who can read audit logs?
  • Are audit reads logged?
  • What is the retention policy?
  • Are audit events immutable or tamper-evident?
  • Are PII/commercial data rules documented?
  • Are source IP, user agent, actor names, and before/after values allowed?
  • Are support impersonation/delegation events covered?

Operations

  • Is audit pipeline health monitored?
  • Are audit write failures alerted?
  • Are audit outbox backlog and oldest age visible?
  • Are audit query dashboards/tools documented?
  • Are audit gaps included in RCA actions?

27. PR Review Checklist

When reviewing a PR that changes business state, security permissions, workflow state, or support/admin actions, ask:

  • Does this change introduce an audit-worthy action?
  • Is the action name stable and business-readable?
  • Is actor captured correctly?
  • Is target entity captured correctly?
  • Is outcome captured explicitly?
  • Are rejected attempts audit-worthy?
  • Are before/after values necessary and safe?
  • Is request/correlation/trace context included?
  • Does the audit event commit consistently with business state?
  • Can async actions be traced back to original causation?
  • Are user-provided reason/comment fields sanitized?
  • Is sensitive data excluded or redacted?
  • Are metric labels bounded?
  • Is audit pipeline failure observable?
  • Is retention/access policy respected?
  • Does this require an ADR or internal security/compliance review?

28. Practical Design Rules

Use these rules as baseline:

  1. Treat audit logs as evidence, not debug text.
  2. Emit audit from the layer that knows business intent.
  3. Use stable business action names.
  4. Always capture actor, action, target, outcome, timestamp, and correlation.
  5. Capture before/after only when necessary and safe.
  6. Model system, service, batch, delegated, and impersonated actors explicitly.
  7. Audit important rejected attempts, not only successes.
  8. Do not rely on trace retention for audit evidence.
  9. Do not put full payloads into audit logs.
  10. Use transactionally safe patterns for critical audit events.
  11. Monitor the audit pipeline itself.
  12. Restrict audit read/write access.
  13. Verify retention, immutability, and privacy requirements internally.
  14. Review audit changes like production architecture changes.

29. Summary

Audit logging is the discipline of preserving accountable business, security, and compliance evidence.

You should now be able to:

  • distinguish audit logs from application logs
  • identify business, security, and compliance audit events
  • design actor/action/target/outcome audit schema
  • model before/after changes safely
  • reason about audit consistency and transaction boundaries
  • use outbox-style patterns for durable audit publishing
  • audit HTTP, async, job, and workflow actions
  • connect audit events with request/correlation/trace context
  • identify audit logging failure modes
  • review audit logging for privacy, retention, immutability, cost, and operational readiness

The next part focuses on security and privacy in logging: how to prevent observability from becoming a source of data leakage.

Lesson Recap

You just completed lesson 13 in build core. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.