OpenTelemetry Tracing API for Ruby

TIP

This document teaches how to use OpenTelemetry Ruby API. To learn how to install and configure OpenTelemetry Ruby SDK, see Getting started with OpenTelemetry Rubyopen in new window.

Installation

OpenTelemetry-Rubyopen in new window is the Ruby implementation of OpenTelemetry. It provides OpenTelemetry Tracing API which you can use to instrument your application with traces.

gem 'opentelemetry'

Quickstart

Step 1. Let's instrument the following function:

def create_user(name, email)
  User.create(name: name, email: email)
end

Step 2. Wrap the operation with a span:

require 'opentelemetry'

tracer = OpenTelemetry.tracer_provider.tracer('my_app_or_gem', '1.0.0')

def create_user(name, email)
  tracer.in_span('insert-user', kind: :server) do |span|
    User.create(name: name, email: email)
  end
end
 

 


 

 

Step 4. Record contextual information with attributes:

def create_user(name, email)
  tracer.in_span('insert-user', kind: :server) do |span|
    if span.recording?
      span.set_attribute('enduser.name', name)
      span.set_attribute('enduser.email', email)
    end

    User.create(name: name, email: email)
  end
end


 
 
 
 




And that's it! You don't have to manually record exceptions, because tracer.in_span does that automatically for you.

Tracer

To start creating spans, you need a tracer. You can create a tracer by providing the name and version of the library/application doing the instrumentation:

require 'opentelemetry'

tracer = OpenTelemetry.tracer_provider.tracer('my_app_or_gem', '1.0.0')

You can have as many tracers as you want, but usually you need only one tracer for an app or a library. Later, you can use tracer names to identify the instrumentation that produces the spans.

Creating spans

Once you have a tracer, creating spans is easy:

# Create a span with name "operation-name" and kind="server".
tracer.in_span('operation-name', kind: :server) do |span|
  do_some_work
end

Internally, in_span helper creates a span, resques and records any exceptions, sets the span status code, and finishes the span:

# Create a span.
span = tracer.start_span('operation-name', kind: :server)

# Activate the span in the current context.
OpenTelemetry::Trace.with_span(span) do |span|
  do_some_work
rescue Exception => e
  span.record_exception(e)
  span.status = OpenTelemetry::Trace::Status.error("Unhandled exception of type: #{e.class}")
  raise e
ensure
  span.finish
end

Adding span attributes

To record contextual information, you can annotate spans with attributes. For example, an HTTP endpoint may have such attributes as http.method = GET and http.route = /projects/:id.

# To avoid expensive computations, check that span is recording
# before setting any attributes.
if span.recording?
  span.set_attribute('http.method", 'GET')
  span.set_attribute('http.route", '/projects/:id')
end

You can name attributes as you want, but for common operations you should use semantic attributes convention.

Adding span events

You can annotate spans with events, for example, you can use events to record log messages:

span.add_event(
  name: 'log',
  attributes: {
    'log.severity' => 'error',
    'log.message' => 'User not found',
    'enduser.id' => '123'
  }
)

Setting status code

You can set error status code to indicate that the operation contains an error:

rescue Exception => e
  span.status = OpenTelemetry::Trace::Status.error(e.to_s)
end

Recording exceptions

OpenTelemetry provides a shortcut to record exceptions which is usually used together with status:

rescue Exception => e
  # Record the exception and update the span status.
  span.record_exception(e)
  span.status = OpenTelemetry::Trace::Status.error(e.to_s)
end

Please note that tracer.in_span automatically rescues and records exceptions for you.

Trace Context

OpenTelemetry stores the active span in a context and saves the context in a thread-local storage. You can nest contexts inside each other and OpenTelemetry will automatically activate the parent span context when you end the span.

tracer.in_span sets the active span for you, but you can also activate the span manually:

OpenTelemetry::Trace.with_span(main) do |span|
  do_some_work
end

To get the current span:

span = OpenTelemetry::Trace.current_span

What's next?

Last Updated: