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.agent.call A2A::Agent a2a.operation, a2a.response_type, a2a.error.code
a2a.bindings.json_rpc.call A2A::Server::Bindings::JsonRpc rpc.method, rpc.jsonrpc.request_id, a2a.response_type
a2a.bindings.rest.call A2A::Server::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:

  1. Providers wrap existing methods with trace spans using Traces::Provider(SomeClass). They live in lib/traces/provider/a2a/.
  2. Backends connect those spans to an observability system (OpenTelemetry, Datadog, console output, etc.).
  3. Config (config/traces.rb) tells the traces gem 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"=>"/", "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 path
  • http.status_code – Response status code

a2a.server.triage.call

Resolves which A2A operation the request maps to.

  • a2a.operation – Resolved operation name (e.g., SendMessage, GetTask)
  • a2a.proto_operation – Protocol buffer operation name

a2a.agent.call

Runs your handler block (wrapped in the A2A::Agent error boundary).

  • a2a.operation – Operation being handled
  • a2a.response_type – One of stream, error, or result
  • a2a.error.code – JSON-RPC error code (only when the result is an error)

a2a.bindings.json_rpc.call

Handles JSON-RPC 2.0 protocol binding.

  • http.status_code – Response status code
  • rpc.method – JSON-RPC method name
  • rpc.jsonrpc.request_id – JSON-RPC request ID
  • a2a.response_type – One of stream, error, or result

a2a.bindings.rest.call

Handles HTTP+JSON/REST protocol binding.

  • http.status_code – Response status code
  • a2a.verb – Lowercase HTTP method (e.g., post, get, delete)
  • a2a.path – Request path with the /rest prefix stripped
  • a2a.response_type – One of stream, error, or result

This site uses Just the Docs, a documentation theme for Jekyll.