🌲 Timber - Great Node Logging Made Easy
Node logging has problems. The average node project has libraries logging to the
console
, internal logs using winston or
bunyan, and frameworks logging in their own format.
The end result is usually this:
192.442.345.32 - - [Mon, 09 Oct 2017 23:23:37 GMT] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
Log message from console.warn
{"level":"debug","message":"Log message from winston"}
Timber solves this by normalizing and augmenting your logs with structured data, regardless of
the source. This not only normalizes your logs, but captures additional useful metadata:
GET / HTTP/1.1 @metadata {"dt": "2017-10-08T23:23:37.234Z", "level": "info", "context": {"http": {"remote_addr": "192.442.345.32"}}, "event": {"http_request": {"method": "GET", "path": "/", "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"}}}
Log message from console.warn @metadata {"dt": "2017-10-08T23:23:37.234Z", "level": "warn"}
Log message from winston @metadata {"dt": "2017-10-08T23:23:37.234Z", "level": "debug"}
Ah! Consistent, easy to use logs. To get started:
- Installation - Simple setup
- Usage - Clean API. Works with
console
, winston, and bunyan. - Integrations - Automatic context and metadata for your existing logs
- The Timber Console - Beautiful, fast, and designed for developers
- Get things done with your logs 💪
Installation
Signup at timber.io and follow the in-app instructions.
For those interested in manual instructions, see the timber.io installation docs.
Usage
Basic logging
No special API, Timber works directly with your logger of choice:
console.log("My log message")
console.info("My log message")
console.warn("My log message")
console.error("My log message")
winston.info("My log message")
winston.warn("My log message")
logger.info("My log message")
logger.warn("My log message")
Logging events (structured data)
Log structured data without sacrificing readability:
console.warn("Payment rejected", {
event: {
payment_rejected: { customer_id: "abcd1234", amount: 100, reason: "Card expired" }
}
});
winston.warn("Payment rejected", {
event: {
payment_rejected: { customer_id: "abcd1234", amount: 100, reason: "Card expired" }
}
});
logger.warn({
event: {
payment_rejected: { customer_id: "abcd1234", amount: 100, reason: "Card expired" }
}
}, "Payment rejected");
Setting context
Context for node is coming soon!
Because node does not have a concept of local storage, we're working to implement
continuation local storage.
This will enable shared join data across your logs, allowing you to relate them.
Star / watch this repo to be notified when this goes live!
Configuration
Express middleware
If you're using express, you can use the timber middleware to automatically log
all http request/response events
const express = require('express')
const timber = require('timber')
const transport = new timber.transports.HTTPS('your-timber-api-key');
timber.install(transport);
const app = express()
app.use(timber.middlewares.express())
app.get('/', function (req, res) {
res.send('hello, world!')
})
The express middleware accepts a single argument for configuration options. To learn more about the available options, visit the express integration docs
Logging with Winston
If you're using winston, you can use the winston transport to send all of winston's logs to timber.io
const winston = require('winston')
const timber = require('timber')
const transport = new timber.transports.HTTPS('your-api-key')
timber.install(transport)
winston.remove(winston.transports.Console)
winston.add(winston.transports.Console, {
formatter: timber.formatters.Winston
})
winston.log('info', 'Sample log message')
When you pass a metadata object to winston, timber will automatically augment your log line with it:
winston.log('info', 'Log message with metadata', { user: 'username' })
You can augment your log with a custom event by providing an event
key at the root of your metadata object:
winston.log('info', 'Log message with event', { event: custom_event_name: { ... } })
Adding custom context is just as easily done by adding the context
key to the root of your metadata object:
winston.log('info', 'Log message with event', { context: { ... } })
If you're using the timber express middleware, you'll most likely want to configure it to use winston as the logger. This can be done by setting the logger
config attribute to winston
:
timber.config.logger = winston
Logging with Bunyan
If you're using bunyan, you can use the bunyan transport to send all of bunyan's logs to timber.io
const bunyan = require('bunyan')
const timber = require('timber')
const winston = require('winston')
const timber = require('timber')
const transport = new timber.transports.HTTPS('your-api-key')
timber.install(transport)
const log = bunyan.createLogger({ name: 'Timber Logger' })
log.info('Sample log message')
If you want to augment your log with custom metadata, simply add an object as the first argument:
log.info({ user: 'username' }, 'Log message with metadata')
You can augment your log with a custom event by providing an event
key at the root of your metadata object:
log.info({ event: { custom_event_name: { ... } } }, 'Log message with event')
Adding custom context is just as easily done by adding the context
key to the root of your metadata object:
log.info({ context: { ... } }, 'Log message with event')
If you're using the timber express middleware, you'll most likely want to configure it to use bunyan as the logger. This can be done by setting the logger
config attribute to the bunyan logger you created:
timber.config.logger = log
Attaching a custom stream
By default, Timber makes attaching to stdout
and stderr
very easy through the convenient timber.install(transport)
function.
However, it's possible to attach the transport to any writeable stream
using the timber.attach()
function!
const transport = timber.transports.HTTPS('timber-api-key')
timber.attach([transport], process.stdout)
timber.attach([transport], process.stderr)
const file_transport = fs.createWriteStream("./output.log", {flags: "a"})
timber.attach([transport, file_transport], process.stdout)
Integrations
Timber integrates with popular frameworks and libraries to capture context and metadata you
couldn't otherwise. This automatically augments logs produced by these libraries, making them
easier to search and use. Below is a list of libraries we
support:
- Frameworks
- Loggers
- Platforms
...more coming soon! Make a request by opening an issue
Get things done with your logs
Logging features designed to help developers get more done:
- Powerful searching. - Find what you need faster.
- Live tail users. - Easily solve customer issues.
- Viw logs per HTTP request. - See the full story without the noise.
- Inspect HTTP request parameters. - Quickly reproduce issues.
- Threshold based alerting. - Know when things break.
- ...and more! Checkout our the Timber application docs
The Timber Console
Learn more about our app.
Your Moment of Zen
ustomer issues.](https://timber.io/docs/app/console/tail-a-user)
3. [**Viw logs per HTTP request.** - See the full story without the noise.](https://timber.io/docs/app/console/trace-http-requests)
4. [**Inspect HTTP request parameters.** - Quickly reproduce issues.](https://timber.io/docs/app/console/inspect-http-requests)
5. [**Threshold based alerting.** - Know when things break.](https://timber.io/docs/app/alerts)
6. ...and more! Checkout our [the Timber application docs](https://timber.io/docs/app)
The Timber Console
Learn more about our app.
Your Moment of Zen