Keyboard shortcuts

Press โ† or โ†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Telemetry And Diagnostics

๐Ÿ“ˆ Observability lens: the gateway emits three signals โ€” logs, request traces, and HTTP metrics. This page explains how each is produced, how to verify them locally, and where to look when a request misbehaves.

Signal Overview

SignalProduced byDestination
Logstracing events from layers, validators, and MCP handlers.Console and a rotating file in the working directory.
Request tracestower_http::TraceLayer spans around every HTTP request.OTLP trace export when --enable-open-telemetry is set.
HTTP metricsaxum-otel-metrics (HttpMetricsLayer) request instruments.OTLP metrics export when --enable-otel-metrics is set.

All export settings are process config; see the telemetry section of the Configuration Reference.

Logging

Console and file logging are always on. The file appender writes contextforge-gateway-rs.log (configurable with --log-name) in the current working directory and rotates hourly by default (--log-rotation).

Three environment filters control verbosity independently:

Env varDefaultControls
RUST_LOGdebugConsole events.
RUST_FILE_LOGdebugFile events.
RUST_TRACE_LOGinfoWhich spans reach the OTLP trace exporter.

Dataplane log messages use a stable method_name - event text field = value shape, so boundary-specific prefixes are grep-friendly: claims_layer, user_config_store_layer, virtual_host_config_layer, AuthorizedCallValidator::validate, initialize:, call_tool, and so on.

Traces

TraceLayer emits its request spans at DEBUG level.

โš ๏ธ RUST_TRACE_LOG=debug is required for trace export. The default span filter (info) drops the HTTP spans before they reach the OTLP exporter, so nothing arrives at the trace backend.

Enable export with --enable-open-telemetry true plus the OTLP protocol, endpoint, and header flags. Each handled HTTP request produces one span with method, route, status, and latency.

Metrics

--enable-otel-metrics true turns on the HTTP server instruments: request duration histogram, active request gauge, and request/response body size histograms, all labeled with method, status code, and service.name.

Metrics are pushed by a PeriodicReader every 30 seconds, so allow about 35 seconds after the first request before the first data point appears downstream.

Local Verification Stack

A complete local pipeline ships under docker/ as overlays on top of the local run stack:

ComponentRoleEndpoint
LangfuseTrace backend and span viewer.UI at http://localhost:3100, login admin@example.com / changeme, project ContextForge Gateway (Rust).
OTel CollectorReceives OTLP from the gateway, fans traces and metrics out.OTLP/HTTP on :4318, Prometheus exposition on :8889, raw dumps via docker logs otel-collector.
PrometheusScrapes the collector for browsable PromQL.UI at http://localhost:9090.

1. Start the stack

docker compose \
  -f docker/docker-compose-local.yaml \
  -f docker/docker-compose-langfuse.yaml \
  -f docker/docker-compose-otel-collector.yaml \
  up -d

Re-run with ps instead of up -d and wait until every container is healthy.

2. Run the gateway with export enabled

RUST_TRACE_LOG=debug \
cargo run --release --bin contextforge-gateway-rs -- \
  --address 0.0.0.0:8001 \
  --redis-port 6379 --redis-address 127.0.0.1 --redis-mode=plain-text \
  --token-verification-public-key assets/jwt.key.pub \
  --number-of-cpus 4 \
  --upstream-connection-mode=plain-text-or-tls \
  --enable-open-telemetry true \
  --enable-otel-metrics true \
  --otlp-protocol http-protobuf \
  --otlp-endpoint  http://127.0.0.1:3100/api/public/otel/v1/traces \
  --otlp-headers   "Authorization=Basic cGstbGYtY29udGV4dGZvcmdlOnNrLWxmLWNvbnRleHRmb3JnZQ==" \
  --otlp-metrics-endpoint http://127.0.0.1:4318/v1/metrics \
  --otlp-service-name contextforge-gateway-rs

The Authorization header is Basic auth for the seeded Langfuse project keys (pk-lf-contextforge:sk-lf-contextforge, base64-encoded).

3. Generate traffic

for i in {1..10}; do
  curl -s -o /dev/null -w "%{http_code}\n" \
    http://127.0.0.1:8001/contextforge-rs/admin/tokens/admin@example.com
done

Any response counts: each request is traced and recorded as a metric sample.

4. Inspect the data

  • Langfuse: open http://localhost:3100 and check the project โ€” one span per request with method, route, status, and latency.
  • Prometheus: open http://localhost:9090; Status โ†’ Targets should show otel-collector:8889 as UP, then try the starter queries below.
  • Collector stdout: docker logs otel-collector --tail 200 shows raw OTLP trace and metric dumps.

The data path is:

gateway :8001
  -> OTLP/HTTP traces  -> Langfuse :3100 (span UI)
  -> OTLP/HTTP metrics -> OTel Collector :4318
                            -> stdout dump (docker logs)
                            -> Prometheus exposition :8889 -> Prometheus :9090

Tear the stack down with the same three -f files and down.

Prometheus Starter Queries

QuestionQuery
Request count by method, status, and servicehttp_server_request_duration_seconds_count
p95 latencyhistogram_quantile(0.95, sum by (le) (rate(http_server_request_duration_seconds_bucket[1m])))
In-flight requestshttp_server_active_requests
Payload throughputhttp_server_request_body_size_bytes_sum / http_server_response_body_size_bytes_sum

Debugging Checklist

Work down the boundaries in request order; each failure has an owning signal. Failure Modes lists the exact responses per boundary.

SymptomWhere to look
401 responsesclaims_layer log lines: missing/invalid bearer token, unsupported algorithm, or no configured decoder key.
400 config responsesuser_config_store_layer log lines and Redis content for the JWT subject.
404 Server not foundvirtual_host_config_layer debug line showing the requested virtual host id and how many the callerโ€™s config has.
MCP routing errorsAuthorizedCallValidator::validate debug lines, then call_tool/read_resource/get_prompt warns for the split and backend resolution.
Backend failuresinitialize: warns for backends that failed to connect; routed-call warns name the failing backend.
Plugin problemsCPEX pipeline error logs; an invalid reload marks the plugin runtime failed until a valid config is applied.

Known Gaps

Tracked upstream, not yet implemented here: