We use cookies or similar technologies to personalize your online experience & tailor marketing to you. Many of our product features require cookies to function properly.

Read our privacy policy I accept cookies from this site

Beeline for Ruby

The Ruby Beeline for Honeycomb is quick and easy way to instrument your Ruby application. It includes several optional wrappers that automatically instrument HTTP requests and database queries.

It provides automatic tracing out of the box, linking database queries to the HTTP request from which they originated. While this is a great way to get general insights about your app as quickly as possible, as you forge ahead on your observability journey you may find you’d like to add new events or traces to add more details specific to your app. The Ruby Beeline provides simple interfaces for adding both.

To see an example of the Ruby Beeline in action, try out the examples.

If you’d like to see more options in the Ruby Beeline, please file an issue or vote up one already filed! You can also contact us at support@honeycomb.io.

Requirements  🔗

You can find your API key on your Team Settings page. If you don’t have a API key yet, sign up for a Honeycomb trial.

Quick installation  🔗

To install the Ruby Beeline for your application:

  1. Add honeycomb-beeline to your Gemfile:

    gem 'honeycomb-beeline'
  2. If you have a rails application, you can run the generator to create your configuration file:

    bundle exec rails generate honeycomb YOUR_API_KEY --dataset rails

    Otherwise in your code, initialize the beeline with your API key and dataset name:

    Honeycomb.configure do |config|
      config.write_key = 'YOUR_API_KEY'
      config.dataset = 'MyRubyApp'
  3. Augment the data with interesting information from your app and extra context such as errors so that you can see rich information about your app in Honeycomb.

    Honeycomb.add_field('interesting_thing', 'banana')
  4. Add additional spans and turn a series of events into a trace:

    Honeycomb.start_span(name: 'slow_operation') do |span|
      span.add_field('interesting_thing', 'banana')

Initializing for use with proxies  🔗

To use a proxy, you will need to create a custom libhoney client and provide it when configuring the Beeline:

Honeycomb.configure do |config|
  config.client = Libhoney::Client.new(
    writekey: 'YOUR_API_KEY',
    dataset: 'ruby',
    proxy_config: ['myproxy.example.com', 8080])

# ... or if you need to authenticate to your proxy

Honeycomb.configure do |config|
  config.client = Libhoney::Client.new(
    writekey: 'YOUR_API_KEY',
    dataset: 'ruby',
    proxy_config: ['myproxy.example.com', 8080, 'username', 'password'])

Note: Proxy support was added in libhoney version 1.14.0.

Adding context to events  🔗

The Ruby Beeline tracks the current span within the overall trace. This allows you to add as many additional custom fields as you like.

Here is an example of adding a custom field to the current span:

def calculate_big_number
  # ... do work, find a big number
  Honeycomb.add_field('big_num', result)
  # ... do thing with big_num, maybe something fails
  Honeycomb.add_field('error', err)

Additional fields are added under the app. namespace. For example, the field above would appear in your event as app.big_num. The namespace groups your fields together to make them easy to find and examine.

If you prefer to avoid the app. namespace, for example in order to overwrite an automatically-populated field, you can have your block take the current span as a paramter and call add_field directly on that instance.

def calculate_another_big_number
  Honeycomb.start_span(name: 'big_number_calculation') do |span|
    # ... do work, find another big_num
    span.add_field('handler.name', 'calc_other_big_num')

These additional fields are your opportunity to add important and detailed context to your instrumentation. Put a timer around a section of code, add per- user information, include details about what it took to craft a response, and so on. It is expected that some fields will only be present on some requests. Error handlers are a great example of this; they will obviously only exist when an error has occurred.

It is common practice to add in these fields along the way as they are processed in different levels of middleware. For example, if you have an authentication middleware, it would add a field with the authenticated user’s ID and name as soon as it resolves them. Later on in the call stack, you might add additional fields describing what the user is trying to achieve with this specific HTTP request.

If you’re interested in adding custom fields to all spans, use add_field_to_trace instead.

add_field_to_trace adds the field to both the currently active span and all other spans that have yet to be sent and are involved in this trace that occur within this process. Additionally, these fields are packaged up and passed along to downstream processes if they are also using a beeline. This function is good for adding context that is better scoped to the request than this specific unit of work, e.g. user IDs, globally relevant feature flags, errors, etc. Fields added here are also prefixed with app.

