AuditTrail.ControllerAudit (audit_trail v0.1.1)

Copy Markdown

Plug for declarative controller-level audit logging.

Declare at the top of a controller which actions to audit and under what event name. The plug hooks into register_before_send so it captures the final response status without you touching each function body.

Usage

plug AuditTrail.ControllerAudit,
  write: [
    api_login:          "auth:login",
    api_logout:         "auth:logout",
    api_register:       "user:registered",
    api_reset_password: "auth:password_reset"
  ],
  read: [
    api_verify: "auth:session"
  ]

Behaviour

  • write actions are logged regardless of response status (success or error both matter for the audit trail).
  • read actions are only logged on 2xx responses — failed reads are noise.
  • Sensitive params (password, code, token, etc.) are stripped automatically before the payload is stored.
  • Oversized values (file uploads, base64 blobs, huge lists) are truncated automatically before the payload is stored — see @max_value_bytes/ @max_list_items.
  • The actor is read from the process dictionary at before_send time, so login flows that call AuditTrail.set_actor/1 mid-action are captured correctly.

Resource and operation tagging (optional)

By default the logged payload only carries action and event_type. To also tag what resource was touched and which CRUD operation happened, pass an optional resource: and split actions across create:, read:, update:, delete: instead of (or alongside) write:/read::

plug AuditTrail.ControllerAudit,
  resource: "payment",
  create: [api_charge:  "payment:charged"],
  update: [api_refund:  "payment:refunded"],
  delete: [api_void:    "payment:voided"],
  read:   [api_show:    "payment:viewed"]

create, update, and delete behave exactly like write (logged on success and failure) but additionally set operation to "create", "update", or "delete" in the payload, so you can filter logs by CRUD operation. write still works unchanged and tags operation: "write". None of this is required — omit resource: and keep using write/read exactly as before.

Capturing the specific record (optional)

To filter the audit trail down to one record (e.g. "every log entry for this one payment"), the payload also carries resource_id. It's picked up automatically from conn.params["id"] or conn.params["uuid"] — no setup needed if your route already uses either key. If your param is named something else, point at it explicitly:

plug AuditTrail.ControllerAudit,
  resource:  "idf",
  id_param:  "uuid",   # or :uuid — matches conn.params["uuid"]
  update:    [api_update_idf: "idf:updated"]

resource_id is nil if neither the default keys nor id_param: match anything in conn.params — existing controllers need no changes.