1 | # winston
|
2 |
|
3 | A logger for just about everything.
|
4 |
|
5 | [![Version npm](https://img.shields.io/npm/v/winston.svg?style=flat-square)](https://www.npmjs.com/package/winston)
|
6 | [![npm Downloads](https://img.shields.io/npm/dm/winston.svg?style=flat-square)](https://npmcharts.com/compare/winston?minimal=true)
|
7 | [![build status](https://github.com/winstonjs/winston/actions/workflows/ci.yml/badge.svg)](https://github.com/winstonjs/winston/actions/workflows/ci.yml)
|
8 | [![coverage status](https://coveralls.io/repos/github/winstonjs/winston/badge.svg?branch=master)](https://coveralls.io/github/winstonjs/winston?branch=master)
|
9 |
|
10 | [![NPM](https://nodei.co/npm/winston.png?downloads=true&downloadRank=true)](https://nodei.co/npm/winston/)
|
11 |
|
12 | ## winston@3
|
13 |
|
14 | See the [Upgrade Guide](UPGRADE-3.0.md) for more information. Bug reports and
|
15 | PRs welcome!
|
16 |
|
17 | ## Looking for `winston@2.x` documentation?
|
18 |
|
19 | Please note that the documentation below is for `winston@3`.
|
20 | [Read the `winston@2.x` documentation].
|
21 |
|
22 | ## Motivation
|
23 |
|
24 | `winston` is designed to be a simple and universal logging library with
|
25 | support for multiple transports. A transport is essentially a storage device
|
26 | for your logs. Each `winston` logger can have multiple transports (see:
|
27 | [Transports]) configured at different levels (see: [Logging levels]). For
|
28 | example, one may want error logs to be stored in a persistent remote location
|
29 | (like a database), but all logs output to the console or a local file.
|
30 |
|
31 | `winston` aims to decouple parts of the logging process to make it more
|
32 | flexible and extensible. Attention is given to supporting flexibility in log
|
33 | formatting (see: [Formats]) & levels (see: [Using custom logging levels]), and
|
34 | ensuring those APIs decoupled from the implementation of transport logging
|
35 | (i.e. how the logs are stored / indexed, see: [Adding Custom Transports]) to
|
36 | the API that they exposed to the programmer.
|
37 |
|
38 | ## Quick Start
|
39 |
|
40 | TL;DR? Check out the [quick start example][quick-example] in `./examples/`.
|
41 | There are a number of other examples in [`./examples/*.js`][examples].
|
42 | Don't see an example you think should be there? Submit a pull request
|
43 | to add it!
|
44 |
|
45 | ## Usage
|
46 |
|
47 | The recommended way to use `winston` is to create your own logger. The
|
48 | simplest way to do this is using `winston.createLogger`:
|
49 |
|
50 | ``` js
|
51 | const winston = require('winston');
|
52 |
|
53 | const logger = winston.createLogger({
|
54 | level: 'info',
|
55 | format: winston.format.json(),
|
56 | defaultMeta: { service: 'user-service' },
|
57 | transports: [
|
58 | //
|
59 | // - Write all logs with importance level of `error` or less to `error.log`
|
60 | // - Write all logs with importance level of `info` or less to `combined.log`
|
61 | //
|
62 | new winston.transports.File({ filename: 'error.log', level: 'error' }),
|
63 | new winston.transports.File({ filename: 'combined.log' }),
|
64 | ],
|
65 | });
|
66 |
|
67 | //
|
68 | // If we're not in production then log to the `console` with the format:
|
69 | // `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
|
70 | //
|
71 | if (process.env.NODE_ENV !== 'production') {
|
72 | logger.add(new winston.transports.Console({
|
73 | format: winston.format.simple(),
|
74 | }));
|
75 | }
|
76 | ```
|
77 |
|
78 | You may also log directly via the default logger exposed by
|
79 | `require('winston')`, but this merely intended to be a convenient shared
|
80 | logger to use throughout your application if you so choose.
|
81 |
|
82 | ## Table of contents
|
83 |
|
84 | * [Motivation](#motivation)
|
85 | * [Quick Start](#quick-start)
|
86 | * [Usage](#usage)
|
87 | * [Table of Contents](#table-of-contents)
|
88 | * [Logging](#logging)
|
89 | * [Creating your logger](#creating-your-own-logger)
|
90 | * [Streams, `objectMode`, and `info` objects](#streams-objectmode-and-info-objects)
|
91 | * [Formats]
|
92 | * [Combining formats](#combining-formats)
|
93 | * [String interpolation](#string-interpolation)
|
94 | * [Filtering `info` Objects](#filtering-info-objects)
|
95 | * [Creating custom formats](#creating-custom-formats)
|
96 | * [Logging levels]
|
97 | * [Using logging levels](#using-logging-levels)
|
98 | * [Using custom logging levels](#using-custom-logging-levels)
|
99 | * [Transports]
|
100 | * [Multiple transports of the same type](#multiple-transports-of-the-same-type)
|
101 | * [Adding Custom Transports](#adding-custom-transports)
|
102 | * [Common Transport options](#common-transport-options)
|
103 | * [Exceptions](#exceptions)
|
104 | * [Handling Uncaught Exceptions with winston](#handling-uncaught-exceptions-with-winston)
|
105 | * [To Exit or Not to Exit](#to-exit-or-not-to-exit)
|
106 | * [Rejections](#rejections)
|
107 | * [Handling Uncaught Promise Rejections with winston](#handling-uncaught-promise-rejections-with-winston)
|
108 | * [Profiling](#profiling)
|
109 | * [Streaming Logs](#streaming-logs)
|
110 | * [Querying Logs](#querying-logs)
|
111 | * [Further Reading](#further-reading)
|
112 | * [Using the default logger](#using-the-default-logger)
|
113 | * [Awaiting logs to be written in `winston`](#awaiting-logs-to-be-written-in-winston)
|
114 | * [Working with multiple Loggers in `winston`](#working-with-multiple-loggers-in-winston)
|
115 | * [Installation](#installation)
|
116 | * [Run Tests](#run-tests)
|
117 |
|
118 | ## Logging
|
119 |
|
120 | Logging levels in `winston` conform to the severity ordering specified by
|
121 | [RFC5424]: _severity of all levels is assumed to be numerically **ascending**
|
122 | from most important to least important._
|
123 |
|
124 | ``` js
|
125 | const levels = {
|
126 | error: 0,
|
127 | warn: 1,
|
128 | info: 2,
|
129 | http: 3,
|
130 | verbose: 4,
|
131 | debug: 5,
|
132 | silly: 6
|
133 | };
|
134 | ```
|
135 |
|
136 | ### Creating your own Logger
|
137 | You get started by creating a logger using `winston.createLogger`:
|
138 |
|
139 | ``` js
|
140 | const logger = winston.createLogger({
|
141 | transports: [
|
142 | new winston.transports.Console(),
|
143 | new winston.transports.File({ filename: 'combined.log' })
|
144 | ]
|
145 | });
|
146 | ```
|
147 |
|
148 | A logger accepts the following parameters:
|
149 |
|
150 | | Name | Default | Description |
|
151 | | ------------- | --------------------------- | --------------- |
|
152 | | `level` | `'info'` | Log only if [`info.level`](#streams-objectmode-and-info-objects) less than or equal to this level |
|
153 | | `levels` | `winston.config.npm.levels` | Levels (and colors) representing log priorities |
|
154 | | `format` | `winston.format.json` | Formatting for `info` messages (see: [Formats]) |
|
155 | | `transports` | `[]` _(No transports)_ | Set of logging targets for `info` messages |
|
156 | | `exitOnError` | `true` | If false, handled exceptions will not cause `process.exit` |
|
157 | | `silent` | `false` | If true, all logs are suppressed |
|
158 |
|
159 | The levels provided to `createLogger` will be defined as convenience methods
|
160 | on the `logger` returned.
|
161 |
|
162 | ``` js
|
163 | //
|
164 | // Logging
|
165 | //
|
166 | logger.log({
|
167 | level: 'info',
|
168 | message: 'Hello distributed log files!'
|
169 | });
|
170 |
|
171 | logger.info('Hello again distributed logs');
|
172 | ```
|
173 |
|
174 | You can add or remove transports from the `logger` once it has been provided
|
175 | to you from `winston.createLogger`:
|
176 |
|
177 | ``` js
|
178 | const files = new winston.transports.File({ filename: 'combined.log' });
|
179 | const console = new winston.transports.Console();
|
180 |
|
181 | logger
|
182 | .clear() // Remove all transports
|
183 | .add(console) // Add console transport
|
184 | .add(files) // Add file transport
|
185 | .remove(console); // Remove console transport
|
186 | ```
|
187 |
|
188 | You can also wholesale reconfigure a `winston.Logger` instance using the
|
189 | `configure` method:
|
190 |
|
191 | ``` js
|
192 | const logger = winston.createLogger({
|
193 | level: 'info',
|
194 | transports: [
|
195 | new winston.transports.Console(),
|
196 | new winston.transports.File({ filename: 'combined.log' })
|
197 | ]
|
198 | });
|
199 |
|
200 | //
|
201 | // Replaces the previous transports with those in the
|
202 | // new configuration wholesale.
|
203 | //
|
204 | const DailyRotateFile = require('winston-daily-rotate-file');
|
205 | logger.configure({
|
206 | level: 'verbose',
|
207 | transports: [
|
208 | new DailyRotateFile(opts)
|
209 | ]
|
210 | });
|
211 | ```
|
212 |
|
213 | ### Creating child loggers
|
214 |
|
215 | You can create child loggers from existing loggers to pass metadata overrides:
|
216 |
|
217 | ``` js
|
218 | const logger = winston.createLogger({
|
219 | transports: [
|
220 | new winston.transports.Console(),
|
221 | ]
|
222 | });
|
223 |
|
224 | const childLogger = logger.child({ requestId: '451' });
|
225 | ```
|
226 |
|
227 | ### Streams, `objectMode`, and `info` objects
|
228 |
|
229 | In `winston`, both `Logger` and `Transport` instances are treated as
|
230 | [`objectMode`](https://nodejs.org/api/stream.html#stream_object_mode)
|
231 | streams that accept an `info` object.
|
232 |
|
233 | The `info` parameter provided to a given format represents a single log
|
234 | message. The object itself is mutable. Every `info` must have at least the
|
235 | `level` and `message` properties:
|
236 |
|
237 | ``` js
|
238 | const info = {
|
239 | level: 'info', // Level of the logging message
|
240 | message: 'Hey! Log something?' // Descriptive message being logged.
|
241 | };
|
242 | ```
|
243 |
|
244 | Properties **besides level and message** are considered as "`meta`". i.e.:
|
245 |
|
246 | ``` js
|
247 | const { level, message, ...meta } = info;
|
248 | ```
|
249 |
|
250 | Several of the formats in `logform` itself add additional properties:
|
251 |
|
252 | | Property | Format added by | Description |
|
253 | | ----------- | --------------- | ----------- |
|
254 | | `splat` | `splat()` | String interpolation splat for `%d %s`-style messages. |
|
255 | | `timestamp` | `timestamp()` | timestamp the message was received. |
|
256 | | `label` | `label()` | Custom label associated with each message. |
|
257 | | `ms` | `ms()` | Number of milliseconds since the previous log message. |
|
258 |
|
259 | As a consumer you may add whatever properties you wish – _internal state is
|
260 | maintained by `Symbol` properties:_
|
261 |
|
262 | - `Symbol.for('level')` _**(READ-ONLY)**:_ equal to `level` property.
|
263 | **Is treated as immutable by all code.**
|
264 | - `Symbol.for('message'):` complete string message set by "finalizing formats":
|
265 | - `json`
|
266 | - `logstash`
|
267 | - `printf`
|
268 | - `prettyPrint`
|
269 | - `simple`
|
270 | - `Symbol.for('splat')`: additional string interpolation arguments. _Used
|
271 | exclusively by `splat()` format._
|
272 |
|
273 | These Symbols are stored in another package: `triple-beam` so that all
|
274 | consumers of `logform` can have the same Symbol reference. i.e.:
|
275 |
|
276 | ``` js
|
277 | const { LEVEL, MESSAGE, SPLAT } = require('triple-beam');
|
278 |
|
279 | console.log(LEVEL === Symbol.for('level'));
|
280 | // true
|
281 |
|
282 | console.log(MESSAGE === Symbol.for('message'));
|
283 | // true
|
284 |
|
285 | console.log(SPLAT === Symbol.for('splat'));
|
286 | // true
|
287 | ```
|
288 |
|
289 | > **NOTE:** any `{ message }` property in a `meta` object provided will
|
290 | > automatically be concatenated to any `msg` already provided: For
|
291 | > example the below will concatenate 'world' onto 'hello':
|
292 | >
|
293 | > ``` js
|
294 | > logger.log('error', 'hello', { message: 'world' });
|
295 | > logger.info('hello', { message: 'world' });
|
296 | > ```
|
297 |
|
298 | ## Formats
|
299 |
|
300 | Formats in `winston` can be accessed from `winston.format`. They are
|
301 | implemented in [`logform`](https://github.com/winstonjs/logform), a separate
|
302 | module from `winston`. This allows flexibility when writing your own transports
|
303 | in case you wish to include a default format with your transport.
|
304 |
|
305 | In modern versions of `node` template strings are very performant and are the
|
306 | recommended way for doing most end-user formatting. If you want to bespoke
|
307 | format your logs, `winston.format.printf` is for you:
|
308 |
|
309 | ``` js
|
310 | const { createLogger, format, transports } = require('winston');
|
311 | const { combine, timestamp, label, printf } = format;
|
312 |
|
313 | const myFormat = printf(({ level, message, label, timestamp }) => {
|
314 | return `${timestamp} [${label}] ${level}: ${message}`;
|
315 | });
|
316 |
|
317 | const logger = createLogger({
|
318 | format: combine(
|
319 | label({ label: 'right meow!' }),
|
320 | timestamp(),
|
321 | myFormat
|
322 | ),
|
323 | transports: [new transports.Console()]
|
324 | });
|
325 | ```
|
326 |
|
327 | To see what built-in formats are available and learn more about creating your
|
328 | own custom logging formats, see [`logform`][logform].
|
329 |
|
330 | ### Combining formats
|
331 |
|
332 | Any number of formats may be combined into a single format using
|
333 | `format.combine`. Since `format.combine` takes no `opts`, as a convenience it
|
334 | returns pre-created instance of the combined format.
|
335 |
|
336 | ``` js
|
337 | const { createLogger, format, transports } = require('winston');
|
338 | const { combine, timestamp, label, prettyPrint } = format;
|
339 |
|
340 | const logger = createLogger({
|
341 | format: combine(
|
342 | label({ label: 'right meow!' }),
|
343 | timestamp(),
|
344 | prettyPrint()
|
345 | ),
|
346 | transports: [new transports.Console()]
|
347 | })
|
348 |
|
349 | logger.log({
|
350 | level: 'info',
|
351 | message: 'What time is the testing at?'
|
352 | });
|
353 | // Outputs:
|
354 | // { level: 'info',
|
355 | // message: 'What time is the testing at?',
|
356 | // label: 'right meow!',
|
357 | // timestamp: '2017-09-30T03:57:26.875Z' }
|
358 | ```
|
359 |
|
360 | ### String interpolation
|
361 |
|
362 | The `log` method provides the string interpolation using [util.format]. **It
|
363 | must be enabled using `format.splat()`.**
|
364 |
|
365 | Below is an example that defines a format with string interpolation of
|
366 | messages using `format.splat` and then serializes the entire `info` message
|
367 | using `format.simple`.
|
368 |
|
369 | ``` js
|
370 | const { createLogger, format, transports } = require('winston');
|
371 | const logger = createLogger({
|
372 | format: format.combine(
|
373 | format.splat(),
|
374 | format.simple()
|
375 | ),
|
376 | transports: [new transports.Console()]
|
377 | });
|
378 |
|
379 | // info: test message my string {}
|
380 | logger.log('info', 'test message %s', 'my string');
|
381 |
|
382 | // info: test message 123 {}
|
383 | logger.log('info', 'test message %d', 123);
|
384 |
|
385 | // info: test message first second {number: 123}
|
386 | logger.log('info', 'test message %s, %s', 'first', 'second', { number: 123 });
|
387 | ```
|
388 |
|
389 | ### Filtering `info` Objects
|
390 |
|
391 | If you wish to filter out a given `info` Object completely when logging then
|
392 | simply return a falsey value.
|
393 |
|
394 | ``` js
|
395 | const { createLogger, format, transports } = require('winston');
|
396 |
|
397 | // Ignore log messages if they have { private: true }
|
398 | const ignorePrivate = format((info, opts) => {
|
399 | if (info.private) { return false; }
|
400 | return info;
|
401 | });
|
402 |
|
403 | const logger = createLogger({
|
404 | format: format.combine(
|
405 | ignorePrivate(),
|
406 | format.json()
|
407 | ),
|
408 | transports: [new transports.Console()]
|
409 | });
|
410 |
|
411 | // Outputs: {"level":"error","message":"Public error to share"}
|
412 | logger.log({
|
413 | level: 'error',
|
414 | message: 'Public error to share'
|
415 | });
|
416 |
|
417 | // Messages with { private: true } will not be written when logged.
|
418 | logger.log({
|
419 | private: true,
|
420 | level: 'error',
|
421 | message: 'This is super secret - hide it.'
|
422 | });
|
423 | ```
|
424 |
|
425 | Use of `format.combine` will respect any falsey values return and stop
|
426 | evaluation of later formats in the series. For example:
|
427 |
|
428 | ``` js
|
429 | const { format } = require('winston');
|
430 | const { combine, timestamp, label } = format;
|
431 |
|
432 | const willNeverThrow = format.combine(
|
433 | format(info => { return false })(), // Ignores everything
|
434 | format(info => { throw new Error('Never reached') })()
|
435 | );
|
436 | ```
|
437 |
|
438 | ### Creating custom formats
|
439 |
|
440 | Formats are prototypal objects (i.e. class instances) that define a single
|
441 | method: `transform(info, opts)` and return the mutated `info`:
|
442 |
|
443 | - `info`: an object representing the log message.
|
444 | - `opts`: setting specific to the current instance of the format.
|
445 |
|
446 | They are expected to return one of two things:
|
447 |
|
448 | - **An `info` Object** representing the modified `info` argument. Object
|
449 | references need not be preserved if immutability is preferred. All current
|
450 | built-in formats consider `info` mutable, but [immutablejs] is being
|
451 | considered for future releases.
|
452 | - **A falsey value** indicating that the `info` argument should be ignored by the
|
453 | caller. (See: [Filtering `info` Objects](#filtering-info-objects)) below.
|
454 |
|
455 | `winston.format` is designed to be as simple as possible. To define a new
|
456 | format simple pass it a `transform(info, opts)` function to get a new
|
457 | `Format`.
|
458 |
|
459 | The named `Format` returned can be used to create as many copies of the given
|
460 | `Format` as desired:
|
461 |
|
462 | ``` js
|
463 | const { format } = require('winston');
|
464 |
|
465 | const volume = format((info, opts) => {
|
466 | if (opts.yell) {
|
467 | info.message = info.message.toUpperCase();
|
468 | } else if (opts.whisper) {
|
469 | info.message = info.message.toLowerCase();
|
470 | }
|
471 |
|
472 | return info;
|
473 | });
|
474 |
|
475 | // `volume` is now a function that returns instances of the format.
|
476 | const scream = volume({ yell: true });
|
477 | console.dir(scream.transform({
|
478 | level: 'info',
|
479 | message: `sorry for making you YELL in your head!`
|
480 | }, scream.options));
|
481 | // {
|
482 | // level: 'info'
|
483 | // message: 'SORRY FOR MAKING YOU YELL IN YOUR HEAD!'
|
484 | // }
|
485 |
|
486 | // `volume` can be used multiple times to create different formats.
|
487 | const whisper = volume({ whisper: true });
|
488 | console.dir(whisper.transform({
|
489 | level: 'info',
|
490 | message: `WHY ARE THEY MAKING US YELL SO MUCH!`
|
491 | }, whisper.options));
|
492 | // {
|
493 | // level: 'info'
|
494 | // message: 'why are they making us yell so much!'
|
495 | // }
|
496 | ```
|
497 |
|
498 | ## Logging Levels
|
499 |
|
500 | Logging levels in `winston` conform to the severity ordering specified by
|
501 | [RFC5424]: _severity of all levels is assumed to be numerically **ascending**
|
502 | from most important to least important._
|
503 |
|
504 | Each `level` is given a specific integer priority. The higher the priority the
|
505 | more important the message is considered to be, and the lower the
|
506 | corresponding integer priority. For example, as specified exactly in RFC5424
|
507 | the `syslog` levels are prioritized from 0 to 7 (highest to lowest).
|
508 |
|
509 | ```js
|
510 | {
|
511 | emerg: 0,
|
512 | alert: 1,
|
513 | crit: 2,
|
514 | error: 3,
|
515 | warning: 4,
|
516 | notice: 5,
|
517 | info: 6,
|
518 | debug: 7
|
519 | }
|
520 | ```
|
521 |
|
522 | Similarly, `npm` logging levels are prioritized from 0 to 6 (highest to
|
523 | lowest):
|
524 |
|
525 | ``` js
|
526 | {
|
527 | error: 0,
|
528 | warn: 1,
|
529 | info: 2,
|
530 | http: 3,
|
531 | verbose: 4,
|
532 | debug: 5,
|
533 | silly: 6
|
534 | }
|
535 | ```
|
536 |
|
537 | If you do not explicitly define the levels that `winston` should use, the
|
538 | `npm` levels above will be used.
|
539 |
|
540 | ### Using Logging Levels
|
541 |
|
542 | Setting the level for your logging message can be accomplished in one of two
|
543 | ways. You can pass a string representing the logging level to the log() method
|
544 | or use the level specified methods defined on every winston Logger.
|
545 |
|
546 | ``` js
|
547 | //
|
548 | // Any logger instance
|
549 | //
|
550 | logger.log('silly', "127.0.0.1 - there's no place like home");
|
551 | logger.log('debug', "127.0.0.1 - there's no place like home");
|
552 | logger.log('verbose', "127.0.0.1 - there's no place like home");
|
553 | logger.log('info', "127.0.0.1 - there's no place like home");
|
554 | logger.log('warn', "127.0.0.1 - there's no place like home");
|
555 | logger.log('error', "127.0.0.1 - there's no place like home");
|
556 | logger.info("127.0.0.1 - there's no place like home");
|
557 | logger.warn("127.0.0.1 - there's no place like home");
|
558 | logger.error("127.0.0.1 - there's no place like home");
|
559 |
|
560 | //
|
561 | // Default logger
|
562 | //
|
563 | winston.log('info', "127.0.0.1 - there's no place like home");
|
564 | winston.info("127.0.0.1 - there's no place like home");
|
565 | ```
|
566 |
|
567 | `winston` allows you to define a `level` property on each transport which
|
568 | specifies the **maximum** level of messages that a transport should log. For
|
569 | example, using the `syslog` levels you could log only `error` messages to the
|
570 | console and everything `info` and below to a file (which includes `error`
|
571 | messages):
|
572 |
|
573 | ``` js
|
574 | const logger = winston.createLogger({
|
575 | levels: winston.config.syslog.levels,
|
576 | transports: [
|
577 | new winston.transports.Console({ level: 'error' }),
|
578 | new winston.transports.File({
|
579 | filename: 'combined.log',
|
580 | level: 'info'
|
581 | })
|
582 | ]
|
583 | });
|
584 | ```
|
585 |
|
586 | You may also dynamically change the log level of a transport:
|
587 |
|
588 | ``` js
|
589 | const transports = {
|
590 | console: new winston.transports.Console({ level: 'warn' }),
|
591 | file: new winston.transports.File({ filename: 'combined.log', level: 'error' })
|
592 | };
|
593 |
|
594 | const logger = winston.createLogger({
|
595 | transports: [
|
596 | transports.console,
|
597 | transports.file
|
598 | ]
|
599 | });
|
600 |
|
601 | logger.info('Will not be logged in either transport!');
|
602 | transports.console.level = 'info';
|
603 | transports.file.level = 'info';
|
604 | logger.info('Will be logged in both transports!');
|
605 | ```
|
606 |
|
607 | `winston` supports customizable logging levels, defaulting to npm style
|
608 | logging levels. Levels must be specified at the time of creating your logger.
|
609 |
|
610 | ### Using Custom Logging Levels
|
611 |
|
612 | In addition to the predefined `npm`, `syslog`, and `cli` levels available in
|
613 | `winston`, you can also choose to define your own:
|
614 |
|
615 | ``` js
|
616 | const myCustomLevels = {
|
617 | levels: {
|
618 | foo: 0,
|
619 | bar: 1,
|
620 | baz: 2,
|
621 | foobar: 3
|
622 | },
|
623 | colors: {
|
624 | foo: 'blue',
|
625 | bar: 'green',
|
626 | baz: 'yellow',
|
627 | foobar: 'red'
|
628 | }
|
629 | };
|
630 |
|
631 | const customLevelLogger = winston.createLogger({
|
632 | levels: myCustomLevels.levels
|
633 | });
|
634 |
|
635 | customLevelLogger.foobar('some foobar level-ed message');
|
636 | ```
|
637 |
|
638 | Although there is slight repetition in this data structure, it enables simple
|
639 | encapsulation if you do not want to have colors. If you do wish to have
|
640 | colors, in addition to passing the levels to the Logger itself, you must make
|
641 | winston aware of them:
|
642 |
|
643 | ``` js
|
644 | winston.addColors(myCustomLevels.colors);
|
645 | ```
|
646 |
|
647 | This enables loggers using the `colorize` formatter to appropriately color and style
|
648 | the output of custom levels.
|
649 |
|
650 | Additionally, you can also change background color and font style.
|
651 | For example,
|
652 | ``` js
|
653 | baz: 'italic yellow',
|
654 | foobar: 'bold red cyanBG'
|
655 | ```
|
656 |
|
657 | Possible options are below.
|
658 |
|
659 | * Font styles: `bold`, `dim`, `italic`, `underline`, `inverse`, `hidden`,
|
660 | `strikethrough`.
|
661 |
|
662 | * Font foreground colors: `black`, `red`, `green`, `yellow`, `blue`, `magenta`,
|
663 | `cyan`, `white`, `gray`, `grey`.
|
664 |
|
665 | * Background colors: `blackBG`, `redBG`, `greenBG`, `yellowBG`, `blueBG`
|
666 | `magentaBG`, `cyanBG`, `whiteBG`
|
667 |
|
668 | ### Colorizing Standard logging levels
|
669 |
|
670 | To colorize the standard logging level add
|
671 | ```js
|
672 | winston.format.combine(
|
673 | winston.format.colorize(),
|
674 | winston.format.json()
|
675 | );
|
676 | ```
|
677 | 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.
|
678 |
|
679 | ## Transports
|
680 |
|
681 | There are several [core transports] included in `winston`, which leverage the
|
682 | built-in networking and file I/O offered by Node.js core. In addition, there
|
683 | are [additional transports] written by members of the community.
|
684 |
|
685 | ## Multiple transports of the same type
|
686 |
|
687 | It is possible to use multiple transports of the same type e.g.
|
688 | `winston.transports.File` when you construct the transport.
|
689 |
|
690 | ``` js
|
691 | const logger = winston.createLogger({
|
692 | transports: [
|
693 | new winston.transports.File({
|
694 | filename: 'combined.log',
|
695 | level: 'info'
|
696 | }),
|
697 | new winston.transports.File({
|
698 | filename: 'errors.log',
|
699 | level: 'error'
|
700 | })
|
701 | ]
|
702 | });
|
703 | ```
|
704 |
|
705 | If you later want to remove one of these transports you can do so by using the
|
706 | transport itself. e.g.:
|
707 |
|
708 | ``` js
|
709 | const combinedLogs = logger.transports.find(transport => {
|
710 | return transport.filename === 'combined.log'
|
711 | });
|
712 |
|
713 | logger.remove(combinedLogs);
|
714 | ```
|
715 |
|
716 | ## Adding Custom Transports
|
717 |
|
718 | Adding a custom transport is easy. All you need to do is accept any options
|
719 | you need, implement a log() method, and consume it with `winston`.
|
720 |
|
721 | ``` js
|
722 | const Transport = require('winston-transport');
|
723 | const util = require('util');
|
724 |
|
725 | //
|
726 | // Inherit from `winston-transport` so you can take advantage
|
727 | // of the base functionality and `.exceptions.handle()`.
|
728 | //
|
729 | module.exports = class YourCustomTransport extends Transport {
|
730 | constructor(opts) {
|
731 | super(opts);
|
732 | //
|
733 | // Consume any custom options here. e.g.:
|
734 | // - Connection information for databases
|
735 | // - Authentication information for APIs (e.g. loggly, papertrail,
|
736 | // logentries, etc.).
|
737 | //
|
738 | }
|
739 |
|
740 | log(info, callback) {
|
741 | setImmediate(() => {
|
742 | this.emit('logged', info);
|
743 | });
|
744 |
|
745 | // Perform the writing to the remote service
|
746 | callback();
|
747 | }
|
748 | };
|
749 | ```
|
750 |
|
751 | ## Common Transport options
|
752 |
|
753 | As every transport inherits from [winston-transport], it's possible to set
|
754 | a custom format and a custom log level on each transport separately:
|
755 |
|
756 | ``` js
|
757 | const logger = winston.createLogger({
|
758 | transports: [
|
759 | new winston.transports.File({
|
760 | filename: 'error.log',
|
761 | level: 'error',
|
762 | format: winston.format.json()
|
763 | }),
|
764 | new transports.Http({
|
765 | level: 'warn',
|
766 | format: winston.format.json()
|
767 | }),
|
768 | new transports.Console({
|
769 | level: 'info',
|
770 | format: winston.format.combine(
|
771 | winston.format.colorize(),
|
772 | winston.format.simple()
|
773 | )
|
774 | })
|
775 | ]
|
776 | });
|
777 | ```
|
778 |
|
779 | ## Exceptions
|
780 |
|
781 | ### Handling Uncaught Exceptions with winston
|
782 |
|
783 | With `winston`, it is possible to catch and log `uncaughtException` events
|
784 | from your process. With your own logger instance you can enable this behavior
|
785 | when it's created or later on in your applications lifecycle:
|
786 |
|
787 | ``` js
|
788 | const { createLogger, transports } = require('winston');
|
789 |
|
790 | // Enable exception handling when you create your logger.
|
791 | const logger = createLogger({
|
792 | transports: [
|
793 | new transports.File({ filename: 'combined.log' })
|
794 | ],
|
795 | exceptionHandlers: [
|
796 | new transports.File({ filename: 'exceptions.log' })
|
797 | ]
|
798 | });
|
799 |
|
800 | // Or enable it later on by adding a transport or using `.exceptions.handle`
|
801 | const logger = createLogger({
|
802 | transports: [
|
803 | new transports.File({ filename: 'combined.log' })
|
804 | ]
|
805 | });
|
806 |
|
807 | // Call exceptions.handle with a transport to handle exceptions
|
808 | logger.exceptions.handle(
|
809 | new transports.File({ filename: 'exceptions.log' })
|
810 | );
|
811 | ```
|
812 |
|
813 | If you want to use this feature with the default logger, simply call
|
814 | `.exceptions.handle()` with a transport instance.
|
815 |
|
816 | ``` js
|
817 | //
|
818 | // You can add a separate exception logger by passing it to `.exceptions.handle`
|
819 | //
|
820 | winston.exceptions.handle(
|
821 | new winston.transports.File({ filename: 'path/to/exceptions.log' })
|
822 | );
|
823 |
|
824 | //
|
825 | // Alternatively you can set `handleExceptions` to true when adding transports
|
826 | // to winston.
|
827 | //
|
828 | winston.add(new winston.transports.File({
|
829 | filename: 'path/to/combined.log',
|
830 | handleExceptions: true
|
831 | }));
|
832 | ```
|
833 |
|
834 | ### To Exit or Not to Exit
|
835 |
|
836 | By default, winston will exit after logging an uncaughtException. If this is
|
837 | not the behavior you want, set `exitOnError = false`
|
838 |
|
839 | ``` js
|
840 | const logger = winston.createLogger({ exitOnError: false });
|
841 |
|
842 | //
|
843 | // or, like this:
|
844 | //
|
845 | logger.exitOnError = false;
|
846 | ```
|
847 |
|
848 | When working with custom logger instances, you can pass in separate transports
|
849 | to the `exceptionHandlers` property or set `handleExceptions` on any
|
850 | transport.
|
851 |
|
852 | ##### Example 1
|
853 |
|
854 | ``` js
|
855 | const logger = winston.createLogger({
|
856 | transports: [
|
857 | new winston.transports.File({ filename: 'path/to/combined.log' })
|
858 | ],
|
859 | exceptionHandlers: [
|
860 | new winston.transports.File({ filename: 'path/to/exceptions.log' })
|
861 | ]
|
862 | });
|
863 | ```
|
864 |
|
865 | ##### Example 2
|
866 |
|
867 | ``` js
|
868 | const logger = winston.createLogger({
|
869 | transports: [
|
870 | new winston.transports.Console({
|
871 | handleExceptions: true
|
872 | })
|
873 | ],
|
874 | exitOnError: false
|
875 | });
|
876 | ```
|
877 |
|
878 | The `exitOnError` option can also be a function to prevent exit on only
|
879 | certain types of errors:
|
880 |
|
881 | ``` js
|
882 | function ignoreEpipe(err) {
|
883 | return err.code !== 'EPIPE';
|
884 | }
|
885 |
|
886 | const logger = winston.createLogger({ exitOnError: ignoreEpipe });
|
887 |
|
888 | //
|
889 | // or, like this:
|
890 | //
|
891 | logger.exitOnError = ignoreEpipe;
|
892 | ```
|
893 |
|
894 | ## Rejections
|
895 |
|
896 | ### Handling Uncaught Promise Rejections with winston
|
897 |
|
898 | With `winston`, it is possible to catch and log `uncaughtRejection` events
|
899 | from your process. With your own logger instance you can enable this behavior
|
900 | when it's created or later on in your applications lifecycle:
|
901 |
|
902 | ``` js
|
903 | const { createLogger, transports } = require('winston');
|
904 |
|
905 | // Enable rejection handling when you create your logger.
|
906 | const logger = createLogger({
|
907 | transports: [
|
908 | new transports.File({ filename: 'combined.log' })
|
909 | ],
|
910 | rejectionHandlers: [
|
911 | new transports.File({ filename: 'rejections.log' })
|
912 | ]
|
913 | });
|
914 |
|
915 | // Or enable it later on by adding a transport or using `.rejections.handle`
|
916 | const logger = createLogger({
|
917 | transports: [
|
918 | new transports.File({ filename: 'combined.log' })
|
919 | ]
|
920 | });
|
921 |
|
922 | // Call rejections.handle with a transport to handle rejections
|
923 | logger.rejections.handle(
|
924 | new transports.File({ filename: 'rejections.log' })
|
925 | );
|
926 | ```
|
927 |
|
928 | If you want to use this feature with the default logger, simply call
|
929 | `.rejections.handle()` with a transport instance.
|
930 |
|
931 | ``` js
|
932 | //
|
933 | // You can add a separate rejection logger by passing it to `.rejections.handle`
|
934 | //
|
935 | winston.rejections.handle(
|
936 | new winston.transports.File({ filename: 'path/to/rejections.log' })
|
937 | );
|
938 |
|
939 | //
|
940 | // Alternatively you can set `handleRejections` to true when adding transports
|
941 | // to winston.
|
942 | //
|
943 | winston.add(new winston.transports.File({
|
944 | filename: 'path/to/combined.log',
|
945 | handleRejections: true
|
946 | }));
|
947 | ```
|
948 |
|
949 | ## Profiling
|
950 |
|
951 | In addition to logging messages and metadata, `winston` also has a simple
|
952 | profiling mechanism implemented for any logger:
|
953 |
|
954 | ``` js
|
955 | //
|
956 | // Start profile of 'test'
|
957 | //
|
958 | logger.profile('test');
|
959 |
|
960 | setTimeout(function () {
|
961 | //
|
962 | // Stop profile of 'test'. Logging will now take place:
|
963 | // '17 Jan 21:00:00 - info: test duration=1000ms'
|
964 | //
|
965 | logger.profile('test');
|
966 | }, 1000);
|
967 | ```
|
968 |
|
969 | Also you can start a timer and keep a reference that you can call `.done()``
|
970 | on:
|
971 |
|
972 | ``` js
|
973 | // Returns an object corresponding to a specific timing. When done
|
974 | // is called the timer will finish and log the duration. e.g.:
|
975 | //
|
976 | const profiler = logger.startTimer();
|
977 | setTimeout(function () {
|
978 | profiler.done({ message: 'Logging message' });
|
979 | }, 1000);
|
980 | ```
|
981 |
|
982 | All profile messages are set to 'info' level by default, and both message and
|
983 | metadata are optional. For individual profile messages, you can override the default log level by supplying a metadata object with a `level` property:
|
984 |
|
985 | ```js
|
986 | logger.profile('test', { level: 'debug' });
|
987 | ```
|
988 |
|
989 | ## Querying Logs
|
990 |
|
991 | `winston` supports querying of logs with Loggly-like options. [See Loggly
|
992 | Search API](https://www.loggly.com/docs/api-retrieving-data/). Specifically:
|
993 | `File`, `Couchdb`, `Redis`, `Loggly`, `Nssocket`, and `Http`.
|
994 |
|
995 | ``` js
|
996 | const options = {
|
997 | from: new Date() - (24 * 60 * 60 * 1000),
|
998 | until: new Date(),
|
999 | limit: 10,
|
1000 | start: 0,
|
1001 | order: 'desc',
|
1002 | fields: ['message']
|
1003 | };
|
1004 |
|
1005 | //
|
1006 | // Find items logged between today and yesterday.
|
1007 | //
|
1008 | logger.query(options, function (err, results) {
|
1009 | if (err) {
|
1010 | /* TODO: handle me */
|
1011 | throw err;
|
1012 | }
|
1013 |
|
1014 | console.log(results);
|
1015 | });
|
1016 | ```
|
1017 |
|
1018 | ## Streaming Logs
|
1019 | Streaming allows you to stream your logs back from your chosen transport.
|
1020 |
|
1021 | ``` js
|
1022 | //
|
1023 | // Start at the end.
|
1024 | //
|
1025 | winston.stream({ start: -1 }).on('log', function(log) {
|
1026 | console.log(log);
|
1027 | });
|
1028 | ```
|
1029 |
|
1030 | ## Further Reading
|
1031 |
|
1032 | ### Using the Default Logger
|
1033 |
|
1034 | The default logger is accessible through the `winston` module directly. Any
|
1035 | method that you could call on an instance of a logger is available on the
|
1036 | default logger:
|
1037 |
|
1038 | ``` js
|
1039 | const winston = require('winston');
|
1040 |
|
1041 | winston.log('info', 'Hello distributed log files!');
|
1042 | winston.info('Hello again distributed logs');
|
1043 |
|
1044 | winston.level = 'debug';
|
1045 | winston.log('debug', 'Now my debug messages are written to console!');
|
1046 | ```
|
1047 |
|
1048 | By default, no transports are set on the default logger. You must
|
1049 | add or remove transports via the `add()` and `remove()` methods:
|
1050 |
|
1051 | ``` js
|
1052 | const files = new winston.transports.File({ filename: 'combined.log' });
|
1053 | const console = new winston.transports.Console();
|
1054 |
|
1055 | winston.add(console);
|
1056 | winston.add(files);
|
1057 | winston.remove(console);
|
1058 | ```
|
1059 |
|
1060 | Or do it with one call to configure():
|
1061 |
|
1062 | ``` js
|
1063 | winston.configure({
|
1064 | transports: [
|
1065 | new winston.transports.File({ filename: 'somefile.log' })
|
1066 | ]
|
1067 | });
|
1068 | ```
|
1069 |
|
1070 | For more documentation about working with each individual transport supported
|
1071 | by `winston` see the [`winston` Transports](docs/transports.md) document.
|
1072 |
|
1073 | ### Awaiting logs to be written in `winston`
|
1074 |
|
1075 | Often it is useful to wait for your logs to be written before exiting the
|
1076 | process. Each instance of `winston.Logger` is also a [Node.js stream]. A
|
1077 | `finish` event will be raised when all logs have flushed to all transports
|
1078 | after the stream has been ended.
|
1079 |
|
1080 | ``` js
|
1081 | const transport = new winston.transports.Console();
|
1082 | const logger = winston.createLogger({
|
1083 | transports: [transport]
|
1084 | });
|
1085 |
|
1086 | logger.on('finish', function (info) {
|
1087 | // All `info` log messages has now been logged
|
1088 | });
|
1089 |
|
1090 | logger.info('CHILL WINSTON!', { seriously: true });
|
1091 | logger.end();
|
1092 | ```
|
1093 |
|
1094 | It is also worth mentioning that the logger also emits an 'error' event which
|
1095 | you should handle or suppress if you don't want unhandled exceptions:
|
1096 |
|
1097 | ``` js
|
1098 | //
|
1099 | // Handle errors
|
1100 | //
|
1101 | logger.on('error', function (err) { /* Do Something */ });
|
1102 | ```
|
1103 |
|
1104 | ### Working with multiple Loggers in winston
|
1105 |
|
1106 | Often in larger, more complex, applications it is necessary to have multiple
|
1107 | logger instances with different settings. Each logger is responsible for a
|
1108 | different feature area (or category). This is exposed in `winston` in two
|
1109 | ways: through `winston.loggers` and instances of `winston.Container`. In fact,
|
1110 | `winston.loggers` is just a predefined instance of `winston.Container`:
|
1111 |
|
1112 | ``` js
|
1113 | const winston = require('winston');
|
1114 | const { format } = winston;
|
1115 | const { combine, label, json } = format;
|
1116 |
|
1117 | //
|
1118 | // Configure the logger for `category1`
|
1119 | //
|
1120 | winston.loggers.add('category1', {
|
1121 | format: combine(
|
1122 | label({ label: 'category one' }),
|
1123 | json()
|
1124 | ),
|
1125 | transports: [
|
1126 | new winston.transports.Console({ level: 'silly' }),
|
1127 | new winston.transports.File({ filename: 'somefile.log' })
|
1128 | ]
|
1129 | });
|
1130 |
|
1131 | //
|
1132 | // Configure the logger for `category2`
|
1133 | //
|
1134 | winston.loggers.add('category2', {
|
1135 | format: combine(
|
1136 | label({ label: 'category two' }),
|
1137 | json()
|
1138 | ),
|
1139 | transports: [
|
1140 | new winston.transports.Http({ host: 'localhost', port:8080 })
|
1141 | ]
|
1142 | });
|
1143 | ```
|
1144 |
|
1145 | Now that your loggers are setup, you can require winston _in any file in your
|
1146 | application_ and access these pre-configured loggers:
|
1147 |
|
1148 | ``` js
|
1149 | const winston = require('winston');
|
1150 |
|
1151 | //
|
1152 | // Grab your preconfigured loggers
|
1153 | //
|
1154 | const category1 = winston.loggers.get('category1');
|
1155 | const category2 = winston.loggers.get('category2');
|
1156 |
|
1157 | category1.info('logging to file and console transports');
|
1158 | category2.info('logging to http transport');
|
1159 | ```
|
1160 |
|
1161 | If you prefer to manage the `Container` yourself, you can simply instantiate one:
|
1162 |
|
1163 | ``` js
|
1164 | const winston = require('winston');
|
1165 | const { format } = winston;
|
1166 | const { combine, label, json } = format;
|
1167 |
|
1168 | const container = new winston.Container();
|
1169 |
|
1170 | container.add('category1', {
|
1171 | format: combine(
|
1172 | label({ label: 'category one' }),
|
1173 | json()
|
1174 | ),
|
1175 | transports: [
|
1176 | new winston.transports.Console({ level: 'silly' }),
|
1177 | new winston.transports.File({ filename: 'somefile.log' })
|
1178 | ]
|
1179 | });
|
1180 |
|
1181 | const category1 = container.get('category1');
|
1182 | category1.info('logging to file and console transports');
|
1183 | ```
|
1184 |
|
1185 | ## Installation
|
1186 |
|
1187 | ``` bash
|
1188 | npm install winston
|
1189 | ```
|
1190 |
|
1191 | ``` bash
|
1192 | yarn add winston
|
1193 | ```
|
1194 |
|
1195 | ## Run Tests
|
1196 |
|
1197 | All of the winston tests are written with [`mocha`][mocha], [`nyc`][nyc], and
|
1198 | [`assume`][assume]. They can be run with `npm`.
|
1199 |
|
1200 | ``` bash
|
1201 | npm test
|
1202 | ```
|
1203 |
|
1204 | #### Author: [Charlie Robbins]
|
1205 | #### Contributors: [Jarrett Cruger], [David Hyde], [Chris Alderson]
|
1206 |
|
1207 | [Transports]: #transports
|
1208 | [Logging levels]: #logging-levels
|
1209 | [Formats]: #formats
|
1210 | [Using custom logging levels]: #using-custom-logging-levels
|
1211 | [Adding Custom Transports]: #adding-custom-transports
|
1212 | [core transports]: docs/transports.md#winston-core
|
1213 | [additional transports]: docs/transports.md#additional-transports
|
1214 |
|
1215 | [RFC5424]: https://tools.ietf.org/html/rfc5424
|
1216 | [util.format]: https://nodejs.org/dist/latest/docs/api/util.html#util_util_format_format_args
|
1217 | [mocha]: https://mochajs.org
|
1218 | [nyc]: https://github.com/istanbuljs/nyc
|
1219 | [assume]: https://github.com/bigpipe/assume
|
1220 | [logform]: https://github.com/winstonjs/logform#readme
|
1221 | [winston-transport]: https://github.com/winstonjs/winston-transport
|
1222 |
|
1223 | [Read the `winston@2.x` documentation]: https://github.com/winstonjs/winston/tree/2.x
|
1224 |
|
1225 | [quick-example]: https://github.com/winstonjs/winston/blob/master/examples/quick-start.js
|
1226 | [examples]: https://github.com/winstonjs/winston/tree/master/examples
|
1227 |
|
1228 | [Charlie Robbins]: http://github.com/indexzero
|
1229 | [Jarrett Cruger]: https://github.com/jcrugzz
|
1230 | [David Hyde]: https://github.com/dabh
|
1231 | [Chris Alderson]: https://github.com/chrisalderson
|