Timber.io - Powerful Elixir Logging

ISC License Hex.pm Documentation CircleCI branch Coverage Status

Note: Timber is in alpha testing, if interested in joining, please visit http://timber.io

Timber is a different kind of logging platform; it goes beyond traditional logging by enriching your logs with application level metadata, turning them into rich, structured events without altering the essence of logging. See for yourself at timber.io.

Timber for Elixir works with the standard Elixir Logger system. No wrappers. No jury-rigging. No need to rewrite all your log statements. Just call Logger.info (or whichever level you prefer) and Logger will coordinate everything else with Timber. Even better, Timber will work alongside any other Elixir Logger backend you have installed.

When you want to get more advanced, Timber offers simple ways to record more detailed information about what’s going on in your application.

Installation

To get started, you’ll need to add Timber to your application dependencies:

def application do
  [applications: [:timber]]
end

def deps do
  [{:timber, "~> 0.4"}]
end

You will also need to declare Timber as an Elixir Logger backend in your application configuration. Typically, this is done in config/config.exs for your codebase:

config :logger, backends: [Timber.Logger]

Note that the backends key is defined as a list. Timber complies with the expectations of any other Logger backend and can run alongside other backends without interfering. This is helpful if you use our HTTP transport method but still want to use the default :console backend to see logs locally.

Transport Configuration

The last step to make the Timber library work is to tell it how you want to communicate with Timber’s servers. We call this the “transport strategy.” Currently, our only available strategy is to use stdout in combination with Heroku’s Logplex system. In the future we’ll also be offering HTTP transport strategies and more based on consumer demand.

Heroku Logplex

If you deploy to Heroku, it’s very simple to get started. You’ll need to declare in your application configuration that you want to use the IODevice transport. You can choose to do this only in production, leaving your development environment printing to the standard console. .

config :timber, :transport, Timber.Transports.IODevice

Separately, you’ll need to add your unique log drain URL to Heroku for your application:

heroku drains:add https://<timber-api-key>@api.timber.io/heroku/logplex_frames --app <app-name>

Make sure to replace <timber-api-key> with your Timber API key (found on your Timber dashboard) and <app-name> with the name of the application on Heroku.

You’re done!

If you decide to use Timber in your local development environment, you will want to turn on the colorize, print_timestamps, and print_log_level options so you can see your logs nicely in your terminal window. It will also help to set format to :logfmt:

# config/dev.exs

config :timber, :io_device,
  colorize: true,
  format: :logfmt,
  print_timestamps: true
  print_log_level: true

Events and Context

Timber is a beautiful way to view your logs, but we make logs even more helpful by allowing you to encode specific metadata alongside your log statements. We separate this metadata into events and context. Events are attached to single log statements and represent some specific action that has taken place, like receiving an HTTP request or completing a SQL query. Context, on the other hand, is attached to all log statements such as the currently authenticated user.

Out of the box, Timber knows how to collect some specific information about your system, like Phoenix and Plug HTTP requests, but it needs to be wired into the right places. We make this as simple as possible so that you can get up-and-running with very little work.

Plug & Phoenix

Plug, and frameworks like Phoenix which are based on it, can provide information to Timber about HTTP requests and repsonses. It is up to you whether you want to collect event information, contextual information, or both.

The Timber.ContextPlug plug will add the request ID for the incoming HTTP request to your log context. This way, any log lines written while processing the HTTP request can be filtered.

The Timber.EventPlug plug will log incoming HTTP requests and their responses automatically. This will include header information, request path, query params, time-to-respond, and more.

You only need to add the plug you want to use (or both) to the appropriate pipeline:

defmodule MyApp.Router do
  use MyApp.Web, :router

  pipeline :logging do
    plug Timber.ContextPlug
    plug Timber.EventPlug
  end

  scope "/api", MyApp do
    pipe_through :logging
  end
end

Any of your scopes piped through the :logging pipeline will now have their context and/or events automatically collected by Timber. You can also add Timber to an existing pipeline or pass multiple pipeliness to Phoenix.Router.pipe_through/1.

Note: If you use both, it’s recommended that the Timber.ContextPlug be called first.

Phoenix Controllers & Templates

Timber can also log additional information about Phoenix controllers and templates using the instrumentation tools built right into Phoenix. All you need to do is add Timber.PhoenixInstrumenter to the list of :instrumenters in your endpoint configuration:

config :my_app, MyApp.Endpoint,
  http: [port: 4001],
  root: Path.dirname(__DIR__),
  instrumenters: [Timber.PhoenixInstrumenter],
  pubsub: [name: MyApp.PubSub,
           adapter: Pheonix.PubSub.PG2]

You will need to recompile Phoenix using mix deps.compile phoenix for this change to take effect. If you see duplicate output, try turning off Phoenix’s default logging by modifying the controller definition block in MyApp.Web to add the log: false option:

def controller do
  quote do
    use Phoenix.Controller, log: false
  end
end

Ecto

Timber can collect information about the queries Ecto runs if you declare Timber as one of Ecto’s loggers. This only requires a minor configuration change:

config :my_app, MyApp.Repo,
  loggers: [{Timber.Ecto, :log, []}]

The :loggers configuration key is a special feature from Ecto that informs listed “loggers” of query events. By default, Timber will log an event every time you make a query. This log occurs at the :debug level, but you can easily customize it to the level of your choice:

config :my_app, MyApp.Repo,
  loggers: [{Timber.Ecto, :log, [:info]}]

Additional Events and Contexts

For everything that we don’t provide a plugin for, we try to make it simple to add more information to your application logs. We have a number of events and contexts predefined, but you can also use the freeform CustomContext type to define your own.

For more information, make sure to checkout the documentation.