Ruby instrumentation
Instrument a Ruby or Rails application with OpenTelemetry and send traces to Maple.
This guide sets up the OpenTelemetry Ruby SDK so your Rails, Sinatra or plain Ruby application sends traces to Maple, with instrumentation for Rails, Active Record, Sidekiq, Net::HTTP and the other libraries in opentelemetry-.
To have a coding agent do this setup, use the maple-onboard skill, and maple-audit to check an existing setup.
Prerequisites
- Ruby 3.3+ (the version current releases of
opentelemetry-sdkrequire) - Bundler
- An ingest key from Settings → Ingestion in Maple. Use the private key (
maple_sk_…) for server applications.
Install
Add the SDK, the OTLP exporter and the instrumentation bundle to your Gemfile:
# Gemfile
gem "opentelemetry-sdk"
gem "opentelemetry-exporter-otlp"
gem "opentelemetry-instrumentation-all"
bundle install
opentelemetry- pulls in the instrumentation for every library it supports. Each one only turns on when its library is loaded, so unused ones cost nothing.
Configure Rails
Create an initializer. It runs in every Rails process, including the console, runners and Sidekiq workers:
# config/initializers/opentelemetry.rb
require "opentelemetry/sdk"
require "opentelemetry/exporter/otlp"
require "opentelemetry/instrumentation/all"
OpenTelemetry::SDK.configure do |c|
c.service_name = "my-rails-app"
c.use_all
end
use_all enables every installed instrumentation: Rails (Action Pack, Action View, Active Record, Active Job, Active Support), Rack, Net::HTTP, Faraday, pg, mysql2, Redis, Sidekiq and more. Each request becomes a server span, named after the method and route, with child spans for the queries and outgoing calls it made.
The batch span processor restarts its export thread after a fork, so Puma in cluster mode and other forking servers need no extra setup.
Environment variables
The SDK and the exporter read the standard variables, so the endpoint and key stay out of source:
export OTEL_SERVICE_NAME="my-rails-app"
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.maple.dev"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer YOUR_INGEST_KEY"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=production,vcs.repository.url.full=https://github.com/acme/my-rails-app"
EU organizations use https://ingest.eu.maple.dev. The exporter appends /v1/traces to the endpoint itself. OTEL_SERVICE_NAME overrides the service_name set in the initializer, so you can drop that line once the variable is set everywhere.
The Ruby exporter speaks OTLP over HTTP with protobuf encoding only. Leave OTEL_EXPORTER_OTLP_PROTOCOL unset or set it to http/protobuf; with grpc the SDK logs a warning and exports nothing. To compress batches, set OTEL_.
Sinatra and plain Ruby
Outside Rails, configure the SDK before your app is defined. For Sinatra, add gem "sinatra" as usual and put the setup at the top of the app file or in config.ru:
# app.rb
require "opentelemetry/sdk"
require "opentelemetry/exporter/otlp"
require "opentelemetry/instrumentation/all"
OpenTelemetry::SDK.configure do |c|
c.use_all
end
require "sinatra"
get "/hello" do
"hello"
end
For a script or a one-off job, create your own spans and shut the provider down before exit, so the last batch is sent:
require "opentelemetry/sdk"
require "opentelemetry/exporter/otlp"
OpenTelemetry::SDK.configure
at_exit { OpenTelemetry.tracer_provider.shutdown }
tracer = OpenTelemetry.tracer_provider.tracer("nightly-import")
tracer.in_span("import-orders") do |span|
span.set_attribute("import.source", "s3")
import_orders
end
Sidekiq
With the Rails initializer, Sidekiq needs no extra setup: the Sidekiq server process loads Rails and runs the initializer, and use_all enables the Sidekiq instrumentation. Each enqueue becomes a producer span and each job run a consumer span.
By default the job’s span starts a new trace and links back to the span that enqueued it. To make the job a child of the request that enqueued it, so both appear in one trace, set propagation_style to :child:
OpenTelemetry::SDK.configure do |c|
c.use_all({
"OpenTelemetry::Instrumentation::Sidekiq" => { propagation_style: :child },
})
end
Long-running jobs that enqueue many others make very large traces with :child, so keep the default for those.
Custom spans
class ProcessOrder
TRACER = OpenTelemetry.tracer_provider.tracer("my-rails-app")
def call(order_id)
TRACER.in_span("process-order") do |span|
span.set_attribute("order.id", order_id)
span.set_attribute("payment.method", "card")
charge_payment(order_id)
end
end
end
in_span records any exception raised inside the block on the span, sets its status to error and re-raises. To mark a failure you handle yourself, call span.record_exception(e) and span..
Service map edges come from instrumented client spans that propagate traceparent to an instrumented callee, not from attributes such as peer.service. See Service map.
Attributes Maple uses
Most attributes are optional. These unlock specific features.
Commits and GitHub
Set vcs.ref.head.revision (the full 40-character commit SHA) and vcs.repository.url.full on the resource. They drive deploy markers, the Releases page and, with the GitHub integration, commit hover cards on traces. Read the SHA from your CI or host at build time, such as GITHUB_SHA or HEROKU_SLUG_COMMIT, not from git at runtime:
export OTEL_RESOURCE_ATTRIBUTES="vcs.repository.url.full=https://github.com/acme/api,vcs.ref.head.revision=$GITHUB_SHA"
Databases
The pg, mysql2 and trilogy instrumentations set the standard db.* attributes for you. The database system and name draw the database node on the service map, and the query text groups statements into query shapes. The pg and mysql2 instrumentations obfuscate literal values in the query text by default; keep that on. See Database queries for the full list.
Logs and metrics
The Ruby SDK’s trace support is stable. Its metrics and logs SDKs (opentelemetry-metrics-sdk, opentelemetry-logs-sdk and their OTLP exporters) are still experimental, and their APIs can change between releases. This guide covers traces only. If you try the experimental exporters, they use the same endpoint and headers.
Verify
- Start your application and send it a few requests, or enqueue a Sidekiq job.
- In Maple, open Explore → Traces. The SDK sends spans in batches every 5 seconds by default.
- Each request should show up as one trace with a single root server span, named after the method and route (for example
GET /orders/:idon Rails 7.1 and later), and child spans for the queries and outgoing calls it made.
Your service also appears on the Services page once its first spans arrive.
Troubleshooting
401responses. The key is wrong, was copied from the other region, or the header is malformed.OTEL_EXPORTER_OTLP_HEADERStakesAuthorization=Bearer YOUR_. See Ingest API status codes.INGEST_ KEY The grpc transport protocol is not supportedin the log.OTEL_EXPORTER_OTLP_PROTOCOLis set togrpc, often inherited from a shared environment. Unset it or sethttp/protobuf.please add opentelemetry-in the log. The exporter gem is missing from the bundle, so spans are not exported.exporter- otlp to your Gemfile - Network. From the machine running the app, run
curl -i https://ingest.maple.dev/. Any HTTP status code means the host can reach Maple. A timeout or DNS error means a firewall or proxy is blocking outbound HTTPS.v1/ traces -X POST - Nothing exported. Set
OTEL_LOG_LEVEL=debugto print the SDK’s export errors. For a quick local check, setOTEL_TRACES_EXPORTER=consoleto print spans to stdout instead of sending them. Scripts that exit right away must callOpenTelemetry.first.tracer_ provider. shutdown