Ruby

Send Ruby logs to Timber

Timber integrates with Ruby through its timber Ruby gem, enabling you to send Ruby logs to your Timber account

Features

Example

To quickly demonstrate the power of Timber, the following example logs a simple structured log with context:

Timber.with_context(user: {id: "abcd1234"}) do
logger.info(
"Order #1234 placed, total: $520.23",
order_placed: {id: 1234, total: 520.23}
)
end

This produces the following JSON log line:

{
"dt": "2019-03-04T03:44:21.221232Z",
"level": "info",
"message": "Order #1234 placed, total: $520.23",
"order_placed": {
"id": 1234,
"total": 520.23
},
"context": {
"runtime": {
"thread_id": "70179740727560"
},
"system": {
"pid": 20643,
"process_name": "python"
},
"user": {
"id": 1234
}
}
}

Continue to learn more about setting context, automatic context, structured logging, and more.

Installation

If you're unsure, we recommend installing via the "HTTP" method. To understand why you would choose one over the other, please see the "Sending Logs To Timber" guide.

Rails (HTTP)
Rails (STDOUT)
Non-Rails (HTTP)
Non-Rails (STDOUT)

Send logs directly from within your app over HTTP:

  1. In your Gemfile, add the timber gems:

    Gemfile
    gem 'timber', '~> 3.0'
    gem 'timber-rails', '~> 1.0' # optional
  2. In your shell run bundle install

  3. Add the config/initializers/timber.rb file, replace YOUR_API_KEY and YOUR_SOURCE_ID accordingly:

    config/initializers/timber.rb
    # Install the Timber.io logger
    # Documentation: https://docs.timber.io/setup/languages/ruby
    if Rails.env.production?
    http_device = Timber::LogDevices::HTTP.new("YOUR_API_KEY", "YOUR_SOURCE_ID")
    Rails.logger = Timber::Logger.new(http_device)
    else
    Rails.logger = Timber::Logger.new(STDOUT)
    end

Test The Pipes

Verify installation by testing the pipes. Start by entering your rails console:

bundle exec rails console

Then run the following code:

http_device = Timber::LogDevices::HTTP.new("YOUR_API_KEY", "YOUR_SOURCE_ID")
logger = Timber::Logger.new(http_device)
Rails.logger.info("Order #1234 placed", order_placed: {id: 1234, total: 100.54})
http_device.verify_delivery!

Write logs to STDOUT and ship them external from your app:

This method is more advanced and requires a separate step to ship logs to Timber. Basic knowledge of STDOUT and log management is required. If you are unsure, please use the "HTTP" method. For more information on the advantages of this method please see this guide.

  1. In your Gemfile, add the timber gems:

    Gemfile
    gem 'timber', '~> 3.0'
    gem 'timber-rack', '~> 1.0' # optional
    gem 'timber-rails', '~> 1.0' # optional
  2. In your shell run bundle install

  3. Add the config/initializers/timber.rb file with the following content, replace YOUR_API_KEY and YOUR_SOURCE_ID accordingly:

    config/initializers/timber.rb
    # Install the Timber.io logger
    # Documentation: https://docs.timber.io/setup/languages/ruby
    Rails.logger = Timber::Logger.new(STDOUT)
  4. At this point your application is writing logs to STDOUT in JSON format. Please choose the appropriate platform, log forwarder, or operating system.

Send logs directly from within your app over HTTP:

  1. In your Gemfile, add the timber gems:

    Gemfile
    gem 'timber', '~> 3.0'
    # Optionally install integrations
    gem 'timber-rack', '~> 1.0' # See "Integrations"
  2. In your shell run bundle install

  3. Instantiate the Timber logger for global use, replace YOUR_API_KEY and YOUR_SOURCE_ID accordingly:

    # Install the Timber.io logger
    # Documentation: https://docs.timber.io/setup/languages/ruby
    http_device = Timber::LogDevices::HTTP.new("YOUR_API_KEY", "YOUR_SOURCE_ID")
    LOGGER = Timber::Logger.new(http_device)
  4. Verify installation by testing the pipes:

    bundle console

    Then run the following code:

    http_device = Timber::LogDevices::HTTP.new("YOUR_API_KEY", "YOUR_SOURCE_ID")
    logger = Timber::Logger.new(http_device)
    logger.info("Order #1234 placed", order_placed: {id: 1234, total: 100.54})
    http_device.verify_delivery!

Write logs to STDOUT and ship them external from your app:

This method is more advanced and requires a separate step to ship logs to Timber. Basic knowledge of STDOUT and log management is required. If you are unsure, please use the "HTTP" method. For more information on the advantages of this method please see this guide.

  1. In your Gemfile, add the timber gems:

    Gemfile
    gem 'timber', '~> 3.0'
    gem 'timber-rack', '~> 1.0' # optional
  2. In your shell run bundle install

  3. Instantiate the Timber logger for global use:

    # Install the Timber.io logger
    # Documentation: https://docs.timber.io/setup/languages/ruby
    LOGGER = Timber::Logger.new(STDOUT)
  4. At this point your application is writing logs to STDOUT in JSON format. Please choose the appropriate platform, log forwarder, or operating system.

Configuration

Options

All configuration options are available in the Timber::Config module documentation.

Usage

Basic Logging

Log standard log messages just like you would with the Ruby ::Logger:

logger.info("Hello world")

Structured Logging

If you haven't already, please see our structured logging best practices guide.

