A logger for just about everything.
See the Upgrade Guide for more information. Bug reports and PRs welcome!
winston@2.x documentation?Please note that the documentation below is for winston@3.
[Read the winston@2.x documentation].
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.
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!
The recommended way to use winston is to create your own logger. The
simplest way to do this is using winston.createLogger:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
defaultMeta: { service: 'user-service' },
transports: [
//
// - Write all logs with importance level of `error` or higher to `error.log`
// (i.e., error, fatal, but not other levels)
//
new winston.transports.File({ filename: 'error.log', level: 'error' }),
//
// - Write all logs with importance level of `info` or higher to `combined.log`
// (i.e., fatal, error, warn, and info, but not trace)
//
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.
Note that the default logger doesn't have any transports by default.
You need add transports by yourself, and leaving the default logger without any
transports may produce a high memory usage issue.
objectMode, and info objectsinfo ObjectswinstonwinstonLogging 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.
const levels = {
error: 0,
warn: 1,
info: 2,
http: 3,
verbose: 4,
debug: 5,
silly: 6
};
You get started by creating a logger using winston.createLogger:
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 is 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.
//
// 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:
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:
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)
]
});
You can create child loggers from existing loggers to pass metadata overrides:
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
]
});
const childLogger = logger.child({ requestId: '451' });
.childis likely to be bugged if you're also extending theLoggerclass, due to some implementation details that makethiskeyword to point to unexpected things. Use with caution.
objectMode, and info objectsIn winston, both Logger and Transport instances are treated as
objectMode
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:
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.:
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":jsonlogstashprintfprettyPrintsimpleSymbol.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.:
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 ametaobject provided will automatically be concatenated to anymsgalready provided: For example the below will concatenate 'world' onto 'hello':
js logger.log('error', 'hello', { message: 'world' }); logger.info('hello', { message: 'world' });
Formats in winston can be accessed from winston.format. They are
implemented in 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:
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].
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.
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' }
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.
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 });
info ObjectsIf 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 trans
$ claude mcp add winston \
-- python -m otcore.mcp_server <graph>