1145 lines
32 KiB
Markdown
1145 lines
32 KiB
Markdown
|
# winston
|
|||
|
|
|||
|
A logger for just about everything.
|
|||
|
|
|||
|
[![Version npm](https://img.shields.io/npm/v/winston.svg?style=flat-square)](https://www.npmjs.com/package/winston)[![npm Downloads](https://img.shields.io/npm/dm/winston.svg?style=flat-square)](https://npmcharts.com/compare/winston?minimal=true)[![Build Status](https://img.shields.io/travis/winstonjs/winston/master.svg?style=flat-square)](https://travis-ci.org/winstonjs/winston)[![Dependencies](https://img.shields.io/david/winstonjs/winston.svg?style=flat-square)](https://david-dm.org/winstonjs/winston)
|
|||
|
|
|||
|
[![NPM](https://nodei.co/npm/winston.png?downloads=true&downloadRank=true)](https://nodei.co/npm/winston/)
|
|||
|
|
|||
|
[![Join the chat at https://gitter.im/winstonjs/winston](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/winstonjs/winston?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|||
|
|
|||
|
## winston@3.2.0
|
|||
|
|
|||
|
See the [Upgrade Guide](UPGRADE-3.0.md) for more information. Bug reports and
|
|||
|
PRs welcome!
|
|||
|
|
|||
|
## Looking for `winston@2.x` documentation?
|
|||
|
|
|||
|
Please note that the documentation below is for `winston@3`.
|
|||
|
[Read the `winston@2.x` documentation].
|
|||
|
|
|||
|
## Motivation
|
|||
|
|
|||
|
`winston` is designed to be a simple and universal logging library with
|
|||
|
support for multiple transports. A transport is essentially a storage device
|
|||
|
for your logs. Each `winston` logger can have multiple transports (see:
|
|||
|
[Transports]) configured at different levels (see: [Logging levels]). For
|
|||
|
example, one may want error logs to be stored in a persistent remote location
|
|||
|
(like a database), but all logs output to the console or a local file.
|
|||
|
|
|||
|
`winston` aims to decouple parts of the logging process to make it more
|
|||
|
flexible and extensible. Attention is given to supporting flexibility in log
|
|||
|
formatting (see: [Formats]) & levels (see: [Using custom logging levels]), and
|
|||
|
ensuring those APIs decoupled from the implementation of transport logging
|
|||
|
(i.e. how the logs are stored / indexed, see: [Adding Custom Transports]) to
|
|||
|
the API that they exposed to the programmer.
|
|||
|
|
|||
|
## Quick Start
|
|||
|
|
|||
|
TL;DR? Check out the [quick start example][quick-example] in `./examples/`.
|
|||
|
There are a number of other examples in [`./examples/*.js`][examples].
|
|||
|
Don't see an example you think should be there? Submit a pull request
|
|||
|
to add it!
|
|||
|
|
|||
|
## Usage
|
|||
|
|
|||
|
The recommended way to use `winston` is to create your own logger. The
|
|||
|
simplest way to do this is using `winston.createLogger`:
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({
|
|||
|
level: 'info',
|
|||
|
format: winston.format.json(),
|
|||
|
defaultMeta: { service: 'user-service' },
|
|||
|
transports: [
|
|||
|
//
|
|||
|
// - Write to all logs with level `info` and below to `combined.log`
|
|||
|
// - Write all logs error (and below) to `error.log`.
|
|||
|
//
|
|||
|
new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
|||
|
new winston.transports.File({ filename: 'combined.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
//
|
|||
|
// If we're not in production then log to the `console` with the format:
|
|||
|
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
|
|||
|
//
|
|||
|
if (process.env.NODE_ENV !== 'production') {
|
|||
|
logger.add(new winston.transports.Console({
|
|||
|
format: winston.format.simple()
|
|||
|
}));
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
You may also log directly via the default logger exposed by
|
|||
|
`require('winston')`, but this merely intended to be a convenient shared
|
|||
|
logger to use throughout your application if you so choose.
|
|||
|
|
|||
|
## Table of contents
|
|||
|
|
|||
|
* [Motivation](#motivation)
|
|||
|
* [Quick Start](#quick-start)
|
|||
|
* [Usage](#usage)
|
|||
|
* [Table of Contents](#table-of-contents)
|
|||
|
* [Logging](#logging)
|
|||
|
* [Creating your logger](#creating-your-own-logger)
|
|||
|
* [Streams, `objectMode`, and `info` objects](#streams-objectmode-and-info-objects)
|
|||
|
* [Formats]
|
|||
|
* [Combining formats](#combining-formats)
|
|||
|
* [String interpolation](#string-interpolation)
|
|||
|
* [Filtering `info` Objects](#filtering-info-objects)
|
|||
|
* [Creating custom formats](#creating-custom-formats)
|
|||
|
* [Logging levels]
|
|||
|
* [Using logging levels](#using-logging-levels)
|
|||
|
* [Using custom logging levels](#using-custom-logging-levels)
|
|||
|
* [Transports]
|
|||
|
* [Multiple transports of the same type](#multiple-transports-of-the-same-type)
|
|||
|
* [Adding Custom Transports](#adding-custom-transports)
|
|||
|
* [Exceptions](#exceptions)
|
|||
|
* [Handling Uncaught Exceptions with winston](#handling-uncaught-exceptions-with-winston)
|
|||
|
* [To Exit or Not to Exit](#to-exit-or-not-to-exit)
|
|||
|
* [Profiling](#profiling)
|
|||
|
* [Streaming Logs](#streaming-logs)
|
|||
|
* [Querying Logs](#querying-logs)
|
|||
|
* [Further Reading](#further-reading)
|
|||
|
* [Using the default logger](#using-the-default-logger)
|
|||
|
* [Awaiting logs to be written in `winston`](#awaiting-logs-to-be-written-in-winston)
|
|||
|
* [Working with multiple Loggers in `winston`](#working-with-multiple-loggers-in-winston)
|
|||
|
* [Installation](#installation)
|
|||
|
* [Run Tests](#run-tests)
|
|||
|
|
|||
|
## Logging
|
|||
|
|
|||
|
Logging levels in `winston` conform to the severity ordering specified by
|
|||
|
[RFC5424]: _severity of all levels is assumed to be numerically **ascending**
|
|||
|
from most important to least important._
|
|||
|
|
|||
|
``` js
|
|||
|
const levels = {
|
|||
|
error: 0,
|
|||
|
warn: 1,
|
|||
|
info: 2,
|
|||
|
verbose: 3,
|
|||
|
debug: 4,
|
|||
|
silly: 5
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
### Creating your own Logger
|
|||
|
You get started by creating a logger using `winston.createLogger`:
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({
|
|||
|
transports: [
|
|||
|
new winston.transports.Console(),
|
|||
|
new winston.transports.File({ filename: 'combined.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
A logger accepts the following parameters:
|
|||
|
|
|||
|
| Name | Default | Description |
|
|||
|
| ------------- | --------------------------- | --------------- |
|
|||
|
| `level` | `'info'` | Log only if [`info.level`](#streams-objectmode-and-info-objects) less than or equal to this level |
|
|||
|
| `levels` | `winston.config.npm.levels` | Levels (and colors) representing log priorities |
|
|||
|
| `format` | `winston.format.json` | Formatting for `info` messages (see: [Formats]) |
|
|||
|
| `transports` | `[]` _(No transports)_ | Set of logging targets for `info` messages |
|
|||
|
| `exitOnError` | `true` | If false, handled exceptions will not cause `process.exit` |
|
|||
|
| `silent` | `false` | If true, all logs are suppressed |
|
|||
|
|
|||
|
The levels provided to `createLogger` will be defined as convenience methods
|
|||
|
on the `logger` returned.
|
|||
|
|
|||
|
``` js
|
|||
|
//
|
|||
|
// Logging
|
|||
|
//
|
|||
|
logger.log({
|
|||
|
level: 'info',
|
|||
|
message: 'Hello distributed log files!'
|
|||
|
});
|
|||
|
|
|||
|
logger.info('Hello again distributed logs');
|
|||
|
```
|
|||
|
|
|||
|
You can add or remove transports from the `logger` once it has been provided
|
|||
|
to you from `winston.createLogger`:
|
|||
|
|
|||
|
``` js
|
|||
|
const files = new winston.transports.File({ filename: 'combined.log' });
|
|||
|
const console = new winston.transports.Console();
|
|||
|
|
|||
|
logger
|
|||
|
.clear() // Remove all transports
|
|||
|
.add(console) // Add console transport
|
|||
|
.add(files) // Add file transport
|
|||
|
.remove(console); // Remove console transport
|
|||
|
```
|
|||
|
|
|||
|
You can also wholesale reconfigure a `winston.Logger` instance using the
|
|||
|
`configure` method:
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({
|
|||
|
level: 'info',
|
|||
|
transports: [
|
|||
|
new winston.transports.Console(),
|
|||
|
new winston.transports.File({ filename: 'combined.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
//
|
|||
|
// Replaces the previous transports with those in the
|
|||
|
// new configuration wholesale.
|
|||
|
//
|
|||
|
const DailyRotateFile = require('winston-daily-rotate-file');
|
|||
|
logger.configure({
|
|||
|
level: 'verbose',
|
|||
|
transports: [
|
|||
|
new DailyRotateFile(opts)
|
|||
|
]
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
### Creating child loggers
|
|||
|
|
|||
|
You can create child loggers from existing loggers to pass metadata overrides:
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({
|
|||
|
transports: [
|
|||
|
new winston.transports.Console(),
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
const childLogger = logger.child({ requestId: '451' });
|
|||
|
```
|
|||
|
|
|||
|
### Streams, `objectMode`, and `info` objects
|
|||
|
|
|||
|
In `winston`, both `Logger` and `Transport` instances are treated as
|
|||
|
[`objectMode`](https://nodejs.org/api/stream.html#stream_object_mode)
|
|||
|
streams that accept an `info` object.
|
|||
|
|
|||
|
The `info` parameter provided to a given format represents a single log
|
|||
|
message. The object itself is mutable. Every `info` must have at least the
|
|||
|
`level` and `message` properties:
|
|||
|
|
|||
|
``` js
|
|||
|
const info = {
|
|||
|
level: 'info', // Level of the logging message
|
|||
|
message: 'Hey! Log something?' // Descriptive message being logged.
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
Properties **besides level and message** are considered as "`meta`". i.e.:
|
|||
|
|
|||
|
``` js
|
|||
|
const { level, message, ...meta } = info;
|
|||
|
```
|
|||
|
|
|||
|
Several of the formats in `logform` itself add additional properties:
|
|||
|
|
|||
|
| Property | Format added by | Description |
|
|||
|
| ----------- | --------------- | ----------- |
|
|||
|
| `splat` | `splat()` | String interpolation splat for `%d %s`-style messages. |
|
|||
|
| `timestamp` | `timestamp()` | timestamp the message was received. |
|
|||
|
| `label` | `label()` | Custom label associated with each message. |
|
|||
|
| `ms` | `ms()` | Number of milliseconds since the previous log message. |
|
|||
|
|
|||
|
As a consumer you may add whatever properties you wish – _internal state is
|
|||
|
maintained by `Symbol` properties:_
|
|||
|
|
|||
|
- `Symbol.for('level')` _**(READ-ONLY)**:_ equal to `level` property.
|
|||
|
**Is treated as immutable by all code.**
|
|||
|
- `Symbol.for('message'):` complete string message set by "finalizing formats":
|
|||
|
- `json`
|
|||
|
- `logstash`
|
|||
|
- `printf`
|
|||
|
- `prettyPrint`
|
|||
|
- `simple`
|
|||
|
- `Symbol.for('splat')`: additional string interpolation arguments. _Used
|
|||
|
exclusively by `splat()` format._
|
|||
|
|
|||
|
These Symbols are stored in another package: `triple-beam` so that all
|
|||
|
consumers of `logform` can have the same Symbol reference. i.e.:
|
|||
|
|
|||
|
``` js
|
|||
|
const { LEVEL, MESSAGE, SPLAT } = require('triple-beam');
|
|||
|
|
|||
|
console.log(LEVEL === Symbol.for('level'));
|
|||
|
// true
|
|||
|
|
|||
|
console.log(MESSAGE === Symbol.for('message'));
|
|||
|
// true
|
|||
|
|
|||
|
console.log(SPLAT === Symbol.for('splat'));
|
|||
|
// true
|
|||
|
```
|
|||
|
|
|||
|
> **NOTE:** any `{ message }` property in a `meta` object provided will
|
|||
|
> automatically be concatenated to any `msg` already provided: For
|
|||
|
> example the below will concatenate 'world' onto 'hello':
|
|||
|
>
|
|||
|
> ``` js
|
|||
|
> logger.log('error', 'hello', { message: 'world' });
|
|||
|
> logger.info('hello', { message: 'world' });
|
|||
|
> ```
|
|||
|
|
|||
|
## Formats
|
|||
|
|
|||
|
Formats in `winston` can be accessed from `winston.format`. They are
|
|||
|
implemented in [`logform`](https://github.com/winstonjs/logform), a separate
|
|||
|
module from `winston`. This allows flexibility when writing your own transports
|
|||
|
in case you wish to include a default format with your transport.
|
|||
|
|
|||
|
In modern versions of `node` template strings are very performant and are the
|
|||
|
recommended way for doing most end-user formatting. If you want to bespoke
|
|||
|
format your logs, `winston.format.printf` is for you:
|
|||
|
|
|||
|
``` js
|
|||
|
const { createLogger, format, transports } = require('winston');
|
|||
|
const { combine, timestamp, label, printf } = format;
|
|||
|
|
|||
|
const myFormat = printf(({ level, message, label, timestamp }) => {
|
|||
|
return `${timestamp} [${label}] ${level}: ${message}`;
|
|||
|
});
|
|||
|
|
|||
|
const logger = createLogger({
|
|||
|
format: combine(
|
|||
|
label({ label: 'right meow!' }),
|
|||
|
timestamp(),
|
|||
|
myFormat
|
|||
|
),
|
|||
|
transports: [new transports.Console()]
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
To see what built-in formats are available and learn more about creating your
|
|||
|
own custom logging formats, see [`logform`][logform].
|
|||
|
|
|||
|
### Combining formats
|
|||
|
|
|||
|
Any number of formats may be combined into a single format using
|
|||
|
`format.combine`. Since `format.combine` takes no `opts`, as a convenience it
|
|||
|
returns pre-created instance of the combined format.
|
|||
|
|
|||
|
``` js
|
|||
|
const { createLogger, format, transports } = require('winston');
|
|||
|
const { combine, timestamp, label, prettyPrint } = format;
|
|||
|
|
|||
|
const logger = createLogger({
|
|||
|
format: combine(
|
|||
|
label({ label: 'right meow!' }),
|
|||
|
timestamp(),
|
|||
|
prettyPrint()
|
|||
|
),
|
|||
|
transports: [new transports.Console()]
|
|||
|
})
|
|||
|
|
|||
|
logger.log({
|
|||
|
level: 'info',
|
|||
|
message: 'What time is the testing at?'
|
|||
|
});
|
|||
|
// Outputs:
|
|||
|
// { level: 'info',
|
|||
|
// message: 'What time is the testing at?',
|
|||
|
// label: 'right meow!',
|
|||
|
// timestamp: '2017-09-30T03:57:26.875Z' }
|
|||
|
```
|
|||
|
|
|||
|
### String interpolation
|
|||
|
|
|||
|
The `log` method provides the string interpolation using [util.format]. **It
|
|||
|
must be enabled using `format.splat()`.**
|
|||
|
|
|||
|
Below is an example that defines a format with string interpolation of
|
|||
|
messages using `format.splat` and then serializes the entire `info` message
|
|||
|
using `format.simple`.
|
|||
|
|
|||
|
``` js
|
|||
|
const { createLogger, format, transports } = require('winston');
|
|||
|
const logger = createLogger({
|
|||
|
format: format.combine(
|
|||
|
format.splat(),
|
|||
|
format.simple()
|
|||
|
),
|
|||
|
transports: [new transports.Console()]
|
|||
|
});
|
|||
|
|
|||
|
// info: test message my string {}
|
|||
|
logger.log('info', 'test message %s', 'my string');
|
|||
|
|
|||
|
// info: test message 123 {}
|
|||
|
logger.log('info', 'test message %d', 123);
|
|||
|
|
|||
|
// info: test message first second {number: 123}
|
|||
|
logger.log('info', 'test message %s, %s', 'first', 'second', { number: 123 });
|
|||
|
```
|
|||
|
|
|||
|
### Filtering `info` Objects
|
|||
|
|
|||
|
If you wish to filter out a given `info` Object completely when logging then
|
|||
|
simply return a falsey value.
|
|||
|
|
|||
|
``` js
|
|||
|
const { createLogger, format, transports } = require('winston');
|
|||
|
|
|||
|
// Ignore log messages if they have { private: true }
|
|||
|
const ignorePrivate = format((info, opts) => {
|
|||
|
if (info.private) { return false; }
|
|||
|
return info;
|
|||
|
});
|
|||
|
|
|||
|
const logger = createLogger({
|
|||
|
format: format.combine(
|
|||
|
ignorePrivate(),
|
|||
|
format.json()
|
|||
|
),
|
|||
|
transports: [new transports.Console()]
|
|||
|
});
|
|||
|
|
|||
|
// Outputs: {"level":"error","message":"Public error to share"}
|
|||
|
logger.log({
|
|||
|
level: 'error',
|
|||
|
message: 'Public error to share'
|
|||
|
});
|
|||
|
|
|||
|
// Messages with { private: true } will not be written when logged.
|
|||
|
logger.log({
|
|||
|
private: true,
|
|||
|
level: 'error',
|
|||
|
message: 'This is super secret - hide it.'
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
Use of `format.combine` will respect any falsey values return and stop
|
|||
|
evaluation of later formats in the series. For example:
|
|||
|
|
|||
|
``` js
|
|||
|
const { format } = require('winston');
|
|||
|
const { combine, timestamp, label } = format;
|
|||
|
|
|||
|
const willNeverThrow = format.combine(
|
|||
|
format(info => { return false })(), // Ignores everything
|
|||
|
format(info => { throw new Error('Never reached') })()
|
|||
|
);
|
|||
|
```
|
|||
|
|
|||
|
### Creating custom formats
|
|||
|
|
|||
|
Formats are prototypal objects (i.e. class instances) that define a single
|
|||
|
method: `transform(info, opts)` and return the mutated `info`:
|
|||
|
|
|||
|
- `info`: an object representing the log message.
|
|||
|
- `opts`: setting specific to the current instance of the format.
|
|||
|
|
|||
|
They are expected to return one of two things:
|
|||
|
|
|||
|
- **An `info` Object** representing the modified `info` argument. Object
|
|||
|
references need not be preserved if immutability is preferred. All current
|
|||
|
built-in formats consider `info` mutable, but [immutablejs] is being
|
|||
|
considered for future releases.
|
|||
|
- **A falsey value** indicating that the `info` argument should be ignored by the
|
|||
|
caller. (See: [Filtering `info` Objects](#filtering-info-objects)) below.
|
|||
|
|
|||
|
`winston.format` is designed to be as simple as possible. To define a new
|
|||
|
format simple pass it a `transform(info, opts)` function to get a new
|
|||
|
`Format`.
|
|||
|
|
|||
|
The named `Format` returned can be used to create as many copies of the given
|
|||
|
`Format` as desired:
|
|||
|
|
|||
|
``` js
|
|||
|
const { format } = require('winston');
|
|||
|
|
|||
|
const volume = format((info, opts) => {
|
|||
|
if (opts.yell) {
|
|||
|
info.message = info.message.toUpperCase();
|
|||
|
} else if (opts.whisper) {
|
|||
|
info.message = info.message.toLowerCase();
|
|||
|
}
|
|||
|
|
|||
|
return info;
|
|||
|
});
|
|||
|
|
|||
|
// `volume` is now a function that returns instances of the format.
|
|||
|
const scream = volume({ yell: true });
|
|||
|
console.dir(scream.transform({
|
|||
|
level: 'info',
|
|||
|
message: `sorry for making you YELL in your head!`
|
|||
|
}, scream.options));
|
|||
|
// {
|
|||
|
// level: 'info'
|
|||
|
// message: 'SORRY FOR MAKING YOU YELL IN YOUR HEAD!'
|
|||
|
// }
|
|||
|
|
|||
|
// `volume` can be used multiple times to create different formats.
|
|||
|
const whisper = volume({ whisper: true });
|
|||
|
console.dir(whisper.transform({
|
|||
|
level: 'info',
|
|||
|
message: `WHY ARE THEY MAKING US YELL SO MUCH!`
|
|||
|
}, whisper.options));
|
|||
|
// {
|
|||
|
// level: 'info'
|
|||
|
// message: 'why are they making us yell so much!'
|
|||
|
// }
|
|||
|
```
|
|||
|
|
|||
|
## Logging Levels
|
|||
|
|
|||
|
Logging levels in `winston` conform to the severity ordering specified by
|
|||
|
[RFC5424]: _severity of all levels is assumed to be numerically **ascending**
|
|||
|
from most important to least important._
|
|||
|
|
|||
|
Each `level` is given a specific integer priority. The higher the priority the
|
|||
|
more important the message is considered to be, and the lower the
|
|||
|
corresponding integer priority. For example, as specified exactly in RFC5424
|
|||
|
the `syslog` levels are prioritized from 0 to 7 (highest to lowest).
|
|||
|
|
|||
|
```js
|
|||
|
{
|
|||
|
emerg: 0,
|
|||
|
alert: 1,
|
|||
|
crit: 2,
|
|||
|
error: 3,
|
|||
|
warning: 4,
|
|||
|
notice: 5,
|
|||
|
info: 6,
|
|||
|
debug: 7
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
Similarly, `npm` logging levels are prioritized from 0 to 5 (highest to
|
|||
|
lowest):
|
|||
|
|
|||
|
``` js
|
|||
|
{
|
|||
|
error: 0,
|
|||
|
warn: 1,
|
|||
|
info: 2,
|
|||
|
verbose: 3,
|
|||
|
debug: 4,
|
|||
|
silly: 5
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
If you do not explicitly define the levels that `winston` should use, the
|
|||
|
`npm` levels above will be used.
|
|||
|
|
|||
|
### Using Logging Levels
|
|||
|
|
|||
|
Setting the level for your logging message can be accomplished in one of two
|
|||
|
ways. You can pass a string representing the logging level to the log() method
|
|||
|
or use the level specified methods defined on every winston Logger.
|
|||
|
|
|||
|
``` js
|
|||
|
//
|
|||
|
// Any logger instance
|
|||
|
//
|
|||
|
logger.log('silly', "127.0.0.1 - there's no place like home");
|
|||
|
logger.log('debug', "127.0.0.1 - there's no place like home");
|
|||
|
logger.log('verbose', "127.0.0.1 - there's no place like home");
|
|||
|
logger.log('info', "127.0.0.1 - there's no place like home");
|
|||
|
logger.log('warn', "127.0.0.1 - there's no place like home");
|
|||
|
logger.log('error', "127.0.0.1 - there's no place like home");
|
|||
|
logger.info("127.0.0.1 - there's no place like home");
|
|||
|
logger.warn("127.0.0.1 - there's no place like home");
|
|||
|
logger.error("127.0.0.1 - there's no place like home");
|
|||
|
|
|||
|
//
|
|||
|
// Default logger
|
|||
|
//
|
|||
|
winston.log('info', "127.0.0.1 - there's no place like home");
|
|||
|
winston.info("127.0.0.1 - there's no place like home");
|
|||
|
```
|
|||
|
|
|||
|
`winston` allows you to define a `level` property on each transport which
|
|||
|
specifies the **maximum** level of messages that a transport should log. For
|
|||
|
example, using the `syslog` levels you could log only `error` messages to the
|
|||
|
console and everything `info` and below to a file (which includes `error`
|
|||
|
messages):
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({
|
|||
|
levels: winston.config.syslog.levels,
|
|||
|
transports: [
|
|||
|
new winston.transports.Console({ level: 'error' }),
|
|||
|
new winston.transports.File({
|
|||
|
filename: 'combined.log',
|
|||
|
level: 'info'
|
|||
|
})
|
|||
|
]
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
You may also dynamically change the log level of a transport:
|
|||
|
|
|||
|
``` js
|
|||
|
const transports = {
|
|||
|
console: new winston.transports.Console({ level: 'warn' }),
|
|||
|
file: new winston.transports.File({ filename: 'combined.log', level: 'error' })
|
|||
|
};
|
|||
|
|
|||
|
const logger = winston.createLogger({
|
|||
|
transports: [
|
|||
|
transports.console,
|
|||
|
transports.file
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
logger.info('Will not be logged in either transport!');
|
|||
|
transports.console.level = 'info';
|
|||
|
transports.file.level = 'info';
|
|||
|
logger.info('Will be logged in both transports!');
|
|||
|
```
|
|||
|
|
|||
|
`winston` supports customizable logging levels, defaulting to npm style
|
|||
|
logging levels. Levels must be specified at the time of creating your logger.
|
|||
|
|
|||
|
### Using Custom Logging Levels
|
|||
|
|
|||
|
In addition to the predefined `npm`, `syslog`, and `cli` levels available in
|
|||
|
`winston`, you can also choose to define your own:
|
|||
|
|
|||
|
``` js
|
|||
|
const myCustomLevels = {
|
|||
|
levels: {
|
|||
|
foo: 0,
|
|||
|
bar: 1,
|
|||
|
baz: 2,
|
|||
|
foobar: 3
|
|||
|
},
|
|||
|
colors: {
|
|||
|
foo: 'blue',
|
|||
|
bar: 'green',
|
|||
|
baz: 'yellow',
|
|||
|
foobar: 'red'
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const customLevelLogger = winston.createLogger({
|
|||
|
levels: myCustomLevels.levels
|
|||
|
});
|
|||
|
|
|||
|
customLevelLogger.foobar('some foobar level-ed message');
|
|||
|
```
|
|||
|
|
|||
|
Although there is slight repetition in this data structure, it enables simple
|
|||
|
encapsulation if you do not want to have colors. If you do wish to have
|
|||
|
colors, in addition to passing the levels to the Logger itself, you must make
|
|||
|
winston aware of them:
|
|||
|
|
|||
|
``` js
|
|||
|
winston.addColors(myCustomLevels.colors);
|
|||
|
```
|
|||
|
|
|||
|
This enables loggers using the `colorize` formatter to appropriately color and style
|
|||
|
the output of custom levels.
|
|||
|
|
|||
|
Additionally, you can also change background color and font style.
|
|||
|
For example,
|
|||
|
``` js
|
|||
|
baz: 'italic yellow',
|
|||
|
foobar: 'bold red cyanBG'
|
|||
|
```
|
|||
|
|
|||
|
Possible options are below.
|
|||
|
|
|||
|
* Font styles: `bold`, `dim`, `italic`, `underline`, `inverse`, `hidden`,
|
|||
|
`strikethrough`.
|
|||
|
|
|||
|
* Font foreground colors: `black`, `red`, `green`, `yellow`, `blue`, `magenta`,
|
|||
|
`cyan`, `white`, `gray`, `grey`.
|
|||
|
|
|||
|
* Background colors: `blackBG`, `redBG`, `greenBG`, `yellowBG`, `blueBG`
|
|||
|
`magentaBG`, `cyanBG`, `whiteBG`
|
|||
|
|
|||
|
### Colorizing Standard logging levels
|
|||
|
|
|||
|
To colorize the standard logging level add
|
|||
|
```js
|
|||
|
winston.format.combine(
|
|||
|
winston.format.colorize(),
|
|||
|
winston.format.json()
|
|||
|
);
|
|||
|
```
|
|||
|
where `winston.format.json()` is whatever other formatter you want to use. The `colorize` formatter must come before any formatters adding text you wish to color.
|
|||
|
|
|||
|
## Transports
|
|||
|
|
|||
|
There are several [core transports] included in `winston`, which leverage the
|
|||
|
built-in networking and file I/O offered by Node.js core. In addition, there
|
|||
|
are [additional transports] written by members of the community.
|
|||
|
|
|||
|
## Multiple transports of the same type
|
|||
|
|
|||
|
It is possible to use multiple transports of the same type e.g.
|
|||
|
`winston.transports.File` when you construct the transport.
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({
|
|||
|
transports: [
|
|||
|
new winston.transports.File({
|
|||
|
filename: 'combined.log',
|
|||
|
level: 'info'
|
|||
|
}),
|
|||
|
new winston.transports.File({
|
|||
|
filename: 'errors.log',
|
|||
|
level: 'error'
|
|||
|
})
|
|||
|
]
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
If you later want to remove one of these transports you can do so by using the
|
|||
|
transport itself. e.g.:
|
|||
|
|
|||
|
``` js
|
|||
|
const combinedLogs = logger.transports.find(transport => {
|
|||
|
return transport.filename === 'combined.log'
|
|||
|
});
|
|||
|
|
|||
|
logger.remove(combinedLogs);
|
|||
|
```
|
|||
|
|
|||
|
## Adding Custom Transports
|
|||
|
|
|||
|
Adding a custom transport is easy. All you need to do is accept any options
|
|||
|
you need, implement a log() method, and consume it with `winston`.
|
|||
|
|
|||
|
``` js
|
|||
|
const Transport = require('winston-transport');
|
|||
|
const util = require('util');
|
|||
|
|
|||
|
//
|
|||
|
// Inherit from `winston-transport` so you can take advantage
|
|||
|
// of the base functionality and `.exceptions.handle()`.
|
|||
|
//
|
|||
|
module.exports = class YourCustomTransport extends Transport {
|
|||
|
constructor(opts) {
|
|||
|
super(opts);
|
|||
|
//
|
|||
|
// Consume any custom options here. e.g.:
|
|||
|
// - Connection information for databases
|
|||
|
// - Authentication information for APIs (e.g. loggly, papertrail,
|
|||
|
// logentries, etc.).
|
|||
|
//
|
|||
|
}
|
|||
|
|
|||
|
log(info, callback) {
|
|||
|
setImmediate(() => {
|
|||
|
this.emit('logged', info);
|
|||
|
});
|
|||
|
|
|||
|
// Perform the writing to the remote service
|
|||
|
callback();
|
|||
|
}
|
|||
|
};
|
|||
|
```
|
|||
|
|
|||
|
## Exceptions
|
|||
|
|
|||
|
### Handling Uncaught Exceptions with winston
|
|||
|
|
|||
|
With `winston`, it is possible to catch and log `uncaughtException` events
|
|||
|
from your process. With your own logger instance you can enable this behavior
|
|||
|
when it's created or later on in your applications lifecycle:
|
|||
|
|
|||
|
``` js
|
|||
|
const { createLogger, transports } = require('winston');
|
|||
|
|
|||
|
// Enable exception handling when you create your logger.
|
|||
|
const logger = createLogger({
|
|||
|
transports: [
|
|||
|
new transports.File({ filename: 'combined.log' })
|
|||
|
],
|
|||
|
exceptionHandlers: [
|
|||
|
new transports.File({ filename: 'exceptions.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
// Or enable it later on by adding a transport or using `.exceptions.handle`
|
|||
|
const logger = createLogger({
|
|||
|
transports: [
|
|||
|
new transports.File({ filename: 'combined.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
// Call exceptions.handle with a transport to handle exceptions
|
|||
|
logger.exceptions.handle(
|
|||
|
new transports.File({ filename: 'exceptions.log' })
|
|||
|
);
|
|||
|
```
|
|||
|
|
|||
|
If you want to use this feature with the default logger, simply call
|
|||
|
`.exceptions.handle()` with a transport instance.
|
|||
|
|
|||
|
``` js
|
|||
|
//
|
|||
|
// You can add a separate exception logger by passing it to `.exceptions.handle`
|
|||
|
//
|
|||
|
winston.exceptions.handle(
|
|||
|
new winston.transports.File({ filename: 'path/to/exceptions.log' })
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Alternatively you can set `handleExceptions` to true when adding transports
|
|||
|
// to winston.
|
|||
|
//
|
|||
|
winston.add(new winston.transports.File({
|
|||
|
filename: 'path/to/combined.log',
|
|||
|
handleExceptions: true
|
|||
|
}));
|
|||
|
```
|
|||
|
|
|||
|
### To Exit or Not to Exit
|
|||
|
|
|||
|
By default, winston will exit after logging an uncaughtException. If this is
|
|||
|
not the behavior you want, set `exitOnError = false`
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({ exitOnError: false });
|
|||
|
|
|||
|
//
|
|||
|
// or, like this:
|
|||
|
//
|
|||
|
logger.exitOnError = false;
|
|||
|
```
|
|||
|
|
|||
|
When working with custom logger instances, you can pass in separate transports
|
|||
|
to the `exceptionHandlers` property or set `handleExceptions` on any
|
|||
|
transport.
|
|||
|
|
|||
|
##### Example 1
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({
|
|||
|
transports: [
|
|||
|
new winston.transports.File({ filename: 'path/to/combined.log' })
|
|||
|
],
|
|||
|
exceptionHandlers: [
|
|||
|
new winston.transports.File({ filename: 'path/to/exceptions.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
##### Example 2
|
|||
|
|
|||
|
``` js
|
|||
|
const logger = winston.createLogger({
|
|||
|
transports: [
|
|||
|
new winston.transports.Console({
|
|||
|
handleExceptions: true
|
|||
|
})
|
|||
|
],
|
|||
|
exitOnError: false
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
The `exitOnError` option can also be a function to prevent exit on only
|
|||
|
certain types of errors:
|
|||
|
|
|||
|
``` js
|
|||
|
function ignoreEpipe(err) {
|
|||
|
return err.code !== 'EPIPE';
|
|||
|
}
|
|||
|
|
|||
|
const logger = winston.createLogger({ exitOnError: ignoreEpipe });
|
|||
|
|
|||
|
//
|
|||
|
// or, like this:
|
|||
|
//
|
|||
|
logger.exitOnError = ignoreEpipe;
|
|||
|
```
|
|||
|
|
|||
|
## Profiling
|
|||
|
|
|||
|
In addition to logging messages and metadata, `winston` also has a simple
|
|||
|
profiling mechanism implemented for any logger:
|
|||
|
|
|||
|
``` js
|
|||
|
//
|
|||
|
// Start profile of 'test'
|
|||
|
//
|
|||
|
logger.profile('test');
|
|||
|
|
|||
|
setTimeout(function () {
|
|||
|
//
|
|||
|
// Stop profile of 'test'. Logging will now take place:
|
|||
|
// '17 Jan 21:00:00 - info: test duration=1000ms'
|
|||
|
//
|
|||
|
logger.profile('test');
|
|||
|
}, 1000);
|
|||
|
```
|
|||
|
|
|||
|
Also you can start a timer and keep a reference that you can call `.done()``
|
|||
|
on:
|
|||
|
|
|||
|
``` js
|
|||
|
// Returns an object corresponding to a specific timing. When done
|
|||
|
// is called the timer will finish and log the duration. e.g.:
|
|||
|
//
|
|||
|
const profiler = logger.startTimer();
|
|||
|
setTimeout(function () {
|
|||
|
profiler.done({ message: 'Logging message' });
|
|||
|
}, 1000);
|
|||
|
```
|
|||
|
|
|||
|
All profile messages are set to 'info' level by default, and both message and
|
|||
|
metadata are optional. For individual profile messages, you can override the default log level by supplying a metadata object with a `level` property:
|
|||
|
|
|||
|
```js
|
|||
|
logger.profile('test', { level: 'debug' });
|
|||
|
```
|
|||
|
|
|||
|
## Querying Logs
|
|||
|
|
|||
|
`winston` supports querying of logs with Loggly-like options. [See Loggly
|
|||
|
Search API](https://www.loggly.com/docs/api-retrieving-data/). Specifically:
|
|||
|
`File`, `Couchdb`, `Redis`, `Loggly`, `Nssocket`, and `Http`.
|
|||
|
|
|||
|
``` js
|
|||
|
const options = {
|
|||
|
from: new Date() - (24 * 60 * 60 * 1000),
|
|||
|
until: new Date(),
|
|||
|
limit: 10,
|
|||
|
start: 0,
|
|||
|
order: 'desc',
|
|||
|
fields: ['message']
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// Find items logged between today and yesterday.
|
|||
|
//
|
|||
|
logger.query(options, function (err, results) {
|
|||
|
if (err) {
|
|||
|
/* TODO: handle me */
|
|||
|
throw err;
|
|||
|
}
|
|||
|
|
|||
|
console.log(results);
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## Streaming Logs
|
|||
|
Streaming allows you to stream your logs back from your chosen transport.
|
|||
|
|
|||
|
``` js
|
|||
|
//
|
|||
|
// Start at the end.
|
|||
|
//
|
|||
|
winston.stream({ start: -1 }).on('log', function(log) {
|
|||
|
console.log(log);
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
## Further Reading
|
|||
|
|
|||
|
### Using the Default Logger
|
|||
|
|
|||
|
The default logger is accessible through the `winston` module directly. Any
|
|||
|
method that you could call on an instance of a logger is available on the
|
|||
|
default logger:
|
|||
|
|
|||
|
``` js
|
|||
|
const winston = require('winston');
|
|||
|
|
|||
|
winston.log('info', 'Hello distributed log files!');
|
|||
|
winston.info('Hello again distributed logs');
|
|||
|
|
|||
|
winston.level = 'debug';
|
|||
|
winston.log('debug', 'Now my debug messages are written to console!');
|
|||
|
```
|
|||
|
|
|||
|
By default, no transports are set on the default logger. You must
|
|||
|
add or remove transports via the `add()` and `remove()` methods:
|
|||
|
|
|||
|
``` js
|
|||
|
const files = new winston.transports.File({ filename: 'combined.log' });
|
|||
|
const console = new winston.transports.Console();
|
|||
|
|
|||
|
winston.add(console);
|
|||
|
winston.add(files);
|
|||
|
winston.remove(console);
|
|||
|
```
|
|||
|
|
|||
|
Or do it with one call to configure():
|
|||
|
|
|||
|
``` js
|
|||
|
winston.configure({
|
|||
|
transports: [
|
|||
|
new winston.transports.File({ filename: 'somefile.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
For more documentation about working with each individual transport supported
|
|||
|
by `winston` see the [`winston` Transports](docs/transports.md) document.
|
|||
|
|
|||
|
### Awaiting logs to be written in `winston`
|
|||
|
|
|||
|
Often it is useful to wait for your logs to be written before exiting the
|
|||
|
process. Each instance of `winston.Logger` is also a [Node.js stream]. A
|
|||
|
`finish` event will be raised when all logs have flushed to all transports
|
|||
|
after the stream has been ended.
|
|||
|
|
|||
|
``` js
|
|||
|
const transport = new winston.transports.Console();
|
|||
|
const logger = winston.createLogger({
|
|||
|
transports: [transport]
|
|||
|
});
|
|||
|
|
|||
|
logger.on('finish', function (info) {
|
|||
|
// All `info` log messages has now been logged
|
|||
|
});
|
|||
|
|
|||
|
logger.info('CHILL WINSTON!', { seriously: true });
|
|||
|
logger.end();
|
|||
|
```
|
|||
|
|
|||
|
It is also worth mentioning that the logger also emits an 'error' event which
|
|||
|
you should handle or suppress if you don't want unhandled exceptions:
|
|||
|
|
|||
|
``` js
|
|||
|
//
|
|||
|
// Handle errors
|
|||
|
//
|
|||
|
logger.on('error', function (err) { /* Do Something */ });
|
|||
|
|
|||
|
//
|
|||
|
// Or just suppress them.
|
|||
|
//
|
|||
|
logger.emitErrs = false;
|
|||
|
```
|
|||
|
|
|||
|
### Working with multiple Loggers in winston
|
|||
|
|
|||
|
Often in larger, more complex, applications it is necessary to have multiple
|
|||
|
logger instances with different settings. Each logger is responsible for a
|
|||
|
different feature area (or category). This is exposed in `winston` in two
|
|||
|
ways: through `winston.loggers` and instances of `winston.Container`. In fact,
|
|||
|
`winston.loggers` is just a predefined instance of `winston.Container`:
|
|||
|
|
|||
|
``` js
|
|||
|
const winston = require('winston');
|
|||
|
const { format } = winston;
|
|||
|
const { combine, label, json } = format;
|
|||
|
|
|||
|
//
|
|||
|
// Configure the logger for `category1`
|
|||
|
//
|
|||
|
winston.loggers.add('category1', {
|
|||
|
format: combine(
|
|||
|
label({ label: 'category one' }),
|
|||
|
json()
|
|||
|
),
|
|||
|
transports: [
|
|||
|
new winston.transports.Console({ level: 'silly' }),
|
|||
|
new winston.transports.File({ filename: 'somefile.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
//
|
|||
|
// Configure the logger for `category2`
|
|||
|
//
|
|||
|
winston.loggers.add('category2', {
|
|||
|
format: combine(
|
|||
|
label({ label: 'category two' }),
|
|||
|
json()
|
|||
|
),
|
|||
|
transports: [
|
|||
|
new winston.transports.Http({ host: 'localhost', port:8080 })
|
|||
|
]
|
|||
|
});
|
|||
|
```
|
|||
|
|
|||
|
Now that your loggers are setup, you can require winston _in any file in your
|
|||
|
application_ and access these pre-configured loggers:
|
|||
|
|
|||
|
``` js
|
|||
|
const winston = require('winston');
|
|||
|
|
|||
|
//
|
|||
|
// Grab your preconfigured loggers
|
|||
|
//
|
|||
|
const category1 = winston.loggers.get('category1');
|
|||
|
const category2 = winston.loggers.get('category2');
|
|||
|
|
|||
|
category1.info('logging to file and console transports');
|
|||
|
category2.info('logging to http transport');
|
|||
|
```
|
|||
|
|
|||
|
If you prefer to manage the `Container` yourself, you can simply instantiate one:
|
|||
|
|
|||
|
``` js
|
|||
|
const winston = require('winston');
|
|||
|
const { format } = winston;
|
|||
|
const { combine, json } = format;
|
|||
|
|
|||
|
const container = new winston.Container();
|
|||
|
|
|||
|
container.add('category1', {
|
|||
|
format: combine(
|
|||
|
label({ label: 'category one' }),
|
|||
|
json()
|
|||
|
),
|
|||
|
transports: [
|
|||
|
new winston.transports.Console({ level: 'silly' }),
|
|||
|
new winston.transports.File({ filename: 'somefile.log' })
|
|||
|
]
|
|||
|
});
|
|||
|
|
|||
|
const category1 = container.get('category1');
|
|||
|
category1.info('logging to file and console transports');
|
|||
|
```
|
|||
|
|
|||
|
## Installation
|
|||
|
|
|||
|
``` bash
|
|||
|
npm install winston
|
|||
|
```
|
|||
|
|
|||
|
``` bash
|
|||
|
yarn add winston
|
|||
|
```
|
|||
|
|
|||
|
## Run Tests
|
|||
|
|
|||
|
All of the winston tests are written with [`mocha`][mocha], [`nyc`][nyc], and
|
|||
|
[`assume`][assume]. They can be run with `npm`.
|
|||
|
|
|||
|
``` bash
|
|||
|
npm test
|
|||
|
```
|
|||
|
|
|||
|
#### Author: [Charlie Robbins]
|
|||
|
#### Contributors: [Jarrett Cruger], [David Hyde], [Chris Alderson]
|
|||
|
|
|||
|
[Transports]: #transports
|
|||
|
[Logging levels]: #logging-levels
|
|||
|
[Formats]: #formats
|
|||
|
[Using custom logging levels]: #using-custom-logging-levels
|
|||
|
[Adding Custom Transports]: #adding-custom-transports
|
|||
|
[core transports]: docs/transports.md#winston-core
|
|||
|
[additional transports]: docs/transports.md#additional-transports
|
|||
|
|
|||
|
[RFC5424]: https://tools.ietf.org/html/rfc5424
|
|||
|
[util.format]: https://nodejs.org/dist/latest/docs/api/util.html#util_util_format_format_args
|
|||
|
[mocha]: https://mochajs.org
|
|||
|
[nyc]: https://github.com/istanbuljs/nyc
|
|||
|
[assume]: https://github.com/bigpipe/assume
|
|||
|
[logform]: https://github.com/winstonjs/logform#readme
|
|||
|
|
|||
|
[Read the `winston@2.x` documentation]: https://github.com/winstonjs/winston/tree/2.x
|
|||
|
|
|||
|
[quick-example]: https://github.com/winstonjs/winston/blob/master/examples/quick-start.js
|
|||
|
[examples]: https://github.com/winstonjs/winston/tree/master/examples
|
|||
|
|
|||
|
[Charlie Robbins]: http://github.com/indexzero
|
|||
|
[Jarrett Cruger]: https://github.com/jcrugzz
|
|||
|
[David Hyde]: https://github.com/dabh
|
|||
|
[Chris Alderson]: https://github.com/chrisalderson
|