Tracing
This guide explains how to add distributed tracing to your A2A agents using the traces gem.
Overview
The agent2agent gem ships trace providers that instrument the request lifecycle. When a tracing backend is active, every request produces spans covering:
| Span Name | Class | Key Attributes |
|---|---|---|
a2a.server.call |
A2A::Server |
http.method, http.path, http.status_code |
a2a.server.triage.call |
A2A::Server::Triage |
a2a.operation, a2a.proto_operation |
a2a.server.dispatcher.call |
A2A::Server::Dispatcher |
a2a.operation, http.status_code, a2a.response_type |
a2a.bindings.json_rpc.call |
A2A::Bindings::JsonRpc |
rpc.method, rpc.jsonrpc.request_id, a2a.response_type |
a2a.bindings.rest.call |
A2A::Bindings::Rest |
a2a.verb, a2a.path, a2a.response_type |
When no backend is configured, tracing is a zero-cost no-op -- the provider blocks are never evaluated.
How It Works
The traces gem uses a provider/backend architecture:
- Providers wrap existing methods with trace spans using
Traces::Provider(SomeClass). They live inlib/traces/provider/a2a/. - Backends connect those spans to an observability system (OpenTelemetry, Datadog, console output, etc.).
- Config (
config/traces.rb) tells thetracesgem which providers to load when a backend is active.
The activation sequence:
TRACES_BACKEND env var set
-> require "traces" (happens automatically via async)
-> backend loaded, Traces.enabled? == true
-> config/traces.rb prepare method called
-> providers loaded, methods wrapped with spans
Enabling Tracing
1. Choose a Backend
Install one of the available backends:
| Backend | Gem | Purpose |
|---|---|---|
| Console | (built-in) | Log spans to stdout via the console gem |
| Test | (built-in) | Validate tracing interface usage in tests |
| OpenTelemetry | traces-backend-open_telemetry |
Export to any OTLP-compatible collector |
| Datadog | traces-backend-datadog |
Export to Datadog APM |
| New Relic | traces-backend-newrelic |
Export to New Relic |
For OpenTelemetry:
# Gemfile
gem "traces-backend-open_telemetry"
gem "opentelemetry-sdk"
gem "opentelemetry-exporter-otlp"
2. Set the Backend Environment Variable
The TRACES_BACKEND environment variable must be set before require "traces" is evaluated (which happens automatically when async is required).
# Console backend (quick debugging)
TRACES_BACKEND=traces/backend/console bundle exec falcon serve
# OpenTelemetry backend
TRACES_BACKEND=traces/backend/open_telemetry bundle exec falcon serve
3. Load the A2A Providers
If you are running your app from the project root (where config/traces.rb exists), the providers are loaded automatically via the prepare hook.
If you are consuming agent2agent as an external gem in your own application, create a config/traces.rb in your app root:
# config/traces.rb
def prepare
require "traces/provider/a2a"
end
This tells the traces gem to load all A2A provider modules when a backend is active.
OpenTelemetry Setup
A complete example with OpenTelemetry and an OTLP exporter:
# Gemfile
gem "agent2agent"
gem "traces-backend-open_telemetry"
gem "opentelemetry-sdk"
gem "opentelemetry-exporter-otlp"
# config/initializers/opentelemetry.rb (or top of config.ru)
require "opentelemetry/sdk"
require "opentelemetry-exporter-otlp"
OpenTelemetry::SDK.configure do |c|
c.service_name = "my-a2a-agent"
c.add_span_processor(
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
OpenTelemetry::Exporter::OTLP::Exporter.new
)
)
end
TRACES_BACKEND=traces/backend/open_telemetry \
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 \
bundle exec falcon serve
Spans will appear in your collector (Jaeger, Grafana Tempo, etc.) with the full request lifecycle.
Console Backend (Quick Debugging)
For local development, the built-in console backend logs spans directly:
TRACES_BACKEND=traces/backend/console bundle exec falcon serve
Output:
0.0s info: traces [oid=0x...] [ec=0x...] [pid=...]
| a2a.server.call
| {"http.method"=>"POST", "http.path"=>"/a2a", "http.status_code"=>200}
Testing with Traces
Use the built-in test backend to validate spans in your test suite:
# config/sus.rb or test_helper.rb
ENV["TRACES_BACKEND"] ||= "traces/backend/test"
The test backend validates that all Traces.trace calls use correct argument types without emitting output.
Available Providers
To list all trace providers available across your loaded gems:
bundle exec bake traces:provider:list
This scans all gems for files under traces/provider/**/*.rb in their require paths.
Span Reference
a2a.server.call
The outermost span covering the full Rack request/response cycle.
http.method-- HTTP method (GET, POST, etc.)http.path-- Request pathhttp.status_code-- Response status code
a2a.server.triage.call
Resolves which A2A operation the request maps to.
a2a.operation-- Resolved operation name (e.g.,send_message,get_task)a2a.proto_operation-- Protocol buffer operation name
a2a.server.dispatcher.call
Dispatches the resolved operation to the agent handler.
a2a.operation-- Operation being dispatchedhttp.status_code-- Response status codea2a.response_type-- One ofstream,error, orresult
a2a.bindings.json_rpc.call
Handles JSON-RPC 2.0 protocol binding.
http.status_code-- Response status coderpc.method-- JSON-RPC method namerpc.jsonrpc.request_id-- JSON-RPC request IDa2a.response_type-- One ofstream,error, orresult
a2a.bindings.rest.call
Handles HTTP+JSON/REST protocol binding.
http.status_code-- Response status codea2a.verb-- REST verb (e.g.,send,get,cancel)a2a.path-- Request patha2a.response_type-- One ofstream,error, orresult