Your Logger calls now take a second argument where you can pass structured data. We recommend logging data with a root level namespace as shown below to avoid type conflicts with other events. You can read more on this in our Event Naming guide.

logger.info(
"Order #1234 placed",
order_placed: {id: 1234, total: 100.54}
)

Setting Context

Before capturing context, please make sure Timber does not already capture the context you want (see the Automatic Context section).

Timber.with_context(organization: {id: "abcd1234"}) do
logger.info(
"Subscription #1234 upgraded to $100",
subscription_upgraded: {id: 1234, total: 100.0}
)
end

Guides

Tying Logs To Users

A very common use case for context is tying logs to users. This allows you to segment and filter you logs by user ID, email, name and so on. This is incredibly helpful with customer support, helping you to narrow down specific events for a user reported issue.

Rails
Rack
Other

Simply install the timber-rails integration. User context is automatically captured via a Rack middleware that is installed automatically for you. You can read more about that in the Rack user context section.

Simply install the timber-rack integration. User context is captured via the provided UserContext Rack middleware. You can read more about that in the Rack user context section.

For non-Rails/Rack environments, you can capture user context in the same way you capture any other context, by setting it as soon as you have that context:

Timber.with_context(user: {id: "abcd1234", email: "paul@bunyan.com"}) do
# code here
end

Tying Logs To HTTP Requests

Rails
Rack
Non-Rails

Simply install the timber-rails integration. HTTP context is automatically captured via a Rack middleware that is installed automatically for you. You can read more about that in the Rack HTTP context section.

Simply install the timber-rack integration. HTTP context is captured via the provided HTTPContext Rack middleware. You can read more about that in the Rack HTTP context section.

For non-Rails/Rack environments, you can capture HTTP context in the same way you capture any other context, by setting it as soon as you have that context:

Timber.with_context(http: {method: "GET", path: "/path"}) do
# code here
end

Timing Code Execution

A common piece of data to include in logs is code execution timings. You can do this like so:

timer = Timber::Timer.start
# ... code to time ...
logger.info(
"Processed background job",
background_job: {duration_ms: timer}
)

Setting Context In A Controller

Rails' ActionController offers an around_action callback that you can use to set context on a per-request basis:

class ApplicationController < ActionController::Base
around_action :set_context
private
def set_context
Timber.with_context(organization: {id: 1234}) do
yield
end
end
end

Log to an additional IO device

If you are choosing to write logs to STDOUT or a file we highly recommend following the "STDOUT" installation instructions and integrating Timber with your platform or operating system.

STDOUT

Simply pass STDOUT as the second argument to Timber::Logger.new:

http_device = Timber::LogDevices::HTTP.new("YOUR_API_KEY", "YOUR_SOURCE_ID")
logger = Timber::Logger.new(http_device, STDOUT)

File

Simply pass a file logger as the second argument to Timber::Logger.new :

http_device = Timber::LogDevices::HTTP.new("YOUR_API_KEY", "YOUR_SOURCE_ID")
file_logger = Logger.new("#{Rails.root}/log/#{Rails.env}.log")
logger = Timber::Logger.new(http_device, file_logger)

Automatic Context

timber automatically captures context to enrich your logs.

system

The system context captures system level information such as hostname and pid:

{
"context": {
"system": {
"hostname": "ec2-44-125-241-8",
"pid": 20643
}
}
}

Field

Description

context.system.hostname

System level hostname, value of Socket.gethostname

context.system.pid

System level process ID, value of Process.pid

runtime

The runtime context captures information about the origin of the log line:

{
"context": {
"runtime": {
"thread_id": "70179740727560"
}
}
}

Field

Description

context.runtime.thread_id

The result of Thread.current.object_id, helping you to segment logs by thread.

Integrations

Timber integrates with 3rd party libraries a la carte style, allowing you to pick and choose the integrations you want. Integrations with timber are entirely optional and serve to upgrade your logs with rich context and metadata if you choose to use them:

Performance

Extreme care was taken into the design of timber to be fast and reliable:

  1. Log data is buffered and flushed on an interval to optimize performance and delivery.

  2. The timber HTTP backend uses a controlled multi-buffer design to efficiently ship data to the Timber service.

  3. Connections are re-used and rotated to ensure efficient delivery of log data.

  4. Delivery failures are retried with an exponential backoff, maximizing successful delivery.

  5. Msgpack is used for payload encoding for it's superior performance and memory management.

  6. The Timber service ingest endpoint is a HA service designed to handle extreme fluctuations of volume, it responds in under 50ms to reduce back pressure.

FAQs

Will installing Timber affect my application's performance?

No. Timber was designed with a keen focus on performance. It leans heavily on a light-weight asynchronous log processing design. Timber is also battle tested on hundreds of Ruby apps since it launched multiple years ago.

Can Timber crash my app?

No, Timber is wrapped in a try / catch block to ensure exceptions do not bubble up to your app.

Troubleshooting

To begin, please see our log delivery troubleshooting guide. This covers the most common issues we see with log delivery:

If the above troubleshooting guide does not resolve your issue then we recommend enabling debug logging within the timber library itself by adding this to your config/initializers/timber.rb file:

config/initializers/timber.rb
Timber::Config.instance.debug_to_stdout!()

This will print internal debug messages to STDOUT. Alternatively, you can debug to a file instead:

config/initializers/timber.rb
Timber::Config.instance.debug_to_file!("/var/log/timber.log")