Access logs record each request and connection Arion handles — method, path, status, timings, bytes — in a format you control. Use them for traffic audit and per-request debugging; for aggregate numbers use metrics instead.
Before you begin
Access logging is a build feature: the binary must be compiled with --features access-log. Log entries are formatted on the worker, then handed to dedicated logger tasks over a channel, so writing never blocks request processing (unless you ask it to — see below).
Enable it
Two pieces of configuration work together. The top-level access_logging section switches the logging service on and tunes it:
access_logging:
num_instances: 1 # parallel logger tasks
queue_length: 1024 # buffered entries per logger
blocking: false # false: drop entries when the queue is full; true: wait
Sinks then attach where the traffic is, using standard Envoy syntax: on a listener, inside http_connection_manager, or inside tcp_proxy. Each attachment point can have its own format and several sinks at once.
# inside the HttpConnectionManager typed_config
access_log:
- name: envoy.access_loggers.file
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog
path: /var/log/arion/access.log
log_format:
text_format: |
[%START_TIME%] %REQ(:METHOD)% %REQ(:PATH)% %RESPONSE_CODE% %DURATION% %UPSTREAM_HOST%The format string
text_format uses the Envoy command-operator grammar: literal text plus %OPERATOR% placeholders, with %REQ(name)% and %RESP(name)% for request and response headers. %START_TIME% is the request start (it honors the top-level timezone setting), %RESPONSE_CODE% the downstream status, %DURATION% the total milliseconds, %UPSTREAM_HOST% the selected endpoint. An operator with no value renders as -. A placeholder Arion does not recognize is a startup (or xDS rejection) error, never silently ignored. When log_format is omitted, Arion writes Envoy's default HTTP access log line. The full table of supported operators is in the access log operator reference.
Variations
Log to stdout or stderr
Use envoy.access_loggers.stdout with type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog (or the stderr/StderrAccessLog pair). Both accept the same log_format. This is the usual choice in containers.
Rotate files
File sinks rotate based on the access_logging section: log_rotation (minutely, hourly or daily) rotates on a schedule, log_max_size (bytes) rotates on size, and max_log_files (default 10) caps how many rotated files are kept. Without either setting, files grow unbounded. Files are created lazily, on the first entry.
Custom operators
You can inject your own fields into log lines without changing Arion. Register the names under access_logging.custom_operators, name a header to inspect at one of the lifecycle hooks (incoming_request_header, upstream_request_header, incoming_response_header, downstream_response_header, the two ext_proc_* hooks, or wasm_header for values set by a WASM filter), then use %user_id% like any other operator:
access_logging:
blocking: false
incoming_request_header: x-arion-metadata
custom_operators: ["user_id", "session_id"]
The header value must be a base64-encoded JSON object; its top-level keys fill the matching operators. Built-in operators can never be overwritten this way, so audit fields stay tamper-proof, and a malformed payload logs a warning and renders - rather than failing the request.
Verify
Start Arion, send a request, and read the sink:
curl -s http://localhost:8080/ >/dev/null
tail -n1 /var/log/arion/access.log
Expected: one line per request in your format, for example [2026-09-28T10:15:04.512Z] GET / 200 3 127.0.0.1:5678.
Limits
- Only
file,stdoutandstderrloggers exist;fluentd,http_grpc,tcp_grpc,opentelemetryandwasmloggers are not supported and are rejected at load. json_formatis not supported — text formats only.- Access log
filterconditions are not supported; every request at the attachment point is logged. - Listener-level entries count whole connections, so byte and duration operators there are per-connection totals, not per-request.