AuditTrail (audit_trail v0.1.1)
Copy MarkdownReusable 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://..."} # shared2. 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 list4. 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
end5. Mark schemas to track
defmodule MyApp.Items.Item do
use Ecto.Schema
use AuditTrail.Schema, track: [:status, :price, :approved_by]
...
end6. Add the plug to your Phoenix pipeline
pipeline :api do
plug :accepts, ["json"]
plug AuditTrail.Plug
end7. 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
Cursor-paginated get_logs/1. Returns %{logs: [...], next_cursor: cursor | nil}.
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.
Functions
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).
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)