OpenTelemetry tracing
AIOpen as plain markdown for AIeRPC instruments every layer of the request pipeline (HTTP server → network → upstream → cache → failsafe → client) with OpenTelemetry spans. Export over OTLP (gRPC or HTTP) to Jaeger, Tempo, Honeycomb, Datadog, or any OTel-compatible backend.
You can configure:
- Where to send — OTLP endpoint, gRPC or HTTP transport, optional TLS, optional auth headers
- How much to send —
sampleRate(probabilistic),forceTraceMatchers(always-trace specific network/method combos),detailed(include high-cardinality attributes) - How to label —
serviceNameand arbitraryresourceAttributes(env-var expanded)
Minimum useful config
A working setup with 10% sampling, gRPC export to a local Jaeger.
server: # ...projects: # ...
tracing: enabled: true endpoint: localhost:4317 # OTLP gRPC endpoint protocol: grpc # "grpc" or "http" sampleRate: 0.1 # 10% probabilistic sampling serviceName: erpc-prod # OTel service.name resource attribute detailed: false # set true to include high-cardinality attrsForce-tracing specific methods
When you need deterministic traces for a specific (network, method) pair regardless of the global sampleRate, use forceTraceMatchers. Useful for debugging a customer-impacting issue without raising the global sample rate.
tracing: enabled: true endpoint: localhost:4317 protocol: grpc sampleRate: 0.01 # 1% global sample forceTraceMatchers: - network: "evm:1" # always trace mainnet method: "eth_getLogs" # ...for getLogs specifically - network: "evm:*" method: "debug_*|trace_*" # always trace expensive trace/debug methods on any chainCustom resource attributes
Attach instance-level metadata that flows into every span. Useful for filtering by region, machine, or pod in your tracing backend.
tracing:
enabled: true
endpoint: localhost:4317
resourceAttributes:
fly.region: ${FLY_REGION}
fly.machine_id: ${FLY_MACHINE_ID}
# Or for Kubernetes:
# k8s.pod_name: ${HOSTNAME}
# k8s.node_name: ${NODE_NAME}Values support ${VAR} env-var expansion. Attributes with empty values (after expansion) are automatically omitted.
Using with Jaeger
The repo's docker-compose.yml (opens in a new tab) includes a Jaeger service for development.
docker-compose up jaegertracing:
enabled: true
endpoint: localhost:4317
protocol: grpc
sampleRate: 1.0 # sample everything during development
detailed: trueThen open the Jaeger UI at http://localhost:16686 (opens in a new tab).
Copy for your AI assistant — full tracing referenceExpand for every option, default, and edge case — or copy this entire section into your AI assistant.
TracingConfig — every field
| Field | Type | Default | Notes |
|---|---|---|---|
enabled | bool | false | Master switch. When false, no spans are exported (instrumentation still runs as no-ops). |
endpoint | string | — | OTLP exporter target. For gRPC: host:port (e.g. localhost:4317). For HTTP: full URL (e.g. http://localhost:4318/v1/traces). |
protocol | "grpc"|"http" | grpc | OTLP transport. grpc uses standard OTLP/gRPC; http uses OTLP/HTTP+protobuf. |
sampleRate | float 0..1 | 0 | Probabilistic head sampling — fraction of root spans recorded. 0 = no traces; 1.0 = every request. |
detailed | bool | false | When true, includes high-cardinality attributes on spans: request JSON-RPC IDs, request params, actual cache keys, internal mutex/lock spans. Significantly increases trace volume; use sparingly. |
serviceName | string | hostname-derived | Overrides the OTel service.name resource attribute that tracing backends use as the service grouping. |
headers | map[string]string | none | Extra headers attached to OTLP exporter requests. Use for authenticated collectors: e.g. Authorization: Bearer ... for Honeycomb / DataDog / Tempo Cloud. |
tls | TLSConfig | none | TLS for OTLP/gRPC connections. See "TLS sub-fields" below. |
resourceAttributes | map[string]string | none | Extra OTel resource attributes attached to every span. Values support ${VAR} env-var expansion; empty values are dropped. |
forceTraceMatchers[] | ForceTraceMatcher[] | none | Force-trace rules that bypass sampleRate. See "ForceTraceMatcher" below. |
Key spans you'll see:
- HTTP server request handling
- Network-level (chain) forwarding — the
Network.Forwardspan carries the full per-request execution trace (execution.attempts,upstreams.tried,upstreams.outcomes,upstreams.reasons,upstreams.durations_ms). See failsafe → Per-attempt observability. - Upstream-level request forwarding
- Cache operations (get/set)
- Failsafe executor operations (hedges, retries, timeouts, breaker probes)
- HTTP client requests to upstreams
- Rate limiters
TLS sub-fields (when protocol: grpc with TLS)
The tls block uses the shared TLSConfig struct. See TLS configuration for the full field reference (enabled, certFile, keyFile, caFile, insecureSkipVerify).
Example for a gRPC OTLP collector with mutual-TLS:
tracing:
endpoint: collector.example.com:4317
protocol: grpc
tls:
enabled: true
certFile: /path/to/client.crt # optional client cert
keyFile: /path/to/client.key # optional client key
caFile: /path/to/ca.crt # CA chain for verifying the collector
insecureSkipVerify: false # set true for development; never in prodForceTraceMatcher
forceTraceMatchers:
- network: <matcher-pattern>
method: <matcher-pattern>| Field | Notes |
|---|---|
network | Matcher pattern (matcher syntax: *, |, !). e.g. evm:1, evm:1|evm:10, evm:*. When omitted, matches any network. |
method | Same matcher syntax. e.g. eth_getLogs, eth_*, debug_*|trace_*. When omitted, matches any method. |
A request is force-traced if any matcher's network AND method both match. Force-traced requests bypass sampleRate entirely (treated as sampled-in regardless of the random draw).
Common uses:
- Always trace expensive methods:
{ method: "debug_*|trace_*" } - Always trace one chain that's the focus of debugging:
{ network: "evm:42161" } - Trace one method on one chain during a customer escalation:
{ network: "evm:1", method: "eth_call" }
Headers — concrete examples
For authenticated OTLP collectors:
# Honeycomb
tracing:
endpoint: api.honeycomb.io:443
protocol: grpc
headers:
x-honeycomb-team: ${HONEYCOMB_API_KEY}
# Grafana Cloud Tempo (basic auth via Authorization header)
tracing:
endpoint: tempo-eu-west-0.grafana.net:443
protocol: grpc
headers:
Authorization: Basic \${GRAFANA_CLOUD_BASIC_AUTH}
# Self-hosted with mTLS, no headers needed (use tls.* instead)Authorization headers should use env-var interpolation; never commit credentials to YAML/TS.
Traced components
Every span eRPC emits is a child of an HTTP server root span. The instrumented layers:
| Layer | What spans cover |
|---|---|
| HTTP server | Parsing, auth check, project + network selection, response serialization |
| Network forwarding | Selection-policy eval, upstream pick, retry/hedge/consensus orchestration |
| Upstream forwarding | Rate-limit check, payload assembly, response normalization |
| Cache | Get/set per policy, compression/decompression, multi-connector fanout |
| Failsafe | Each attempt within a retry/hedge; circuit-breaker decisions; consensus participants |
| HTTP client | Outbound request, response read |
| Rate limiters | Token acquisition, queue wait |
detailed: true adds finer-grained internal spans (mutexes, internal selection-policy bookkeeping) and high-cardinality attributes.
Common pitfalls
sampleRate: 0.1and 100% of force-traced requests —forceTraceMatchersoperates on top of sampling; force-traced requests are not subject to the rate. So your collector ingestssampleRate * total + forceTraced. Make sure that's affordable.detailed: truein production — request params and cache keys can be huge (thinketh_getLogsfilters), and they hit your ingest cost. Reserve for debugging.headersinterpolation —${VAR}is only resolved at config load time. Rotating tokens requires a config reload.tls.insecureSkipVerify: true— accepts any cert. Never set in production; use a proper CA chain or mTLS.protocol: httpwithouthttps://inendpoint— exports go to plaintext HTTP. Most managed providers require HTTPS; set the endpoint to thehttps://.../v1/tracesform.- OTLP/HTTP endpoint format —
protocol: httpexpects a FULL URL including the/v1/tracespath segment;protocol: grpcexpects justhost:portwithout scheme.
Append .llms.txt to this URL (or use the AI link above) to fetch the entire expanded reference as plain markdown for an AI assistant.