AuditTrail (audit_trail v0.1.1)

Copy Markdown

Reusable structured audit logging, DB change tracking, and crash reporting.

Quick start

1. Add to mix.exs

{:audit_trail, path: "../audit_trail"}   # dev / monorepo
{:audit_trail, git: "https://..."}       # shared

2. Configure

config :audit_trail,
  app_name:         "heart-ke",
  loki_push_url:    System.get_env("LOKI_PUSH_URL"),
  loki_read_url:    System.get_env("LOKI_READ_URL"),
  mailer_adapter:   {AuditTrail.Adapters.Swoosh, []},
  swoosh_mailer:    MyApp.Mailer,
  from_email:       {"MyApp", "alerts@myapp.com"},
  crash_routing: [
    %{match: {:module, ~r/MyApp\.Items/},  to: ["items@myapp.com"]},
    %{match: {:module, ~r/MyApp\.Auth/},   to: ["security@myapp.com"]},
    %{match: :default,                      to: ["admin@myapp.com"]}
  ]

3. Add AuditTrail to your application supervision tree

{AuditTrail, []}    # in application.ex children list

4. Add to your Repo (schema-level tracking)

defmodule MyApp.Repo do
  use Ecto.Repo, otp_app: :my_app, adapter: Ecto.Adapters.Postgres
  use AuditTrail.RepoWatcher
end

5. Mark schemas to track

defmodule MyApp.Items.Item do
  use Ecto.Schema
  use AuditTrail.Schema, track: [:status, :price, :approved_by]
  ...
end

6. Add the plug to your Phoenix pipeline

pipeline :api do
  plug :accepts, ["json"]
  plug AuditTrail.Plug
end

7. Use in controllers

def approve(conn, params) do
  with {:ok, item} = result <- Items.approve(params) do
    AuditTrail.monitor(result, conn, "item:approved", original_record: old_item)
    json(conn, item)
  end
end

Summary

Functions

child_spec(opts)

emit(event_type, data)

See AuditTrail.Logger.emit/2.

get_actor()

See AuditTrail.ActorStore.get/0.

get_logs(filters \\ %{})

get_logs_page(filters \\ %{})

Cursor-paginated get_logs/1. Returns %{logs: [...], next_cursor: cursor | nil}.

{:ok, page1} = AuditTrail.get_logs_page(%{resource: "payment", limit: 50})
{:ok, page2} = AuditTrail.get_logs_page(%{resource: "payment", limit: 50, before: page1.next_cursor})

next_cursor is nil once there are no more pages. Works the same way regardless of storage adapter (Loki, Postgres, TimescaleDB).

get_tenant()

See AuditTrail.TenantStore.get/0.

log_external_api(type, service, data_or_fun)

See AuditTrail.Logger.log_external_api/3.

log_repo(type, result, action, source, ctx)

See AuditTrail.Logger.log_repo/5.

monitor(result, conn, event_type, opts \\ [])

See AuditTrail.Logger.monitor/4.

set_actor(actor)

See AuditTrail.ActorStore.set/1.

set_actor(type, label)

See AuditTrail.ActorStore.set/2.

set_tenant(tenant_id)

Tags every subsequent audit event in the current process with a tenant/org id — for multi-tenant apps that want to scope queries to one tenant without doing it at the application layer. Stored in the process dictionary like set_actor/1; propagated by AuditTrail.Task.

AuditTrail.set_tenant(org.id)