Timber v0.4.7 Timber.Eventable protocol

Converts a data structure to a Timber.Event.t. This allows you to support custom types passed to the Logger metadata :event key.

Basic map example

iex> require Logger iex> event_data = %{customer_id: “xiaus1934”, amount: 1900, currency: “USD”} iex> Logger.info(“Payment rejected”, event: %{name: :payment_rejected, data: event_data})

This is the simplest example, and demonstrates Timber’s no lock-in / no code debt promise. Please see Timber.Events.CustomEvent for field explanations.

Using Timber.Events.CustomEvent

iex> require Logger iex> event = Timber.Events.CustomEvent.new(type: :payment_rejected, data: %{customer_id: “xiaus1934”, amount: 1900, currency: “USD”}) iex> Logger.info(“Payment rejected”, event: event)

This adds compile time guarantees in exchange for relying on the Timber library. Please see Timber.Events.CustomEvent for field explanations.

Timing events

Any of the above examples can pass a :time_ms key in the :data map. This is a special key that Timber (and other systems) can use to enhance your experience. An example:

iex> require Logger iex> timer = Timber.Timer.start() iex> # … code to time … iex> time_ms = Timber.Timer.duration_ms(timer) iex> event_data = %{customer_id: “xiaus1934”, amount: 1900, currency: “USD”, time_ms: time_ms} iex> Logger.info(“Payment rejected”, event: %{type: :payment_rejected, data: event_data})

Pro tip! Use your own structs.

At Timber we prefer to define our events as structs. It provides a stronger contract with downstream consumers (alerts, graphs, etc), and there is no risk of code-debt or lock-in to Timber. You are simply adding events to your application. Here’s an example:

First, implement the Timber.Eventable protocol:

iex> defimpl Timber.Eventable, for: Any do iex> def toevent(%{_struct: module} = event) do iex> type = module.type() iex> data = Map.from_struct(event) iex> Timber.Events.CustomEvent.new(type: type, data: data) iex> end iex> end

Notice we expect every event to have a type function, Timber requires this for custom events. Let’s define a behaviour to ensure all events follow this pattern:

iex> defmodule MyApp.Event do iex> @callback type(struct()) :: atom() iex> end

Lastly, define your event and log it!:

iex> require Logger iex> defmodule PaymentRejectedEvent do iex> @behaviour MyApp.Event iex> @derive Timber.Eventable iex> defstruct [:customer_id, :amount, :currency] iex> def type(_event), do: :payment_rejected iex> end iex> event = %PaymentRejectedEvent{customer_id: “xiaus1934”, amount: 1900, currency: “USD”} iex> Logger.info(“Payment rejected”, event: event)

Note: we recommend adding a @callback message(struct()) :: String.t to MyApp.Event. This follows the same pattern set by the Exception behaviour. This way if event creation and logging are separated, logging an event is as simple as:

message = MyApp.Event.message(event)
Logger.info(message, event: event)

Summary

Types

t()
t :: term

Functions

to_event(data)
to_event(any) :: t