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_ATTRIBUTES and OTEL_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:

ruby
# 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:

ruby
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:

ruby
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:

bash
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:

ruby
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:

ruby
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:

ruby
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

ruby
# 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

What's next?