Resource Detectors in OpenTelemetry Ruby
Resource detectors are used to automatically detect and collect information about the environment in which your Ruby application is running. This information is then attached to the telemetry data (traces and metrics) to provide additional context.
The Ruby SDK detects resources from a variety of sources, and by default will use all available resource detectors: environment variables (OTEL_RESOURCE_ATTRIBUTES, OTEL_SERVICE_NAME), host, operating system, and process information.
Built-in detectors
The Ruby SDK includes several built-in resource detectors:
- env - reads
OTEL_RESOURCE_ATTRIBUTESandOTEL_SERVICE_NAME - host - detects host information
- os - detects operating system details
- process - detects Ruby process information
Rails application detection
For Rails applications, you can automatically detect service information:
# config/initializers/opentelemetry.rb
require 'opentelemetry/sdk'
OpenTelemetry::SDK.configure do |c|
# Automatically detect Rails application name
c.service_name = Rails.application.class.module_parent_name.downcase
c.service_version = ENV['APP_VERSION'] || '1.0.0'
# Add Rails-specific resource attributes
c.resource = OpenTelemetry::SDK::Resources::Resource.create({
'service.namespace' => 'rails',
'deployment.environment' => Rails.env,
'framework.name' => 'rails',
'framework.version' => Rails::VERSION::STRING
})
end
Container detection
For containerized Ruby applications, you can detect container information:
def detect_container_info
container_attrs = {}
# Docker container ID
if File.exist?('/proc/self/cgroup')
cgroup_content = File.read('/proc/self/cgroup')
if match = cgroup_content.match(/\/docker\/([a-f0-9]{64})/)
container_attrs['container.id'] = match[1][0..11] # Short ID
end
end
# Container image from environment
if ENV['DOCKER_IMAGE']
container_attrs['container.image.name'] = ENV['DOCKER_IMAGE']
end
container_attrs
rescue StandardError => e
puts "Could not detect container info: #{e.message}"
{}
end
# Usage
OpenTelemetry::SDK.configure do |c|
c.service_name = 'my-ruby-app'
resource_attrs = {
'deployment.environment' => ENV['RAILS_ENV'] || 'development'
}.merge(detect_container_info)
c.resource = OpenTelemetry::SDK::Resources::Resource.create(resource_attrs)
end
Cloud platform detection
Basic cloud platform detection for common providers:
def detect_cloud_platform
cloud_attrs = {}
# AWS EC2
if ENV['AWS_REGION']
cloud_attrs.merge!({
'cloud.provider' => 'aws',
'cloud.region' => ENV['AWS_REGION']
})
# ECS Task metadata
if ENV['ECS_CONTAINER_METADATA_URI_V4']
cloud_attrs['cloud.platform'] => 'aws_ecs'
end
end
# Google Cloud
if ENV['GOOGLE_CLOUD_PROJECT']
cloud_attrs.merge!({
'cloud.provider' => 'gcp',
'cloud.project.id' => ENV['GOOGLE_CLOUD_PROJECT']
})
end
# Heroku
if ENV['DYNO']
cloud_attrs.merge!({
'cloud.provider' => 'heroku',
'service.instance.id' => ENV['DYNO']
})
end
cloud_attrs
rescue StandardError => e
puts "Could not detect cloud platform: #{e.message}"
{}
end
# Usage
OpenTelemetry::SDK.configure do |c|
c.service_name = 'my-app'
resource_attrs = detect_cloud_platform
c.resource = OpenTelemetry::SDK::Resources::Resource.create(resource_attrs)
end
Custom resource via environment
The simplest way to add custom resources is via the OTEL_RESOURCE_ATTRIBUTES environment variable:
export OTEL_RESOURCE_ATTRIBUTES="service.name=my-ruby-app,service.namespace=backend,service.version=2.1.0,deployment.environment=production"
Custom resource in code
Custom resources can also be configured directly in your code:
require 'opentelemetry/sdk'
# Create custom resource
custom_resource = OpenTelemetry::SDK::Resources::Resource.create({
'service.namespace' => 'backend',
'service.name' => 'user-service',
'service.instance.id' => Socket.gethostname,
'service.version' => '2.1.0',
'deployment.environment' => ENV['RAILS_ENV'] || 'development'
})
# The SDK will automatically merge with default detected resources
OpenTelemetry::SDK.configure do |c|
c.resource = custom_resource
end
Ruby-specific resource detection
Create a helper to detect Ruby-specific information:
def detect_ruby_resources
{
'process.runtime.name' => 'ruby',
'process.runtime.version' => RUBY_VERSION,
'process.runtime.description' => RUBY_DESCRIPTION,
'process.pid' => Process.pid,
'process.command' => $PROGRAM_NAME
}
end
def detect_framework_resources
attrs = {}
# Rails detection
if defined?(Rails)
attrs.merge!({
'framework.name' => 'rails',
'framework.version' => Rails::VERSION::STRING,
'service.name' => Rails.application.class.module_parent_name.downcase
})
end
# Sinatra detection
if defined?(Sinatra)
attrs.merge!({
'framework.name' => 'sinatra',
'framework.version' => Sinatra::VERSION
})
end
# Rack detection
if defined?(Rack)
attrs['framework.name'] ||= 'rack'
attrs['framework.version'] ||= Rack::VERSION.join('.')
end
attrs
end
# Complete resource detection
OpenTelemetry::SDK.configure do |c|
resource_attrs = {}
.merge(detect_ruby_resources)
.merge(detect_framework_resources)
.merge(detect_cloud_platform)
c.resource = OpenTelemetry::SDK::Resources::Resource.create(resource_attrs)
end
Production considerations
Performance
Resource detection runs during application initialization. For production Ruby applications:
- Resource detection is fast and doesn't impact runtime performance
- Detection happens once at startup
- Failed detectors don't prevent application startup
Error handling
Resource detectors are designed to be resilient:
def safe_resource_detection
resource_attrs = {
'service.name' => ENV['OTEL_SERVICE_NAME'] || 'ruby-app',
'service.version' => ENV['APP_VERSION'] || 'unknown'
}
# Safe cloud detection
begin
resource_attrs.merge!(detect_cloud_platform)
rescue StandardError => e
puts "Warning: Could not detect cloud platform: #{e.message}"
end
# Safe container detection
begin
resource_attrs.merge!(detect_container_info)
rescue StandardError => e
puts "Warning: Could not detect container info: #{e.message}"
end
resource_attrs
end
# Usage in production
OpenTelemetry::SDK.configure do |c|
c.resource = OpenTelemetry::SDK::Resources::Resource.create(
safe_resource_detection
)
end
Example production configuration
# config/initializers/opentelemetry.rb
require 'opentelemetry/sdk'
# Configure based on environment
env = Rails.env
case env
when 'production'
# Explicit configuration for production
resource_attrs = {
'service.name' => ENV['SERVICE_NAME'] || Rails.application.class.module_parent_name.downcase,
'service.version' => ENV['APP_VERSION'] || 'unknown',
'service.namespace' => ENV['SERVICE_NAMESPACE'] || 'backend',
'deployment.environment' => env
}.merge(detect_cloud_platform)
when 'development'
# Comprehensive detection for development
resource_attrs = detect_ruby_resources
.merge(detect_framework_resources)
.merge('deployment.environment' => env)
else
# Default configuration
resource_attrs = {
'service.name' => Rails.application.class.module_parent_name.downcase,
'deployment.environment' => env
}
end
OpenTelemetry::SDK.configure do |c|
c.resource = OpenTelemetry::SDK::Resources::Resource.create(resource_attrs)
end