How to log every request

View Source

Problem

You want one structured log entry per completed request with method, path, status, duration, and request id.

Solution

Stack = [
    {livery_request_id, undefined},
    {livery_access_log, #{}}
    %% ... handler
].

livery_access_log emits one entry through logger:log/2 after the handler returns. Fields:

KeyValue
msg"livery_access"
protocolh1 / h2 / h3
methodrequest method, binary
pathrequest path, binary
statusresponse status code
duration_usrequest duration in microseconds
request_idid from livery_request_id, or <<>>

Configure the level

Default level is info. Override in state:

{livery_access_log, #{level => notice}}

If your logger primary level is at notice (OTP default), use notice or higher, or raise the primary level for development:

logger:set_primary_config(level, info).

Read the entries

Standard logger handler. To send entries to a custom destination, add a handler:

logger:add_handler(my_access, my_handler,
    #{level => info,
      filters => [{access_only,
        {fun (#{msg := {report, #{msg := "livery_access"}}}, _) -> log;
             (_, _) -> stop
         end, []}}]}).

Ordering

Place livery_access_log after livery_request_id so the recorded request_id is the one the client also receives. Place it inside an error wrapper so 500s are still logged.

Stack = [
    {livery_request_id, undefined},
    livery_middleware:wrap(fun my_app:errors_to_resp/3),
    {livery_access_log, #{}},
    %% ... business middlewares and handler
].

Correlate access logs with traces

If you also run the livery_instrument_trace middleware, install the instrument logger filter once at boot:

ok = livery_instrument_trace:install_logger().

The filter enriches every logger event emitted while a span is active with the span's trace_id, span_id, and trace_flags in the event metadata. Stack the trace middleware outside livery_access_log and each access-log line carries the same ids as the request's span:

Stack = [
    {livery_request_id, undefined},
    {livery_instrument_trace, #{}},
    {livery_access_log, #{}},
    %% ... handler
].

Any logger call your handler makes during the request inherits the same ids, so application logs and access logs line up with the trace in your backend.

See also

  • Reference: livery_request_id, livery_body_limit, livery_timeout, livery_access_log, livery_instrument_trace
  • Recipe: Propagate request IDs