OpenTelemetry Ruby Configuration for Uptrace
This guide shows you how to configure the OpenTelemetry Ruby SDK to export spans and metrics to Uptrace using the OTLP/HTTP protocol.
For detailed API documentation, see:
Overview
OpenTelemetry provides observability for your Ruby applications by collecting telemetry data (traces and metrics) and exporting it to monitoring systems like Uptrace.
Prerequisites
Ensure that you have the following installed locally:
- Ruby >= 3.0 (JRuby >= 9.3.2.0, TruffleRuby >= 22.1 supported)
- Bundler for dependency management
While tested, support for JRuby and TruffleRuby is provided on a best-effort basis at this time.
Installation Options
Option 1: Using Uptrace Ruby Distribution (Recommended)
uptrace-ruby is a thin wrapper over opentelemetry-ruby that configures the OpenTelemetry SDK to export data to Uptrace. It does not add any new functionality and is provided only for your convenience.
Installation:
Add to Gemfile
:
gem 'uptrace'
Or install the gem:
gem install uptrace
Option 2: Direct OTLP Exporter
Skip the wrapper and use the OTLP exporter directly if you prefer more control over the configuration. See the Direct OTLP Configuration section below.
Quick Start Guide
Get your first trace running in 5 minutes by following these steps:
Step 1: Create an Uptrace Project
- Create an Uptrace project to obtain your DSN (Data Source Name)
- Your DSN will look like:
https://<secret>@api.uptrace.dev?grpc=4317
Step 2: Install the Package
gem install uptrace
Step 3: Basic Configuration
Copy the code to main.rb
:
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'rubygems'
require 'bundler/setup'
require 'uptrace'
# Configure OpenTelemetry with sensible defaults.
# DSN can be set via UPTRACE_DSN environment variable.
# Example: export UPTRACE_DSN="https://<project_secret>@uptrace.dev?grpc=4317"
Uptrace.configure_opentelemetry(dsn: '') do |c|
# c is an instance of OpenTelemetry::SDK::Configurator
# Configure service metadata (helps identify this service in Uptrace).
c.service_name = 'myservice'
c.service_version = '1.0.0'
# Add environment information
c.resource = OpenTelemetry::SDK::Resources::Resource.create(
'deployment.environment' => ENV.fetch('RACK_ENV', 'development')
)
end
# Ensure spans are flushed even if the program exits unexpectedly.
at_exit { OpenTelemetry.tracer_provider.shutdown }
# Register a tracer (usually stored globally).
TRACER = OpenTelemetry.tracer_provider.tracer('my_app', '0.1.0')
# Example trace with nested spans.
TRACER.in_span('main-operation', kind: :server) do |main_span|
# Simulate an HTTP request span.
TRACER.in_span('GET /posts/:id', kind: :client) do |http_span|
http_span.set_attribute('http.method', 'GET')
http_span.set_attribute('http.route', '/posts/:id')
http_span.set_attribute('http.url', 'http://localhost:8080/posts/123')
http_span.set_attribute('http.status_code', 200)
http_span.record_exception(ArgumentError.new('Invalid parameter'))
end
# Simulate a database query span.
TRACER.in_span('SELECT posts', kind: :client) do |db_span|
db_span.set_attribute('db.system', 'mysql')
db_span.set_attribute('db.statement', 'SELECT * FROM posts LIMIT 100')
end
# Print the trace URL (clickable in console).
puts "Trace URL: #{Uptrace.trace_url(main_span)}"
end
Step 4: Run Your Application
Run the code, replacing <FIXME>
with your Uptrace DSN:
UPTRACE_DSN="<FIXME>" ruby main.rb
Expected output:
trace URL: https://app.uptrace.dev/traces/<trace_id>
Step 5: View Your Trace
Follow the link to view the generated trace:
Configuration Options
Resource Attributes
Resource attributes provide metadata about your service:
Uptrace.configure_opentelemetry() do |c|
c.service_name = 'user-service'
c.service_version = '2.1.0'
c.resource = OpenTelemetry::SDK::Resources::Resource.create({
'service.namespace' => 'backend',
'service.instance.id' => 'instance-001',
'deployment.environment' => 'production',
'cloud.provider' => 'aws',
'cloud.region' => 'us-west-2'
})
end
Sampling Configuration
For production environments with high traffic, configure sampling to reduce overhead:
# Set sampling rate through environment variable
ENV['OTEL_TRACES_SAMPLER'] = 'traceidratio'
ENV['OTEL_TRACES_SAMPLER_ARG'] = '0.1' # Sample 10% of traces
Uptrace.configure_opentelemetry() do |c|
c.service_name = 'high-traffic-service'
# Sampling configuration will be read from environment variables
end
Auto-Instrumentation
OpenTelemetry Ruby automatically instruments popular libraries and frameworks without requiring code changes.
Supported Libraries
Auto-instrumentation is available for:
- Web frameworks: Rails, Sinatra, Rack
- HTTP clients: Net::HTTP, Faraday, RestClient
- Databases: ActiveRecord, Redis, PostgreSQL (pg), MySQL (mysql2)
- Message queues: Sidekiq
- Other libraries: GraphQL, Bunny (RabbitMQ)
Installation
To use auto-instrumentation in a Ruby project, add it to the Gemfile:
gem 'opentelemetry-instrumentation-all'
Then run bundle install
to install the gem and its dependencies.
Enabling Auto-Instrumentation
The c.use_all()
method enables all available instrumentations:
require 'uptrace'
require 'opentelemetry/instrumentation/all'
Uptrace.configure_opentelemetry(dsn: dsn) do |c|
c.service_name = 'my-rails-app'
c.use_all() # enables all instrumentation!
end
Selective Instrumentation
For more control, enable specific instrumentations:
Uptrace.configure_opentelemetry(dsn: dsn) do |c|
c.service_name = 'my-app'
# Enable specific instrumentations
c.use 'OpenTelemetry::Instrumentation::Rails'
c.use 'OpenTelemetry::Instrumentation::Net::HTTP'
c.use 'OpenTelemetry::Instrumentation::Redis'
c.use 'OpenTelemetry::Instrumentation::PG', {
# Configure database statement handling
db_statement: :obfuscate # Options: :include, :omit, :obfuscate
}
end
Environment Variable Control
Disable specific instrumentations using environment variables:
# Disable Sinatra instrumentation
export OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED=false
# Disable Redis instrumentation
export OTEL_RUBY_INSTRUMENTATION_REDIS_ENABLED=false
Framework Integration
Rails Applications
For Rails applications, create an initializer:
# config/initializers/opentelemetry.rb
require 'uptrace'
require 'opentelemetry/instrumentation/all'
Uptrace.configure_opentelemetry do |c|
c.service_name = ENV['OTEL_SERVICE_NAME'] || Rails.application.class.module_parent_name.downcase
c.service_version = ENV['OTEL_SERVICE_VERSION'] || '1.0.0'
# Add environment information
c.resource = OpenTelemetry::SDK::Resources::Resource.create(
'deployment.environment' => Rails.env
)
c.use_all() # enables all instrumentation!
end
Sinatra Applications
For Sinatra applications, initialize early in your app:
require 'sinatra'
require 'uptrace'
require 'opentelemetry/instrumentation/all'
# Configure OpenTelemetry before defining routes
Uptrace.configure_opentelemetry do |c|
c.service_name = 'my-sinatra-app'
c.use_all()
end
get '/hello' do
'Hello World!'
end
Rack Middleware
For custom Rack applications:
require 'rack'
require 'uptrace'
Uptrace.configure_opentelemetry do |c|
c.service_name = 'my-rack-app'
c.use 'OpenTelemetry::Instrumentation::Rack'
end
app = Proc.new do |env|
['200', {'Content-Type' => 'text/html'}, ['Hello World!']]
end
run app
Already Using OTLP Exporter?
Uptrace fully supports the OpenTelemetry Protocol (OTLP) over both gRPC and HTTP transports.
If you already have an OTLP exporter configured, you can continue using it with Uptrace by simply pointing it to the Uptrace OTLP endpoint.
Connecting to Uptrace
Choose an OTLP endpoint from the table below and pass your DSN via the uptrace-dsn
header for authentication:
Transport | Endpoint | Port |
---|---|---|
gRPC | https://api.uptrace.dev:4317 | 4317 |
HTTP | https://api.uptrace.dev | 443 |
When using HTTP transport, you often need to specify the full URL for each signal type:
https://api.uptrace.dev/v1/traces
https://api.uptrace.dev/v1/logs
https://api.uptrace.dev/v1/metrics
Note: Most OpenTelemetry SDKs support both transports. Use HTTP unless you're already familiar with gRPC.
Recommended Settings
For performance and reliability, we recommend:
- Use
BatchSpanProcessor
andBatchLogProcessor
for batching spans and logs, reducing the number of export requests. - Enable
gzip
compression to reduce bandwidth usage. - Prefer
delta
metrics temporality (Uptrace converts cumulative metrics automatically). - Use Protobuf encoding instead of JSON (Protobuf is more efficient and widely supported).
- Use HTTP transport for simplicity and fewer configuration issues (unless you're already familiar with gRPC).
- Optionally, use the AWS X-Ray ID generator to produce trace IDs compatible with AWS X-Ray.
Common Environment Variables
You can use environment variables to configure resource attributes and propagators::
Variable | Description |
---|---|
OTEL_RESOURCE_ATTRIBUTES | Comma-separated resource attributes, e.g., service.name=myservice,service.version=1.0.0 . |
OTEL_SERVICE_NAME=myservice | Sets the service.name attribute (overrides OTEL_RESOURCE_ATTRIBUTES ). |
OTEL_PROPAGATORS | Comma-separated list of context propagators (default: tracecontext,baggage ). |
Most language SDKs allow configuring the OTLP exporter entirely via environment variables:
# Endpoint (choose HTTP or gRPC)
export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.uptrace.dev" # HTTP
#export OTEL_EXPORTER_OTLP_ENDPOINT="https://api.uptrace.dev:4317" # gRPC
# Pass DSN for authentication
export OTEL_EXPORTER_OTLP_HEADERS="uptrace-dsn=<FIXME>"
# Performance optimizations
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
export OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=BASE2_EXPONENTIAL_BUCKET_HISTOGRAM
export OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE=DELTA
Configure BatchSpanProcessor
to balance throughput and payload size:
export OTEL_BSP_EXPORT_TIMEOUT=10000 # Max export timeout (ms)
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=10000 # Avoid >32MB payloads
export OTEL_BSP_MAX_QUEUE_SIZE=30000 # Adjust for available memory
export OTEL_BSP_MAX_CONCURRENT_EXPORTS=2 # Parallel exports
Exporting Traces
Here is how you can configure the OTLP/gRPC spans exporter for Uptrace following the recommendations above:
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'bundler/setup'
require 'opentelemetry/sdk'
require 'opentelemetry/exporter/otlp'
require 'opentelemetry-propagator-xray'
require 'opentelemetry/instrumentation/all'
# Fetch Uptrace DSN from environment (required)
dsn = ENV.fetch('UPTRACE_DSN', nil)
abort('Missing UPTRACE_DSN environment variable') unless dsn
puts "Using Uptrace DSN: #{dsn}"
# Configure OTLP exporter to send data to Uptrace
exporter = OpenTelemetry::Exporter::OTLP::Exporter.new(
endpoint: 'https://api.uptrace.dev/v1/traces',
headers: { 'uptrace-dsn': dsn }, # Uptrace authentication
compression: 'gzip'
)
# Use a batch span processor for better performance
span_processor = OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
exporter,
max_queue_size: 1000,
max_export_batch_size: 512 # smaller batch size helps avoid large payloads
)
# Configure the global OpenTelemetry SDK
OpenTelemetry::SDK.configure do |c|
c.service_name = 'myservice' # Customize your service name
c.service_version = '1.0.0' # Optional: version for observability
c.id_generator = OpenTelemetry::Propagator::XRay::IDGenerator # Optional: AWS X-Ray style IDs
c.add_span_processor(span_processor)
c.use_all
end
# Get a tracer for your app
tracer = OpenTelemetry.tracer_provider.tracer('my_app_or_gem', '1.0.0')
# Create a sample trace
tracer.in_span('main-operation') do |span|
puts "Trace ID: #{span.context.hex_trace_id}"
end
# Ensure all spans are flushed before exiting
OpenTelemetry.tracer_provider.shutdown
Exporting Logs
Here is how you can configure the OTLP/gRPC logs exporter for Uptrace following the recommendations above:
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'rubygems'
require 'bundler/setup'
require 'opentelemetry/sdk'
require 'opentelemetry-exporter-otlp'
require 'opentelemetry-logs-sdk'
require 'opentelemetry/exporter/otlp_logs'
# Ensure DSN is set
dsn = ENV.fetch('UPTRACE_DSN', nil)
abort('Missing UPTRACE_DSN environment variable') unless dsn
# Configure OpenTelemetry (for traces, metrics, and logs if desired)
OpenTelemetry::SDK.configure
# Configure log exporter
log_exporter = OpenTelemetry::Exporter::OTLP::Logs::LogsExporter.new(
endpoint: 'https://api.uptrace.dev/v1/logs',
headers: { 'uptrace-dsn': dsn }, # Uptrace auth
compression: 'gzip', # Reduce bandwidth
timeout: 10 # Seconds
)
# Attach batch processor (buffers + exports logs)
processor = OpenTelemetry::SDK::Logs::Export::BatchLogRecordProcessor.new(log_exporter)
OpenTelemetry.logger_provider.add_log_record_processor(processor)
# Ensure we flush logs on shutdown
at_exit { OpenTelemetry.logger_provider.shutdown }
# Create a logger (can be reused globally)
LOGGER = OpenTelemetry.logger_provider.logger(name: 'my_app', version: '1.0.0')
# Helper for structured logging
def log_info(message, attrs = {})
LOGGER.on_emit(
timestamp: Time.now.utc,
severity_text: 'INFO',
body: message,
attributes: attrs
)
end
# Example usage
log_info('Application started', service: 'user-service', region: 'eu-west-1')
log_info('Processing completed', status: 'success')
Exporting Metrics
Here is how you can configure the OTLP/gRPC metrics exporter for Uptrace following the recommendations above:
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'rubygems'
require 'bundler/setup'
require 'opentelemetry/sdk'
require 'opentelemetry/exporter/otlp'
require 'opentelemetry-metrics-sdk'
require 'opentelemetry-exporter-otlp-metrics'
# Fetch Uptrace DSN from environment (required)
dsn = ENV.fetch('UPTRACE_DSN', nil)
abort('Missing UPTRACE_DSN environment variable') unless dsn
puts "Using Uptrace DSN: #{dsn}"
# Configure the OTLP metrics exporter
metric_exporter = OpenTelemetry::Exporter::OTLP::Metrics::MetricsExporter.new(
endpoint: 'https://api.uptrace.dev/v1/metrics',
headers: { 'uptrace-dsn': dsn }, # Uptrace authentication
compression: 'gzip'
)
# Periodic reader pushes metrics every 5 seconds
metric_reader = OpenTelemetry::SDK::Metrics::Export::PeriodicMetricReader.new(
exporter: metric_exporter,
export_interval_millis: 5_000,
export_timeout_millis: 10_000
)
# Initialize the SDK with the custom metric reader
OpenTelemetry::SDK.configure do |c|
c.add_metric_reader(metric_reader)
end
# Obtain a Meter instance
meter = OpenTelemetry.meter_provider.meter('example-meter')
# Create a histogram instrument
histogram = meter.create_histogram(
'example_histogram',
unit: 'items',
description: 'Example histogram metric'
)
trap('INT') do
puts "\nShutting down..."
OpenTelemetry.meter_provider.shutdown
exit
end
# Record some metric values periodically
loop do
value = rand(100..200)
puts "Recording histogram value: #{value}"
histogram.record(value, attributes: { 'env' => 'dev', 'feature' => 'demo' })
sleep 1
end
Troubleshooting
Common Issues
Missing traces:
- Check that
UPTRACE_DSN
is set correctly - Verify network connectivity to
api.uptrace.dev:4317
- Ensure spans are being sampled (check sampling configuration)
SSL/Network errors:
- Verify firewall allows outbound connections on port 4317
- Check if corporate proxy is interfering with HTTPS connections
Performance issues:
- Reduce sampling rate in high-traffic environments
- Tune batch processor settings (
max_queue_size
,max_export_batch_size
)
Debug Mode
Enable debug logging to troubleshoot issues:
require 'logger'
require 'uptrace'
require 'opentelemetry/instrumentation/all'
# Enable debug logging for OpenTelemetry
OpenTelemetry.logger = Logger.new(STDOUT)
OpenTelemetry.logger.level = Logger::DEBUG
Uptrace.configure_opentelemetry(dsn: dsn) do |c|
c.service_name = 'debug-service'
c.use_all()
end
Health Checks
Implement health checks to monitor telemetry:
require 'opentelemetry'
def check_telemetry_health
"""Check if telemetry is working correctly"""
tracer = OpenTelemetry.tracer_provider.tracer('health-check')
begin
tracer.in_span('health-check') do |span|
span.set_attribute('health.status', 'ok')
span.set_attribute('check.timestamp', Time.now.to_f)
# Check if span is recording
unless span.recording?
return [false, 'Span not recording']
end
[true, 'Telemetry is healthy']
end
rescue StandardError => e
[false, "Telemetry check failed: #{e.message}"]
end
end
# Use in your application
healthy, message = check_telemetry_health
puts "Telemetry health check: #{message}" unless healthy
Checking Instrumentation Status
Verify which instrumentations are active:
# Check loaded instrumentations
OpenTelemetry.instrumentation_registry.each do |name, instrumentation|
puts "#{name}: #{instrumentation.enabled? ? 'enabled' : 'disabled'}"
end
What's Next?
Next, instrument more operations to get a more detailed picture. Try to prioritize network calls, disk operations, database queries, errors, and logs.
Now that you have basic tracing working, explore these guides to get more from OpenTelemetry Ruby:
Advanced Configuration:
- Propagation - Context propagation across services
- Sampling - Configure sampling for high-traffic applications
Framework-Specific Guides: