Skip to content

Access-log format

The access log's format strings: how operators render as text and JSON, every operator, the filter syntax, and the presets. Use this page when writing --access-log-format, --access-log-json or --access-log-filter; Access logs explains how to turn the log on, and Response flags lists the flags and details.

Text and JSON

An operator whose value is unavailable renders -, as does an operator this proxy does not know; %% is a literal %; %OPERATOR(...):N% keeps the first N characters. In text formats, control characters in values are escaped (\n, \t, \u0001), so a header cannot break a line. A trailing newline (Envoy's formats end with one) is dropped. In the JSON form a value that is a single operator keeps the operator's type, as in Envoy: numbers for codes, sizes and durations, true or false for flags, null when unavailable; other values are JSON strings. Nested objects, numbers and booleans in the dictionary are written as they are.

Operators

operator value
%START_TIME%, %START_TIME(fmt)% when the request started, UTC; default 2026-10-08T12:34:56.789Z. fmt takes %Y %y %C %m %d %e %j %H %I %M %S %p %b %B %a %A %u %w %z %Z %s %T %F %D %R, and %f or %1f..%9f for fractions of a second
%REQ(X?Y):N%, %REQUEST_HEADER(...)% the request header X, else Y; pseudo-headers :method, :path (no scheme or authority), :authority, :scheme, :protocol. The request as the client sent it
%RESP(X?Y):N%, %RESPONSE_HEADER(...)% the response header (:status too)
%TRAILER(X?Y):N%, %RESPONSE_TRAILER(...)% the response trailer
%PROTOCOL% HTTP/1.1, HTTP/1.0, HTTP/2 or HTTP/3: the client's
%RESPONSE_CODE% the status the client got; 0 for none (a reset, a refused SOCKS client, a QUIC flow)
%RESPONSE_CODE_DETAILS% why: see Response flags and details
%RESPONSE_FLAGS%, %RESPONSE_FLAGS_LONG% the flags, as UF,URX or UpstreamConnectionFailure,UpstreamRetryLimitExceeded
%DURATION% milliseconds from the first byte of the request to the last of the response; for tunnels and flows, until they closed
%REQUEST_DURATION% until the request was sent on to the server
%RESPONSE_DURATION% until the first byte of the server's response
%RESPONSE_TX_DURATION% from the first byte of the server's response to the last byte to the client
%COMMON_DURATION(A:B:P)% between two points, in ms, us or ns: START_TIME, DS_RX_BEG, DS_RX_END, US_CX_BEG, US_CX_END, US_HS_END, US_TX_END, US_RX_BEG, US_RX_END, DS_TX_END
%DOWNSTREAM_HANDSHAKE_DURATION% milliseconds of the TLS handshake with the client
%BYTES_RECEIVED%, %BYTES_SENT% body bytes from and to the client; for tunnels and flows, the payload each way
%UPSTREAM_WIRE_BYTES_SENT%, %UPSTREAM_WIRE_BYTES_RECEIVED% bytes to and from the server connection, heads and framing included
%UPSTREAM_HOST%, %UPSTREAM_REMOTE_ADDRESS% (_WITHOUT_PORT) the server or chained proxy connected to
%UPSTREAM_CLUSTER% the chained proxy's host:port, else the server's
%UPSTREAM_LOCAL_ADDRESS% (_WITHOUT_PORT) the proxy's end of that connection
%UPSTREAM_TRANSPORT_FAILURE_REASON% why the TLS handshake with the server failed: the reason and the innermost cause's message (certificate_revoked: Certificate has been revoked, ..., hostname_mismatch: No subject alternative ...)
%UPSTREAM_TLS_VERSION%, %UPSTREAM_TLS_CIPHER%, %UPSTREAM_PEER_SUBJECT%, %UPSTREAM_PEER_ISSUER% the TLS session with the server
%DOWNSTREAM_REMOTE_ADDRESS% (_WITHOUT_PORT), %DOWNSTREAM_REMOTE_PORT% the client (the PROXY protocol's source, when one was accepted)
%DOWNSTREAM_DIRECT_REMOTE_ADDRESS% (_WITHOUT_PORT) the peer of the client's TCP connection: the load balancer when a PROXY protocol header was accepted, else the client
%DOWNSTREAM_LOCAL_ADDRESS% (_WITHOUT_PORT), %DOWNSTREAM_LOCAL_PORT% the proxy's end of the client connection
%DOWNSTREAM_TLS_VERSION%, %DOWNSTREAM_TLS_CIPHER%, %DOWNSTREAM_PEER_SUBJECT%, %DOWNSTREAM_PEER_ISSUER%, %DOWNSTREAM_PEER_FINGERPRINT_256% the TLS session with the client (listener or interception) and its certificate
%REQUESTED_SERVER_NAME% the SNI the client sent
%CONNECTION_ID%, %STREAM_ID% the client connection (as in [conn <id>] log lines) and the HTTP/2 or HTTP/3 stream
%UNIQUE_ID% a random UUID per exchange, made only when a format asks for it; with OpenTelemetry, the exchange's X-Request-Id
%TRACE_ID%, %SPAN_ID% the exchange's trace id and the id of the proxy's span for it, with a tracer (OpenTelemetry)
%HOSTNAME% this host's name
%TLS_JA3_FINGERPRINT%, %TLS_JA4_FINGERPRINT% JA3 and JA4 of the client's ClientHello (JA4 starts with q for QUIC); - when the proxy read none
%MP_RESPONSE_SOURCE% SERVER, PROXY, FILTER, CACHE or REPLAY
%MP_FLOW% h1, h2, h3, tunnel, socks, websocket, tcp, udp, quic or quic_relay
%MP_EARLY_DATA%, %MP_TRANSPARENT_QUIC% whether the request came as 0-RTT data; whether the client came to the transparent QUIC listener
%MP_CHUNKED_LENGTH% whether the request had both Transfer-Encoding: chunked and Content-Length, accepted with --allow-chunked-length (its Content-Length dropped)
%MP_SOCKS_VERSION%, %MP_FORWARDED_VIA% the SOCKS version; the client's own proxy the request went through
%MP_USER%, %MP_URL%, %MP_UPSTREAM_STATUS% the authenticated user; the absolute URL; the status the server sent (before a filter changed it)
%MP_UPSTREAM_PROTOCOL% the protocol spoken to %UPSTREAM_CLUSTER%: to a chained proxy HTTP/1.1 or HTTP/2 (for a tunnel, that of its CONNECT), SOCKS4 or SOCKS5; else to the server HTTP/1.1, HTTP/2 or HTTP/3; - for a tunnel straight to the server
%MP_TIMING(phase)% dns, connect, tls, client_tls, ttfb or total, in milliseconds with three decimals
%MP_SCRIPT_CALLS%, %MP_SCRIPT_TIME% the exchange's calls into scripts, and the milliseconds they took together, with three decimals
%MP_SCRIPT_STEPS%, %MP_SCRIPT_ALLOC% the interpreter steps those calls ran, and about how many bytes of values they created (an estimate)

Filters

filter Java compact syntax
status code statusCode(Comparison.GE, 500) status>=500 (also =, !=, <=, >, <)
duration duration(Comparison.GE, 1000) duration>=1000, duration>=1s
not a health check notHealthCheck() (/healthz, or Envoy's User-Agent: Envoy/HC), notHealthCheckPath("/ready"), notHealthCheckHeader("X-Health-Check") not_health_check, not_health_check(/ready), not_health_check(header=x-health-check)
response flags responseFlag() (any), responseFlag(UPSTREAM_OVERFLOW, ...) response_flag, response_flag(UO,UF)
request header header("x-debug") (present), header("user-agent", "curl/.*") (whole value), headerEquals(...) header(x-debug), header(user-agent~curl/.*), header(x-env=prod)
sampling runtime(10) (by X-Request-ID when there is one, else at random) runtime(10), sample(10%)
combinators allOf(...), anyOf(...), a.and(b), a.or(b), a.negate() && / and, || / or, ! / not, parentheses

Presets

LogFormat names nine formats from LittleProxy, now presets of the same formatter, and Envoy's default: CLF, ELF (combined), JSON, SQUID, W3C, LTSV, CSV, HAPROXY, JSON_EXTENDED and ENVOY. new ActivityLogger(LogFormat.CLF) (or --activity-log-format clf, or --access-log-format clf) uses one; each constant's documentation shows its format string. JSON_EXTENDED adds source, upstream_status, ttfb_ms, total_ms, dns_ms, connect_ms, tls_ms, response_flags, response_code_details and flow to the JSON line.

Limitations

Times are milliseconds: %f digits beyond them are zeros. %REQUEST_DURATION% ends when the request was sent on to the server, and DS_RX_END is that point too. %BYTES_RECEIVED% and %BYTES_SENT% count body bytes, not heads. Upstream wire bytes are what passes the proxy's connection to the server: over TLS, the decrypted bytes. %DOWNSTREAM_LOCAL_ADDRESS% is unknown for clients of the HTTP/3 listener. %HOSTNAME% is looked up once. A transparent QUIC flow that is intercepted as HTTP/3 has a line per request, none of its own.