Sandbox adapter that captures logs per test.
Each test gets its own log buffer via an ETS table. Logs from the
test process and any process in its $callers chain are routed to
that buffer — no interleaving from concurrent tests.
Configuration
# Default — fail on unconsumed :error logs
logger: true
# Stricter — fail on unconsumed :warning and above
logger: [fail_on: :warning]
# Capture only, never fail
logger: [fail_on: false]Pop-based assertions
Reading logs consumes them. At checkin, only unconsumed logs above
the fail_on threshold trigger failure.
# Pop the next log at a level (returns message string or nil)
assert pop_log(sandbox, :error) =~ "sync failed"
# Pop all logs at a level (returns joined string)
assert logs(sandbox, :warning) =~ "duplicate"
# Happy path — never read logs, any error fails at checkin
test "creates user", %{sandbox: sandbox} do
User.create!(attrs)
# checkin: no errors → passes
end
Summary
Functions
Get all logs without consuming them (non-destructive).
Returns a list of %{level: atom, message: binary, metadata: map}.
Pop all log entries at or above level. Returns the messages
joined with newlines as a string. Consumes the entries.
Pop the next log entry at or above level. Returns the message
string, or nil if no matching entry exists. Consumes the entry.
Functions
Get all logs without consuming them (non-destructive).
Returns a list of %{level: atom, message: binary, metadata: map}.
Pop all log entries at or above level. Returns the messages
joined with newlines as a string. Consumes the entries.
assert logs(sandbox, :error) =~ "failed"
assert logs(sandbox) =~ "some info message"
Pop the next log entry at or above level. Returns the message
string, or nil if no matching entry exists. Consumes the entry.
assert pop_log(sandbox, :error) =~ "something broke"
refute pop_log(sandbox, :error) # no more errors