Packages
timber
1.1.14
3.1.2
3.1.1
3.1.0
3.0.0
3.0.0-alpha.3
3.0.0-alpha.2
3.0.0-alpha.1
2.8.4
2.8.3
2.8.2
2.8.1
2.8.0
2.7.0
2.6.1
2.6.0
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.4
2.3.3
2.3.1
2.3.0
2.2.1
2.2.0
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
2.0.0-rc7
2.0.0-rc6
2.0.0-rc5
2.0.0-rc4
2.0.0-rc3
2.0.0-rc2
2.0.0-rc1
1.1.18
1.1.17
1.1.16
1.1.15
1.1.14
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
🌲 Great Elixir Logging Made Easy. Official Timber.io Integration.
Current section
Files
Jump to
Current section
Files
README.md
# 🌲 Timber - Master your Elixir apps with structured logging
<p align="center" style="background: #140f2a;">
<a href="http://files.timber.io/images/readme-interface-5.gif"><img src="http://files.timber.io/images/readme-interface-5.gif" width="100%" /></a>
</p>
[](LICENSE.md)
[](https://hex.pm/packages/timber)
[](https://hexdocs.pm/timber/index.html)
[](https://travis-ci.org/timberio/timber-elixir)
* [Timber website](https://timber.io)
* [Library documentation](https://hex.pm/packages/timber)
* [Support](mailto:support@timber.io)
## Overview
Timber is structured logging solution, giving you _complete_ insight into your applications
without the need for agents, or special APIs. It's just better logging, like it always
should have been. 😄
Timber works by automatically by turning your raw text logs into rich structured events. It
pairs with the [Timber Console](https://app.timber.io) to maximize your productivity, allowing
you to _quickly_ find what you need so that you get back to focusing on your core competencies.
Never feel lost again, _always_ have the data you need.
To provide an example, Timber turns this:
```
Sent 200 in 45.ms
```
Into this:
```
Sent 200 in 45.2ms @metadata {"dt": "2017-02-02T01:33:21.154345Z", "level": "info", "context": {"user": {"id": 1}, "http": {"method": "GET", "host": "timber.io", "path": "/path", "request_id": "abcd1234"}}, "event": {"http_response": {"status": 200, "time_ms": 45.2}}}
```
Allowing you to run queries like:
1. `context.request_id:abcd1234` - View all logs generated for a specific request.
2. `context.user.id:1` - View logs generated by a specific user.
3. `type:exception` - View all exceptions with the ability to zoom out and view them in context (request, user, etc).
4. `http_server_response.time_ms:>=1000` - View slow responses with the ability to zoom out and view them in context (request, user, etc).
5. `level:error` - Levels in your logs, imagine that!
## Installation
1. Add `timber` as a dependency in `mix.exs`:
```elixir
# Mix.exs
def application do
[applications: [:timber]]
end
def deps do
[{:timber, "~> 1.0"}]
end
```
2. In your shell, run `mix deps.get`.
3. In your sheel, run `mix timber.install your-timber-app-api-key`.
* You can obtain your API key by [adding your application within Timber](https://app.timber.io). Each app has it's own unique API key.
## Usage
<details><summary><strong>Basic logging</strong></summary><p>
No special API, Timber works directly with `Logger`:
```elixir
Logger.info("My log message")
# My log message @metadata {"level": "info", "context": {...}}
```
---
</p></details>
<details><summary><strong>Tagging logs</strong></summary><p>
Tags provide a quick way to categorize logs and make them easier to search:
```elixir
Logger.info("My log message", tags: ["tag"])
# My log message @metadata {"level": "info", "tags": ["tag"], "context": {...}}
```
* In the [Timber console](https://app.timber.io) use the query: `tags:tag`.
---
</p></details>
<details><summary><strong>Timings</strong></summary><p>
Timings allow you to capture code execution time:
```elixir
timer = Timber.start_timer()
# ... code to time ...
time_ms = Timber.duration_ms(timer)
Logger.info("Task complete", tags: ["my_task"] time_ms: time_ms)
# Task complete @metadata {"level": "info", "tags": ["my_task"], "time_ms": 56.4324, "context": {...}}
```
* In the [Timber console](https://app.timber.io) use the query: `tags:my_task time_ms>500`
---
</p></details>
<details><summary><strong>Custom events</strong></summary><p>
Custom events allow you to capture events central to your line of business like receiving
credit card payments, saving a draft of a post, or changing a user's password.
Note: before logging a custom event, checkout [`Timber.Events`](lib/timber/events) to make
sure it doesn't already exist.
1. Log a map (simplest)
```elixir
event_data = %{customer_id: "xiaus1934", amount: 1900, currency: "USD"}
Logger.info("Payment rejected", event: %{payment_rejected: event_data})
# Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "xiaus1934", "amount": 100, "reason": "Card expired"}}, "context": {...}}
```
2. Or, log a struct (recommended)
```elixir
def PaymentRejectedEvent do
use Timber.Events.CustomEvent, type: :payment_rejected
@enforce_keys [:customer_id, :amount, :currency]
defstruct [:customer_id, :amount, :currency]
def message(%__MODULE__{customer_id: customer_id}) do
"Payment rejected for #{customer_id}"
end
end
event = %PaymentRejectedEvent{customer_id: "xiaus1934", amount: 1900, currency: "USD"}
message = PaymentRejectedEvent.message(event)
Logger.info(message, event: event)
# Payment rejected @metadata {"level": "warn", "event": {"payment_rejected": {"customer_id": "xiaus1934", "amount": 100, "reason": "Card expired"}}, "context": {...}}
```
* In the [Timber console](https://app.timber.io) use the query:
`payment_rejected.customer_id:xiaus1934` or `payment_rejected.amount>100`
#### What about regular Hashes, JSON, or logfmt?
Go for it! Timber will parse the data server side. If the event is meaningful in any way we
_highly_ recommend using custom events (see above).
```ruby
Logger.info(%{key: "value"})
# {"key": "value"} @metadata {"level": "info", "context": {...}}
Logger.info('{"key": "value"}')
# {"key": "value"} @metadata {"level": "info", "context": {...}}
Logger.info("key=value")
# key=value @metadata {"level": "info", "context": {...}}
```
* In the [Timber console](https://app.timber.io) use the query: `key:value`
---
</p></details>
<details><summary><strong>Custom contexts</strong></summary><p>
Context is additional data shared across log lines. Think of it like log join data.
1. Add a map (simplest)
```elixir
Timber.add_context(%{build: %{version: "1.0.0"}})
Logger.info("My log message")
# My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}}
```
2. Add a struct (recommended)
```elixir
def BuildContext do
use Timber.Contexts.CustomContext, type: :build
@enforce_keys [:version]
defstruct [:version]
end
Timber.add_context(%BuildContext{version: "1.0.0"})
Loger.info("My log message")
# My log message @metadata {"level": "info", "context": {"build": {"version": "1.0.0"}}}
```
* In the [Timber console](https://app.timber.io) use the query: `build.version:1.0.0`
</p></details>
## Jibber-Jabber
<details><summary><strong>Which log events does Timber structure for me?</strong></summary><p>
Out of the box you get everything in the [`Timber.Events`](lib/timber/events) namespace.
We also add context to every log, everything in the [`Timber.Contexts`](lib/timber/contexts)
namespace. Context is structured data representing the current environment when the log line
was written. It is included in every log line. Think of it like join data for your logs.
---
</p></details>
<details><summary><strong>What about my current log statements?</strong></summary><p>
They'll continue to work as expected. Timber adheres strictly to the default `Logger` interface
and will never deviate in *any* way.
In fact, traditional log statements for non-meaningful events, debug statements, etc, are
encouraged. In cases where the data is meaningful, consider [logging a custom event](#usage).
</p></details>
<details><summary><strong>How is Timber different?</strong></summary><p>
1. **No lock-in**. Timber is just _better_ logging. There are no agents or special APIs. This means
no risk of vendor lock-in, code debt, or performance issues.
2. **Data quality.** Instead of relying on parsing alone, Timber ships libraries that structure
and augment your logs from _within_ your application. Improving your log data at the source.
3. **Human readability.** Structuring your logs doesn't have to mean losing readability. Instead,
Timber _augments_ your logs. For example: `log message @metadata {...}`. And when you view
your logs in the [Timber console](https://app.timber.io), you'll see the human friendly messages
with the ability to view the associated metadata.
4. **Sane prices, long retention**. Logging is notoriously expensive with low retention. Timber
is affordable and offers _6 months_ of retention by default.
5. **Normalized schema.** Have multiple apps? All of Timber's libraries adhere to our
[JSON schema](https://github.com/timberio/log-event-json-schema). This means queries, alerts,
and graphs for your ruby app can also be applied to your elixir app (for example).
---
</p></details>
---
<p align="center" style="background: #221f40;">
<a href="http://github.com/timberio/timber-elixir"><img src="http://files.timber.io/images/ruby-library-readme-log-truth.png" height="947" /></a>
</p>