Skip to main content
Arc provides three observability subsystems: Prometheus metrics, structured NDJSON access logs, and W3C distributed tracing.

Metrics

Configuration

Endpoints

Access control: if no auth_token is configured, only loopback addresses (127.x, ::1) are allowed. If auth_token is set, the Authorization: Bearer <token> header is required from any IP.

Prometheus metric reference

Connection metrics: Request/response metrics: Bandwidth: Phase timing (for each of cli_read, up_conn, up_write, up_read, cli_write): io_uring health:
Non-zero values in arc_ring_sq_dropped_total or arc_ring_cq_overflow_total indicate the io_uring ring is too small. Increase io_uring.uring_entries in your configuration.
Upstream pool: Traffic mirroring: Logging subsystem: Rate limiting: Config and routing: TLS and plugins: XDP:

Design

Workers write AtomicU64::fetch_add with Ordering::Relaxed — one instruction, no locking. WorkerMetrics is #[repr(C, align(64))] so each worker’s struct occupies its own cache line. The admin server reads metric snapshots from a background thread that refreshes every 250ms.

Access logs

Arc writes structured NDJSON access logs. There is no text-format option.

Configuration

Log record fields

Each access log line is a single JSON object: Example:

Redaction

Arc redacts sensitive values before writing logs:
  • Headers — matched case-insensitively; values replaced with [REDACTED]
  • Query parameters — matched case-insensitively; values replaced with [REDACTED]
  • Body fields — JSONPath-style ($.field.subfield); values replaced with "[REDACTED]"
Redaction rules are rebuilt automatically on hot reload.

File rotation

Rotation is triggered when the active log file exceeds max_size. The steps are:
  1. Rename the active file to a timestamped archive name (near-instant metadata operation)
  2. Reopen the active path immediately so writes continue uninterrupted
  3. Compress the archive with gzip in a background thread (if compress: true)
  4. Delete the oldest archives to keep only max_files retained

Design — no backpressure on workers

Workers push log events into a bounded SPSC ring buffer. If the ring is full, the event is dropped and a counter is incremented. Workers never block. The writer thread drains all rings, encodes NDJSON manually (no serde at write time), and flushes via io_uring Write operations in batches.

Distributed tracing

Arc implements W3C Trace Context. Every request gets a trace_id and span_id that appear in access logs and are propagated to upstreams.

How trace context is resolved

traceparent format

Arc parses and emits the standard W3C format:
  • 2-char version (00)
  • 32-char trace ID (must not be all zeros)
  • 16-char span ID (must not be all zeros)
  • 2-char flags (01 = sampled)

Upstream propagation

When Arc forwards a request to an upstream, it injects a traceparent header with the same trace_id but a freshly generated span_id. This creates a parent-child span relationship between the Arc hop and the upstream.

Searching logs by trace ID

OTLP export

To export traces to an OpenTelemetry collector:

Troubleshooting

Check that observability.metrics_bind is set and that Arc is running. The default is 127.0.0.1:9090 — requests from other hosts will be refused unless you bind to 0.0.0.0:9090. Confirm with: curl http://127.0.0.1:9090/metrics.
Verify observability.access_log.enabled: true and that logging.output.file is set to a writable path. Also check that sample is not set to 0.0. Logs are written asynchronously via SPSC ring buffer — if the writer thread is behind, a short delay is normal.
Rotation is triggered when FileState.offset >= RotationConfig.max_size_bytes. If the file never reaches that size, no rotation occurs. Check arc_log_written_total in /metrics to confirm bytes are being written. If compression is enabled and arc_log_compress_dropped_total is incrementing, the compression queue is full — reduce write rate or increase queue capacity via ARC_LOG_COMPRESS_QUEUE_CAPACITY.
Check that observability.tracing.endpoint is reachable from Arc. Set insecure: true if the collector does not have TLS. The trace_id field in access logs can be used to confirm Arc is generating trace context even if OTLP export is failing.
Phase timing metrics are only emitted for requests that go through the full pipeline. Check that Arc is receiving and proxying traffic. If metrics appear after the first request, this is expected behavior.