Adding spans to a trace  🔗

We encourage people to think about instrumentation in terms of “units of work”. As your program grows, what constitutes a unit of work is likely to be portions of your overall service rather than an entire run. Spans are a way of breaking up a single external action (say, an HTTP request) into several smaller units in order to gain more insight into your service. Together, many spans make a trace, which you can visualize traces within the Honeycomb query builder.

Adding spans with the Ruby Beeline is easy! Here is an example, where calls to slow_operation get their own span within the trace:

def slow_operation
  Honeycomb.start_span(name: 'slow_operation') do |span|
    # ... go on and do the slow opp, add more data along the way
    span.add_field('interesting_thing', 'banana')

Spans always get a few fields:

You are always welcome (and encouraged!) to add additional fields to spans using the Honeycomb.add_field method.

Other integrations  🔗

The Ruby Beeline integrates with other common ruby gems in order to populate spans and traces with useful information.

Active Support  🔗

The Ruby Beeline can listen to any Active Support instrumentation calls in your application and produce spans from each event. You can listen to events by providing a list when configuring the Beeline.

Honeycomb.configure do |config|
  config.write_key = 'YOUR_API_KEY'
  config.dataset = 'ruby'
  config.notification_events = %w[

AWS  🔗

The ruby beeline will automatically instrument any calls made using v2 or v3 of the aws-sdk gem and create a span for each call.

Faraday  🔗

The Ruby Beeline will attempt to propagate outgoing HTTP requests with a trace propagation header. This allows other services that are using a Beeline to produce a distributed trace.

Rack  🔗

The Ruby Beeline provides a piece of rack middleware that can be used in your rack applications. This will create a trace and root span for each request and continue any distributed traces with trace ids included from other Beelines in the header.

app = Rack::Builder.new do |builder|
  builder.use Honeycomb::Rack::Middleware, client: Honeycomb.client
  builder.run RackApp.new

Rails  🔗

The Ruby Beeline provides a rails generator for easy configuration. It will generate a configuration file with the provided API key, some basic active support events configured and a presend_hook to remove potential PII from the configured notification_events. Rails provides many more active support notification events that you can add to your instrumentation. See the complete list of events provided by rails here.

bundle exec rails generate honeycomb YOUR_API_KEY --dataset rails
Honeycomb.configure do |config|
  config.write_key = 'YOUR_API_KEY'
  config.dataset = "rails"
  config.presend_hook do |fields|
    if fields["name"] == "redis" && fields.has_key?("redis.command")
      # remove potential PII from the redis command
      if fields["redis.command"].respond_to? :split
        fields["redis.command"] = fields["redis.command"].split.first
    if fields["name"] == "sql.active_record"
      # remove potential PII from the active record events
  config.notification_events = %w[

Rake  🔗

The Ruby Beeline will automatically instrument your rake tasks and create a trace and root span for each rake task.

Sequel  🔗

The Ruby Beeline will automatically instrument your sequel database calls and create a span for each call.

Sinatra  🔗

The Ruby Beeline enhances the provided rack middleware to collect extra information for sinatra application.

use Honeycomb::Sinatra::Middleware, client: Honeycomb.client

get '/' do
  'Hello from Honeycomb'

Augmenting or Scrubbing Spans  🔗

If you have some transformations you would like to make on every span before it leaves the process for Honeycomb, the presend_hook is your opportunity to make these changes. Examples are scrubbing sensitive data (eg you may want to ensure specific fields are dropped or hashed) or augmenting data (eg making out-of-band translations between an ID and a more human readable version). Similar to the sample_hook discussed below, you pass the presend_hook a block that will be called on every span with the fields of that span as an argument. The function is free to mutate the hash passed in and those mutations will be what finally gets sent to Honeycomb.

As an example, say we have some HTTP requests that provide sensitive values which have been added to a span. This code will examine all spans just before they’re sent to Honeycomb and replace the sensitive values with “[REDACTED]”.

Honeycomb.configure do |config|
  config.presend_hook do |fields|
    fields['credit_card_number'] = '[REDACTED]'

Sampling events  🔗

To sample a portion of events for very high throughput services, include a custom client with a sample_rate in the initialization of the Ruby Beeline. This sends 1/n of all events, so a sample rate of 5 would send 20% of your events. Try starting with a value of 10:

Honeycomb.configure do |config|
  config.client = Libhoney::Client.new(
    writekey: 'YOUR_API_KEY',
    dataset: 'ruby',
    sample_rate: 5

Sampling is performed by default on a per-trace level in the Ruby Beeline, so adding sampling won’t break your traces. Either all spans in a trace will be sent, or no spans in the trace will be sent.

The Honeycomb engine reweights query results to ensure that sampled computations return correct results.

Customizing sampling logic  🔗

Our Beeline lets you define a sample_hook in order to customize the logic used for deterministic per-trace sampling.

For example, assume you have instrumented an HTTP server. You’d like to keep all errored requests and heavily sample healthy traffic (200 response codes). Also, you don’t really care about 302 redirects in your app, so want to drop those. You could define a sampler function like so:

class CustomSampler
  extend Honeycomb::DeterministicSampler

  def self.sample(fields)
    # capture everything by default
    sample_rate = 1

    case fields['app.response_code']
    when 302
      return [false, 0]
    when 200
      # only keep 1 out of every 100 traces for successful requests
      sample_rate = 100

    if should_sample(sample_rate, fields['trace.trace_id'])
      return [true, sample_rate]

    return [false, 0]

Honeycomb.configure do |config|
  config.sample_hook do |fields|

Note: Defining a sampling hook overrides the deterministic sampling behavior for trace IDs. Unless you take trace.trace_id into account (as we did above by extending the DeterministicSampler), you will get incomplete traces.

Troubleshooting the Beeline  🔗

There are three general approaches to finding out what’s wrong when the Ruby Beeline isn’t doing what you expect.

Enable Debug Mode  🔗

Add the debug line in the config block.

Honeycomb.configure do |config|
  # any other configuration...
  config.debug = true

Use a LogClient  🔗

By using a Libhoney LogClient when configuring the Ruby Beeline you can send the events to STDOUT.

Honeycomb.configure do |config|
  config.client = Libhoney::LogClient.new

Using ENV variables to control framework integrations  🔗

If you are having issues with a specific integration with Rails, Faraday, Sequel etc., use the following ENV variables to determine which integration may be causing the issue or to disable it entirely.

Use HONEYCOMB_DISABLE_AUTOCONFIGURE=true to stop the Beeline from requiring any of the Beeline integrations. This will still allow you to use the Beeline but without any of the automatic instrumentation.

You can also use HONEYCOMB_INTEGRATIONS=rails,faraday using a comma-separated list to only load specific integrations. This will allow you to be more selective about which automatic instrumentation integration you want to use. The available integration list is: active_support, aws, faraday, rack, rails, railtie, rake, sequel, sinatra.

Customizing middleware location in a Rails application  🔗

The Rails framework integration will automatically insert the Rack middleware before the Rails::Rack::Logger middleware. To insert this in some other location, disable the railtie integration as defined above using the ENV variable HONEYCOMB_INTEGRATIONS. You will then need to insert the Honeycomb::Rails::Middleware into your middleware stack manually.

Example event  🔗

Below is a sample event from the Ruby Beeline. This example is an http_server event, generated when your app handles an incoming HTTP request.

  "meta.beeline_version": "1.3.0",
  "meta.local_hostname": "myhost",
  "service_name": "my-test-app",
  "meta.package": "rack",
  "meta.package_version": "1.3",
  "name": "http_server",
  "request.route": "GET /dashboard",
  "request.method": "GET",
  "request.path": "/dashboard",
  "request.protocol": "https",
  "request.http_version": "HTTP/1.1",
  "request.host": "my-test-app.example.com",
  "request.remote_addr": "",
  "request.header.user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36",
  "trace.trace_id": "b694512a-833f-4b35-be5f-6c742ba18e12",
  "trace.span_id": "c35cc326-ed90-4881-a4a8-68526d252f2e",
  "response.status_code": 200,
  "duration_ms": 303.057396

Queries to try  🔗

Here are some examples to get you started querying your app’s behavior:

Which of my app’s routes are the slowest?  🔗

Where is my app spending the most time?  🔗

Which users are using the endpoint that I’d like to deprecate? (Using a custom field user.email 🔗

Contributions  🔗

Features, bug fixes and other changes to Beelines are gladly accepted. Please open issues or a pull request with your change via GitHub. Remember to add your name to the CONTRIBUTORS file!

All contributions will be released under the Apache License 2.0.