This guide shows you how to send metrics from your Ruby application (Rails, Sinatra, Sidekiq) to SigNoz using OpenTelemetry.
The OpenTelemetry Ruby Metrics SDK is currently in Development status (not yet stable). APIs may change in future releases. While it supports synchronous instruments (Counter, UpDownCounter, Histogram) and observable instruments, some features like cumulative temporality, metrics views, and exemplars are not yet implemented. See the official SDK documentation for current status.
Prerequisites
- Ruby 3.1 or later
- Bundler installed
- A SigNoz Cloud account or self-hosted SigNoz instance
Send metrics to SigNoz
Step 1. Set environment variables
Set the following environment variables to configure the OpenTelemetry exporter:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export OTEL_SERVICE_NAME="<service-name>"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_TRACES_EXPORTER="none"
export OTEL_LOGS_EXPORTER="none"
export OTEL_METRIC_EXPORT_INTERVAL="60000"
Verify these values:
<region>: Your SigNoz Cloud region<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: A descriptive name for your service (e.g.,payment-service).OTEL_METRICS_EXPORTER: Set tootlpto export metrics to SigNoz.OTEL_METRIC_EXPORT_INTERVAL: How often to export metrics in milliseconds (default: 60000 for 60 seconds).
Step 1. Set environment variables
Add these environment variables to your deployment manifest:
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: 'https://ingest.<region>.signoz.cloud:443'
- name: OTEL_EXPORTER_OTLP_HEADERS
value: 'signoz-ingestion-key=<your-ingestion-key>'
- name: OTEL_SERVICE_NAME
value: '<service-name>'
- name: OTEL_METRICS_EXPORTER
value: 'otlp'
- name: OTEL_TRACES_EXPORTER
value: 'none'
- name: OTEL_LOGS_EXPORTER
value: 'none'
- name: OTEL_METRIC_EXPORT_INTERVAL
value: '60000'
Verify these values:
<region>: Your SigNoz Cloud region<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: A descriptive name for your service (e.g.,payment-service).OTEL_METRICS_EXPORTER: Set tootlpto export metrics to SigNoz.OTEL_METRIC_EXPORT_INTERVAL: How often to export metrics in milliseconds (default: 60000 for 60 seconds).
Step 1. Set environment variables in Dockerfile
Add environment variables to your Dockerfile:
# ... build stages ...
# Set OpenTelemetry environment variables
ENV OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
ENV OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
ENV OTEL_SERVICE_NAME="<service-name>"
ENV OTEL_METRICS_EXPORTER="otlp"
ENV OTEL_TRACES_EXPORTER="none"
ENV OTEL_LOGS_EXPORTER="none"
ENV OTEL_METRIC_EXPORT_INTERVAL="60000"
CMD ["ruby", "app.rb"]
Or pass them at runtime using docker run:
docker run -e OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443" \
-e OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>" \
-e OTEL_SERVICE_NAME="<service-name>" \
-e OTEL_METRICS_EXPORTER="otlp" \
-e OTEL_TRACES_EXPORTER="none" \
-e OTEL_LOGS_EXPORTER="none" \
-e OTEL_METRIC_EXPORT_INTERVAL="60000" \
your-image:latest
Verify these values:
<region>: Your SigNoz Cloud region<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: A descriptive name for your service (e.g.,payment-service).OTEL_METRICS_EXPORTER: Set tootlpto export metrics to SigNoz.OTEL_METRIC_EXPORT_INTERVAL: How often to export metrics in milliseconds (default: 60000 for 60 seconds).
Step 1. Set environment variables (PowerShell)
$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://ingest.<region>.signoz.cloud:443"
$env:OTEL_EXPORTER_OTLP_HEADERS = "signoz-ingestion-key=<your-ingestion-key>"
$env:OTEL_SERVICE_NAME = "<service-name>"
$env:OTEL_METRICS_EXPORTER = "otlp"
$env:OTEL_TRACES_EXPORTER = "none"
$env:OTEL_LOGS_EXPORTER = "none"
$env:OTEL_METRIC_EXPORT_INTERVAL = "60000"
Verify these values:
<region>: Your SigNoz Cloud region.<your-ingestion-key>: Your SigNoz ingestion key.<service-name>: A descriptive name for your service.OTEL_METRICS_EXPORTER: Set tootlpto export metrics to SigNoz.OTEL_METRIC_EXPORT_INTERVAL: How often to export metrics in milliseconds (default: 60000 for 60 seconds).
Step 2. Install OpenTelemetry packages
Add the following gems to your Gemfile:
gem 'opentelemetry-sdk'
gem 'opentelemetry-metrics-api'
gem 'opentelemetry-metrics-sdk'
gem 'opentelemetry-exporter-otlp-metrics'
Run bundle install:
bundle install
Step 3. Configure OpenTelemetry SDK
Create an initializer file to configure the SDK:
require 'opentelemetry/sdk'
require 'opentelemetry-metrics-sdk'
require 'opentelemetry/exporter/otlp_metrics'
# Configure the SDK
OpenTelemetry::SDK.configure do |c|
c.service_name = ENV['OTEL_SERVICE_NAME'] || 'my-ruby-service'
end
With OTEL_METRICS_EXPORTER=otlp, the SDK uses the OTLP metrics exporter configured by OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS. Metrics are exported periodically based on OTEL_METRIC_EXPORT_INTERVAL.
Step 4. Add custom metrics
Add custom metrics to your application using OpenTelemetry.meter_provider.meter:
require_relative 'config/initializers/opentelemetry'
# Get a meter - MeterProvider is configured by the initializer
METER = OpenTelemetry.meter_provider.meter('my.meter.name')
# Create a counter instrument
REQUEST_COUNTER = METER.create_counter(
'requests.total',
unit: '1',
description: 'Total number of requests'
)
def handle_request(endpoint)
# Record the metric with attributes
REQUEST_COUNTER.add(1, attributes: { 'endpoint' => endpoint })
# ... handle the request ...
end
This example shows a Counter, which only increases. OpenTelemetry supports other metric types like UpDownCounter, Histogram, and Observable Gauge. See Custom Metrics Examples for complete examples of each type.
Step 5. Run your application
Run your application normally:
ruby app.rb
For Rails applications:
rails server
Before your application exits, ensure any remaining metrics are flushed by shutting down the meter provider:
at_exit do
OpenTelemetry.meter_provider.shutdown
end
Validate
Once you have configured your application to start sending metrics to SigNoz, you can start visualizing the metrics in the metrics explorer.
Want more metrics? For system-level metrics like CPU, memory, and thread counts, see Runtime Metrics.
Custom Metrics Examples
OpenTelemetry supports six metric instrument types. Here's a complete example demonstrating all of them:
Synchronous Instruments
Synchronous instruments are called inline with your application code.
Counter - A value that only increases (e.g., total requests, bytes sent):
counter = meter.create_counter(
'requests.total',
unit: '1',
description: 'Total number of requests'
)
# Record a value
counter.add(1, attributes: { 'endpoint' => '/api/users' })
UpDownCounter - A value that can increase or decrease (e.g., active connections, queue size):
updown_counter = meter.create_up_down_counter(
'connections.active',
unit: '1',
description: 'Number of active connections'
)
# Increment
updown_counter.add(1)
# Decrement
updown_counter.add(-1)
Histogram - A distribution of values (e.g., request latency, response sizes):
histogram = meter.create_histogram(
'request.duration',
unit: 'ms',
description: 'Request duration in milliseconds'
)
# Record a value
histogram.record(45.5, attributes: { 'endpoint' => '/api/users' })
Asynchronous Instruments
Asynchronous instruments use callback procs that are invoked during metric collection. The callback must return a numeric value that will be recorded.
In some other OpenTelemetry SDKs, observable callbacks receive an observer object and call observer.observe(value, attributes) to record values. Ruby's SDK uses a simpler model: the callback proc returns a numeric value directly, and the SDK records it automatically.
ObservableCounter - Observes a monotonically increasing value (e.g., CPU time, page faults):
cpu_callback = proc do
# Return the CPU time in seconds
Process.times.utime + Process.times.stime
end
cpu_counter = meter.create_observable_counter(
'system.cpu.time',
callback: cpu_callback,
unit: 's',
description: 'CPU time'
)
ObservableUpDownCounter - Observes a value that can go up or down (e.g., memory usage, thread count):
thread_callback = proc do
# Return the current thread count
Thread.list.count
end
meter.create_observable_up_down_counter(
'process.threads.count',
callback: thread_callback,
unit: '1',
description: 'Current thread count'
)
ObservableGauge - Observes a point-in-time value (e.g., temperature, current memory usage):
require 'get_process_mem'
process_mem = GetProcessMem.new
memory_callback = proc do
# Return resident set size in bytes (cross-platform, no shell command)
process_mem.bytes.to_i
end
meter.create_observable_gauge(
'process.memory.usage',
callback: memory_callback,
unit: 'By',
description: 'Current memory usage in bytes'
)
Runtime Metrics
Ruby does not have a built-in runtime metrics package like Python or Node.js. However, you can collect system and process metrics using observable instruments.
Create a Runtime Metrics Helper
Install an additional gem for cross-platform process memory:
gem 'get_process_mem'
Create a helper module to collect common runtime metrics:
require 'get_process_mem'
module RuntimeMetrics
def self.setup(meter)
# Memory usage (returns bytes)
process_mem = GetProcessMem.new
memory_callback = proc do
process_mem.bytes.to_i
end
meter.create_observable_gauge(
'process.memory.usage',
callback: memory_callback,
unit: 'By',
description: 'Process memory usage in bytes'
)
# Thread count
thread_callback = proc { Thread.list.count }
meter.create_observable_up_down_counter(
'process.threads.count',
callback: thread_callback,
unit: '1',
description: 'Number of threads in the process'
)
# CPU time (user mode)
cpu_user_callback = proc { Process.times.utime }
meter.create_observable_counter(
'process.cpu.time.user',
callback: cpu_user_callback,
unit: 's',
description: 'Process CPU time in user mode'
)
# CPU time (system mode)
cpu_system_callback = proc { Process.times.stime }
meter.create_observable_counter(
'process.cpu.time.system',
callback: cpu_system_callback,
unit: 's',
description: 'Process CPU time in system mode'
)
# GC stats (Ruby specific)
gc_count_callback = proc { GC.count }
meter.create_observable_counter(
'ruby.gc.count',
callback: gc_count_callback,
unit: '1',
description: 'Number of garbage collections'
)
heap_callback = proc { GC.stat[:heap_live_slots] }
meter.create_observable_gauge(
'ruby.gc.heap_live_slots',
callback: heap_callback,
unit: '1',
description: 'Number of live objects in the heap'
)
end
end
Usage
Enable runtime metrics in your initializer:
require 'opentelemetry/sdk'
require 'opentelemetry-metrics-sdk'
require 'opentelemetry/exporter/otlp_metrics'
require_relative '../../lib/runtime_metrics'
OpenTelemetry::SDK.configure do |c|
c.service_name = ENV['OTEL_SERVICE_NAME'] || 'my-ruby-service'
end
# Enable runtime metrics
meter = OpenTelemetry.meter_provider.meter('runtime')
RuntimeMetrics.setup(meter)
Exported Metrics
The runtime metrics helper exports the following metrics:
Process Metrics:
process.memory.usage- Process memory usage in bytes (gauge)process.threads.count- Number of threads in the process (up-down counter)process.cpu.time.user- Process CPU time in user mode (counter)process.cpu.time.system- Process CPU time in system mode (counter)
Ruby Runtime Metrics:
ruby.gc.count- Total number of garbage collections (counter)ruby.gc.heap_live_slots- Number of live objects in the heap (gauge)
Setup OpenTelemetry Collector (Optional)
What is the OpenTelemetry Collector?
Think of the OTel Collector as a middleman between your app and SigNoz. Instead of your application sending data directly to SigNoz, it sends everything to the Collector first, which then forwards it along.
Why use it?
- Cleaning up data — Filter out noisy metrics you don't care about, or remove sensitive info before it leaves your servers.
- Keeping your app lightweight — Let the Collector handle batching, retries, and compression instead of your application code.
- Adding context automatically — The Collector can tag your data with useful info like which Kubernetes pod or cloud region it came from.
- Future flexibility — Want to send data to multiple backends later? The Collector makes that easy without changing your app.
See Switch from direct export to Collector for step-by-step instructions to convert your setup.
For more details, see Why use the OpenTelemetry Collector? and the Collector configuration guide.
Troubleshooting
Metrics not appearing?
Check Environment Variables: Ensure
OTEL_EXPORTER_OTLP_ENDPOINTis set to the base URL only:https://ingest.<region>.signoz.cloud:443- Do not append
/v1/metrics. The Ruby OTLP metrics exporter adds this path automatically.
Check Exporter Protocol: The Ruby OTLP metrics exporter uses HTTP by default (port 4318). Ensure your endpoint matches.
Verify Metrics are Exported: The SDK uses a
PeriodicMetricReaderthat exports at the interval set byOTEL_METRIC_EXPORT_INTERVAL. Ensure you callshutdownbefore your application exits to flush remaining metrics:OpenTelemetry.meter_provider.shutdownEnable Debug Logging: Check for errors in your application logs. You can enable verbose output:
OpenTelemetry.logger = Logger.new(STDOUT) OpenTelemetry.logger.level = Logger::DEBUGVerify Package Installation: Ensure all required gems are installed:
bundle list | grep opentelemetry
Authentication errors
If you see errors like "Unauthorized" or "403 Forbidden":
- Verify your ingestion key is correct in
OTEL_EXPORTER_OTLP_HEADERS - Ensure the header format is exactly:
signoz-ingestion-key=<your-key>(no extra spaces) - Check that your ingestion key is active in the SigNoz Cloud dashboard
"Connection Refused" errors
- If running locally and sending to SigNoz Cloud, check your internet connection and firewall.
- If sending to a self-hosted collector, ensure the collector is running and listening on port 4317 (gRPC) or 4318 (HTTP).
SSL/TLS errors
If you encounter SSL certificate errors:
- Ensure you're using
https://in the endpoint URL - For self-hosted setups with self-signed certificates, you may need to configure the exporter to trust your CA
Metrics not exported on application exit
Ruby applications may exit before the periodic exporter has a chance to send metrics. Add a shutdown hook:
at_exit do
OpenTelemetry.meter_provider.shutdown
end
Next Steps
- Create Dashboards to visualize your metrics.
- Set up Alerts on your metrics.
- Instrument your Ruby application with traces for complete observability.