A reference implementation for instrumenting a Rails application with OpenTelemetry to export traces and logs via OTLP. Copy this project as a starting point for adding observability to your own Rails apps.
- Trace exporting via OTLP (stable)
- Log exporting via OTLP (experimental)
- Rails.logger bridge to automatically export logs as OTel log records
- Auto-instrumentation for Rails, ActiveRecord, Net::HTTP, and more
- Log/trace correlation with automatic
trace_idandspan_idinjection - Custom spans with attributes and exception recording
# Clone and install
git clone <this-repo>
cd rails-opentelemetry
bundle install
# Configure environment variables (required)
cp .env.example .env
# Edit .env with your project ID
# Start the server
rails serverThe .env file must contain:
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.observability.app.launchdarkly.com:4318
LAUNCHDARKLY_PROJECT_ID=your-project-id
OTEL_SERVICE_NAME=rails-opentelemetry-demo # optional
OTEL_SERVICE_VERSION=1.0.0 # optionalVisit http://localhost:3000 to see the demo UI with buttons to trigger different telemetry events.
Add these to your Gemfile:
# OpenTelemetry - Traces (stable)
gem "opentelemetry-sdk"
gem "opentelemetry-exporter-otlp"
gem "opentelemetry-instrumentation-all"
# OpenTelemetry - Logs (experimental)
gem "opentelemetry-logs-sdk"
gem "opentelemetry-exporter-otlp-logs"
# Bridge Ruby Logger (used by Rails) -> OTel Logs
gem "opentelemetry-instrumentation-logger"Then run bundle install.
Note: This example uses
dotenv-railsto load environment variables from.envin development/test environments.
Create config/initializers/opentelemetry.rb. See the full implementation in config/initializers/opentelemetry.rb.
The initializer:
- Loads required environment variables (
OTEL_EXPORTER_OTLP_ENDPOINT,LAUNCHDARKLY_PROJECT_ID) - Creates a shared resource with service name, version, and project ID
- Configures the trace pipeline with a batch span processor and OTLP exporter
- Enables auto-instrumentation for Rails, ActiveRecord, Net::HTTP, Logger, etc.
- Configures the logs pipeline (experimental) with a batch log record processor
- Sets up graceful shutdown to flush logs on exit
That's it! With the initializer in place:
- Automatic instrumentation captures Rails requests, database queries, HTTP calls, etc.
- Rails.logger calls are automatically bridged to OTel logs
- Logs inside spans get
trace_idandspan_idfor correlation
For more granular tracing, create custom spans:
class MyController < ApplicationController
def my_action
tracer = OpenTelemetry.tracer_provider.tracer('my-app')
tracer.in_span('custom_operation') do |span|
# Add attributes
span.set_attribute('user.id', current_user.id)
span.set_attribute('operation.type', 'data_processing')
# Your code here
result = perform_operation
# Logs inside the span automatically include trace context
Rails.logger.info "Operation completed result=#{result}"
end
end
endExceptions can be recorded on spans for debugging:
tracer.in_span('risky_operation') do |span|
begin
dangerous_operation
rescue => e
# Record the exception on the span
span.record_exception(e)
span.status = OpenTelemetry::Trace::Status.error(e.message)
Rails.logger.error "Operation failed: #{e.message}"
raise
end
endThis example app includes demo endpoints for testing:
| Endpoint | Description |
|---|---|
GET / |
Demo UI with buttons to trigger events |
GET /log |
Creates log entries at info and debug levels |
GET /slow |
Simulates a slow operation (0.5-2s) with custom span |
GET /error |
Demonstrates error recording in traces |
curl http://localhost:3000/log
curl http://localhost:3000/slow
curl http://localhost:3000/error| Variable | Required | Default | Description |
|---|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT |
Yes | - | OTLP collector endpoint (HTTP), e.g. https://otel.observability.app.launchdarkly.com:4318 |
LAUNCHDARKLY_PROJECT_ID |
Yes | - | Project ID for routing telemetry |
OTEL_SERVICE_NAME |
No | rails-opentelemetry-demo |
Logical name of your service |
OTEL_SERVICE_VERSION |
No | 1.0.0 |
Version of your service |
The highlight.project_id resource attribute routes telemetry to the correct project in the LaunchDarkly observability backend. This is set automatically from the LAUNCHDARKLY_PROJECT_ID environment variable.
Standard semantic conventions for resource attributes (set via environment variables):
service.name- Logical name of your service (OTEL_SERVICE_NAME)service.version- Version of your service (OTEL_SERVICE_VERSION)deployment.environment- Environment (production, staging, etc.) - add to initializer if needed
┌─────────────────────────────────────────────────────────────────┐
│ Rails Application │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Rails.logger ──► Logger Instrumentation ──► OTel Logs Pipeline │
│ │
│ HTTP Requests ──► Rails Instrumentation ──► OTel Traces │
│ │
│ Custom Spans ──────────────────────────────► OTel Traces │
│ │
├─────────────────────────────────────────────────────────────────┤
│ BatchSpanProcessor BatchLogRecordProcessor │
└──────────────────────┬──────────────────────┬───────────────────┘
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ /v1/traces │ │ /v1/logs │
└────────┬───────┘ └────────┬───────┘
│ │
▼ ▼
┌───────────────────────────────────────────────────┐
│ OTLP Collector │
│ (e.g., otel.observability.app.launchdarkly.com) │
└───────────────────────────────────────────────────┘
- Ruby: 3.1.7
- Rails: 7.1.6
- OpenTelemetry SDK: Latest stable
- OpenTelemetry Logs SDK: Experimental
- Logs are experimental: The OpenTelemetry Logs SDK for Ruby is still experimental. APIs may change.
- Batching: Both traces and logs use batch processors for efficient export
- Graceful shutdown: The
at_exithook ensures logs are flushed before the process exits - Auto-instrumentation:
use_allenables instrumentation for Rails, ActiveRecord, Net::HTTP, Faraday, Redis, Sidekiq, and more
- Ensure
OTEL_EXPORTER_OTLP_ENDPOINTandLAUNCHDARKLY_PROJECT_IDare set (both are required) - Copy
.env.exampleto.envand fill in your values - Check that
LAUNCHDARKLY_PROJECT_IDis not blank
- Check the endpoint is reachable:
curl -v $OTEL_EXPORTER_OTLP_ENDPOINT/v1/traces - Verify your project ID is correct
- Check Rails logs for OTel errors on startup
- Ensure the collector accepts HTTP (port 4318), not gRPC (port 4317)
Make sure opentelemetry-instrumentation-logger is installed and use_all is called, which includes the Logger instrumentation.
MIT