Git Product home page Git Product logo

timber-elixir's Introduction

๐ŸŒฒ Timber - Great Elixir Logging Made Easy

ISC License Hex.pm Documentation Build Status

Timber for Elixir is a drop in backend for the Elixir Logger that unobtrusively augments your logs with rich metadata and context making them easier to search, use, and read. It pairs with the Timber console to deliver a tailored Elixir logging experience designed to make you more productive.

  1. Installation - One command: mix timber.install
  2. Usage - Simple yet powerful API
  3. Integrations - Automatic context and metadata for your existing logs
  4. The Timber Console - Designed for Elixir developers
  5. Get things done with your logs ๐Ÿ’ช

Installation

  1. Add timber as a dependency in mix.exs:

    # Mix.exs
    
    def application do
      [applications: [:timber]]
    end
    
    def deps do
      [{:timber, "~> 2.7"}]
    end
  2. In your shell, run mix deps.get && mix timber.install.

Usage

Basic text logging

The Timber library works directly with the standard Elixir Logger and installs itself as a backend during the setup process. In this way, basic logging is no different than logging without Timber:

Logger.debug("My log statement")
Logger.info("My log statement")
Logger.warn("My log statement")
Logger.error("My log statement")

...read more in our docs


Logging events (structured data)

Log structured data without sacrificing readability:

event_data = %{customer_id: "xiaus1934", amount: 1900, currency: "USD"}
Logger.info("Payment rejected", event: %{payment_rejected: event_data})

...read more in our docs


Setting context

Add shared structured data across your logs:

Timber.add_context(build: %{version: "1.0.0"})
Logger.info("My log message")

...read more in our docs


Pro-tips ๐Ÿ’ช

Timings & Metrics

Time code blocks:

timer = Timber.start_timer()
# ... code to time ...
Logger.info("Processed background job", event: %{background_job: %{time_ms: timer}})

Log generic metrics:

Logger.info("Processed background job", event: %{background_job: %{time_ms: 45.6}})

...read more in our docs


Tracking background jobs

Note: This tip refers to traditional background jobs backed by a queue. For native Elixir processes we capture the context.runtime.vm_pid automatically. Calls like spawn/1 and Task.async/1 will automatially have their pid included in the context.

For traditional background jobs backed by a queue you'll want to capture relevant job context. This allows you to segement logs by specific jobs, making it easy to debug and monitor your job executions. The most important attribute to capture is the id:

%Timber.Contexts.JobContext{queue_name: "my_queue", id: "abcd1234", attempt: 1}
|> Timber.add_context()

Logger.info("Background job execution started")
# ...
Logger.info("Background job execution completed")

...read more in our docs


Track communication with external services

We use this trick internally at Timber to track communication with external services. It logs requests and responses to external services, giving us insight into response times and failed requests.

Below is a contrived example of submitting an invoice to Stripe.

alias Timber.Events.HTTPRequestEvent
alias Timber.Events.HTTPResponseEvent

method = :get
url = "https://api.stripe.com/v1/invoices"
body = "{\"customer\": \"cus_BHhZyYRirFrPkz\"}"
headers = %{}

Logger.info fn ->
  event = HTTPRequestEvent.new(direction: "outgoing", service_name: "stripe", method: method, url: url, headers: headers, body: body)
  message = HTTPRequestEvent.message(event)
  {message, [event: event]}
end

timer = Timber.start_timer()
case :hackney.request(method, url, headers, body, with_body: true) do
  {:ok, status, resp_headers, resp_body} ->
    Logger.info fn ->
      event = HTTPResponseEvent.new(direction: "incoming", service_name: "stripe", status: status, headers: resp_headers, body: resp_body, time_ms: Timber.duration_ms(timer))
      message = HTTPResponseEvent.message(event)
      {message, [event: event]}
    end

  {:error, error} ->
    message = Exception.message(error)
    Logger.error(message, event: error)
    {:error, error}
end

Note: Only method is required for HTTPRequestEvent, and status for HTTPResponseEvent. body, if logged, will be truncated to 2048 bytes for efficiency reasons. This can be adjusted with Timber.Config.http_body_size_limit/0.

...read more in our docs


Adding metadata to errors

By default, Timber will capture and structure all of your errors and exceptions, there is nothing additional you need to do. You'll get the exception message, name, and backtrace. But, in many cases you need additional context and data. Timber supports additional fields in your exceptions, simply add fields as you would any other struct.

defmodule StripeCommunicationError do
  defexception [:message, :customer_id, :card_token, :stripe_response]
end

raise(
  StripeCommunicationError,
  message: "Bad response #{response} from Stripe!",
  customer_id: "xiaus1934",
  card_token: "mwe42f64",
  stripe_response: response_body
)

...read more in our docs


Sharing context between processes

The Timber.Context is local to each process, this is by design as it prevents processes from conflicting with each other as they maintain their contexts. But many times you'll want to share context between processes because they are related (such as processes created by Task or Flow). In these instances copying the context is easy.

current_context = Timber.LocalContext.get()

Task.async fn ->
  Timber.LocalContext.put(current_context)
  Logger.info("Logs from a separate process")
end

current_context in the above example is captured in the parent process, and because Elixir's variable scope is lexical, you can pass the referenced context into the newly created process. Timber.LocalContext.put/1 copies that context into the new process dictionary.

...read more in our docs


Configuration

Below are a few popular configuration options, for a comprehensive list see Timber.Config.

Capture user context

Capturing user context is a powerful feature that allows you to associate logs with users in your application. This is great for support as you can quickly narrow logs to a specific user, making it easy to identify user reported issues.

How to use it

Simply add the UserContext immediately after you authenticate the user:

%Timber.Contexts.UserContext{id: "my_user_id", name: "John Doe", email: "[email protected]"}
|> Timber.add_context()

All of the UserContext attributes are optional, but at least one much be supplied.

Only log slow Ecto SQL queries

Logging SQL queries can be useful but noisy. To reduce the volume of SQL queries you can limit your logging to queries that surpass an execution time threshold:

How to use it

config :timber, Timber.Integrations.EctoLogger,
  query_time_ms_threshold: 2_000 # 2 seconds

In the above example, only queries that exceed 2 seconds in execution time will be logged.

Integrations

Timber integrates with popular frameworks and libraries to capture context and metadata you couldn't otherwise. This automatically upgrades logs produced by these libraries, making them easier to search and use. Below is a list of libraries we support:

...more coming soon! Make a request by opening an issue

Get things done with your logs

Logging features every developer needs:

...and more! Checkout our the Timber application docs

The Timber Console

Timber Console

Learn more about our app.

Your Moment of Zen

timber-elixir's People

Contributors

binarylogic avatar davidantaramian avatar mitchellhenke avatar trevoke avatar andrelip avatar sysashi avatar doughsay avatar elijahkim avatar girishramnani avatar luciofranco avatar rschmukler avatar octosteve avatar nallwhy avatar thebrianemory avatar

Watchers

Nick Sweeting avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.