In this tutorial, we will go through a working example of a Ruby application auto-instrumented with OpenTelemetry. To keep things simple, we will create a basic “Hello World” application, instrument it with OpenTelemetry’s Ruby client library to generate trace data and send it to an OpenTelemetry Collector. The Collector will then export the trace data to an external distributed tracing analytics tool of our choice.
If you are new to the OpenTelemetry open source project or if you are looking for more application instrumentation options, check out this guide.
Our Example Application
Our example application is a local Sinatra server that responds with
“Hello World!“ every time we access it. Let’s first create a dedicated directory for this application.
mkdir sinatra-hello-world-otel cd sinatra-hello-world-otel
Now we can create a Gemfile:
Next we’ll set the following configuration:
source "http://rubygems.org" gem 'rake' gem 'thin' gem 'sinatra'
Our next step is creating the Server file…
…with the following code:
require 'rubygems' require 'bundler/setup' Bundler.require get '/' do 'Hello world!' end
We now have a good and simple example application that we can start by running ruby server.rb. The server will run at
We’ll get back to our Server and Gemfile files in the instrumentation steps below.
Installing the Ruby OpenTelemetry Client Gems
Now that we have our example application up and running, it’s time to proceed to installing the Ruby OpenTelemetry client library. First of all, we will need to install the following three gems:
To install the client gems, we will need to run the following command from our application directory –
gem install opentelemetry-sdk gem install opentelemetry-exporter-otlp gem install opentelemetry-instrumentation-all
These gems will install all necessary libraries to instrument the code and export traces using OpenTelemetry Protocol (OTLP).
Note: For the sake of simplicity, we used the
opentelemetry-instrumentation-all library, which includes instrumentation packages for all Ruby libraries, including Sinatra HTTP server. Of course, if you want to keep things as light as possible, you can selectively install only those packages that are applicable to your application, such as
Defining the Instrumentation in Our Application
After installing the gems, we need to add them to our Gemfile configuration as follows:
gem "opentelemetry-api" gem "opentelemetry-sdk" gem "opentelemetry-exporter-otlp" gem 'opentelemetry-instrumentation-sinatra'
Our next step is enabling the instrumentation in our Server file by adding the following code:
OpenTelemetry::SDK.configure do |c| c.service_name = 'ruby-otlp' c.use_all end
And the following requirements:
require 'opentelemetry/sdk' require 'opentelemetry/exporter/otlp' require 'rubygems' require 'bundler/setup'
This is what our Gemfile and Server files look like now:
source "https://rubygems.org" gem "opentelemetry-api" gem "opentelemetry-sdk" gem "opentelemetry-exporter-otlp" gem 'opentelemetry-instrumentation-sinatra' gem "sinatra" gem 'thin'
require 'opentelemetry/sdk' require 'opentelemetry/exporter/otlp' require 'rubygems' require 'bundler/setup' Bundler.require OpenTelemetry::SDK.configure do |c| c.service_name = 'ruby-otlp' c.use_all end get '/' do 'Hello world!' end
That’s it for instrumenting our app! There’s no additional coding needed to generate the individual spans (the trace building blocks) in each respective part of our application. That’s the beauty of auto-instrumentation. How is this magic done, you ask? The
c.use_all statement above states that the application will be using all instrumentation provided by OpenTelemetry and send all available traces from our application.
Now let’s complete the configuration and install the bundler:
After this step, we need to specify the endpoint that we will be using to export traces. In our example, this is going to be localhost with port 55681, which is the default port that OpenTelemetry uses to send traces in OTLP/HTTP format:
Downloading and Configuring the OpenTelemetry Collector
The last component that we will need, is the OpenTelemetry Collector, which we can download here. In our example, we will be using the otelcontribcol_darwin_amd64 flavor, but you can choose any other version of the collector from the list, as long as the collector is compatible with your operating system.
The data collection and export settings in the OpenTelemetry Collector are defined by a config file in YAML format. We will create this file in the same directory
(sinatra-hello-world-otel) as the collector file that we have just downloaded and call it
config.yaml. This file will have the following configuration:
receivers: otlp: protocols: grpc: http: exporters: logzio: account_token: "<<TRACING-SHIPPING-TOKEN>>" #region: "<<LOGZIO_ACCOUNT_REGION_CODE>>" - (Optional) processors: batch: extensions: pprof: endpoint: :1777 zpages: endpoint: :55679 health_check: service: extensions: [health_check, pprof, zpages] pipelines: traces: receivers: [otlp] processors: [batch] exporters: [logzio]
In this example we send the traces to Logz.io’s Distributed Tracing service, so we configure the collector with the Logz.io exporter which will send traces to the Logz.io account defined by the account token (if you don’t have an account you can get a free one here). However, you can also export the trace data from OpenTelemetry Collector to any other tracing backend by adding the required exporter configuration to this file (you can read more on exporters options here).
Running It All Together
Now that we have everything set up, let’s send some traces.
First, we need to start the OpenTelemetry Collector. We do this by specifying the path to the collector and the required config file. In our example, we run both files from our
sinatra-hello-world-otel directory as follows:
./otelcontribcol_darwin_amd64 --config ./config.yaml
The collector is now running and listening to incoming traces on port 55681.
Our next step is to start our application:
All that is left for us to do at this point is to visit http://127.0.0.1:4567 and refresh the page, triggering our app to generate and emit a trace of that transaction (repeat that a few times to have several sample traces to look at). The Collector will then pick up these traces and send them to the distributed tracing backend defined by the exporter in the collector config file.
Let’s check the Jaeger UI to make sure our traces arrived ok:
The traces are in Jaeger, ready for us to visualize and analyze them.
As you can see, OpenTelemetry makes it pretty simple to automatically instrument Ruby applications. All we had to do, was:
- Install relevant Ruby OpenTelemetry client gems
- Define the gems in our Gemfile and the application file
- Add code to enable auto-instrumentation in our application file
- Configure the exporter endpoint for our application
- Download the OpenTelemetry Collector and configure it to receive the trace data and send it to our tracing analytics tool
For more information on OpenTelemetry instrumentation, visit this guide. If you’re interested in manual instrumentation options for Ruby, check out the OpenTelemetry GitHub repository. If you are interested in trying this integration out using Logz.io backend, feel free to sign up for a free account and then use our documentation to set up auto-instrumentation for your own Ruby application.