Skip to main content

winston 中文网

几乎所有内容的日志器。

¥A logger for just about everything.

winston@3

有关更多信息,请参阅 升级指南。欢迎提交错误报告和 PR!

¥See the Upgrade Guide for more information. Bug reports and PRs welcome!

正在寻找 winston@2.x 文档?

¥Looking for winston@2.x documentation?

请注意,以下文档适用于 winston@3阅读 winston@2.x 文档

¥Please note that the documentation below is for winston@3. Read the winston@2.x documentation.

动机

¥Motivation

winston 旨在成为一个简单且通用的日志库,支持多种传输。传输本质上是日志的存储设备。每个 winston 日志器都可以在不同级别配置多个传输(参见:传输)(参见:日志级别).例如,可能希望将错误日志存储在持久的远程位置(如数据库),但所有日志都输出到控制台或本地文件。

¥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 旨在解耦日志记录过程的各个部分,使其更加灵活和可扩展。注意支持日志格式的灵活性(参见:格式)和级别(参见:使用自定义日志级别),并确保这些 API 与传输日志的实现分离(即日志的存储/索引方式,请参阅:添加自定义传输) 到他们向程序员公开的 API。

¥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?查看 ./examples/ 中的 快速入门示例./examples/*.js 中有许多其他示例。没有看到你认为应该存在的示例?提交拉取请求以添加它!

¥TL;DR? Check out the quick start example in ./examples/. There are a number of other examples in ./examples/*.js. Don't see an example you think should be there? Submit a pull request to add it!

用法

¥Usage

使用 winston 的推荐方法是创建自己的日志器。最简单的方法是使用 winston.createLogger

¥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(),
}));
}

你也可以直接通过 require('winston') 公开的默认日志器进行记录,但这仅仅是一个如果你愿意,可以在整个应用中使用方便的共享日志器。请注意,默认日志器默认没有任何传输。你需要自己添加传输,并且保留默认日志器而不使用任何传输可能会产生高内存使用率问题。

¥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.

目录

¥Table of contents

日志记录

¥Logging

winston 中的日志记录级别符合 RFC5424 指定的严重性顺序:假定所有级别的严重性都按数字从最重要到最不重要的顺序递增。

¥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.

const levels = {
error: 0,
warn: 1,
info: 2,
http: 3,
verbose: 4,
debug: 5,
silly: 6
};

创建你自己的日志器

¥Creating your own Logger

你可以通过使用 winston.createLogger 创建日志器开始:

¥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:

名称默认描述
level'info'仅当 info.level 小于或等于此级别时才记录
levelswinston.config.npm.levels表示日志优先级的级别(和颜色)
formatwinston.format.jsoninfo 消息的格式(参见:[格式])
transports[](无传输)info 消息的日志记录目标集
exitOnErrortrue如果为 false,则处理的异常不会导致 process.exit
silentfalse如果为 true,则所有日志都将被抑制

提供给 createLogger 的级别将定义为返回的 logger 上的便捷方法。

¥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');

一旦 winston.createLogger 向你提供了传输,你就可以从 logger 中添加或删除传输:

¥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

你还可以使用 configure 方法批量重新配置 winston.Logger 实例:

¥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)
]
});

创建子日志器

¥Creating child loggers

你可以从现有日志器创建子日志器以传递元数据覆盖:

¥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' });

如果你还扩展了 Logger 类,则 .child 可能会出现错误,因为某些实现细节会使 this 关键字指向意外的东西。请谨慎使用。

¥.child is likely to be bugged if you're also extending the Logger class, due to some implementation details that make this keyword to point to unexpected things. Use with caution.

流、objectModeinfo 对象

¥Streams, objectMode, and info objects

winston 中,LoggerTransport 实例都被视为接受 info 对象的 objectMode 流。

¥In winston, both Logger and Transport instances are treated as objectMode streams that accept an info object.

提供给给定格式的 info 参数代表单个日志消息。对象本身是可变的。每个 info 必须至少具有 levelmessage 属性:

¥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.
};

除了级别和消息之外的属性被视为“meta”。即:

¥Properties besides level and message are considered as "meta". i.e.:

const { level, message, ...meta } = info;

logform 本身的几种格式添加了附加属性:

¥Several of the formats in logform itself add additional properties:

属性格式由描述
splatsplat()%d %s 样式消息的字符串插值 splat。
timestamptimestamp()收到消息的时间戳。
labellabel()与每个关联的自定义标签消息。
msms()自上一条日志消息以来的毫秒数。

作为消费者,你可以添加任何你想要的属性 - 内部状态由 Symbol 属性维护:

¥As a consumer you may add whatever properties you wish – internal state is maintained by Symbol properties:

  • Symbol.for('level') (READ-ONLY):等于 level 属性。被所有代码视为不可变的。

    ¥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'):其他字符串插值参数。仅由 splat() 格式使用。

    ¥Symbol.for('splat'): additional string interpolation arguments. Used exclusively by splat() format.

这些符号存储在另一个包中:triple-beam 以便所有 logform 的使用者都可以拥有相同的符号引用。即:

¥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

注意:提供的 meta 对象中的任何 { message } 属性将自动连接到任何已提供的 msg:例如,以下内容将 'world' 连接到 'hello':

¥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':

logger.log('error', 'hello', { message: 'world' });
logger.info('hello', { message: 'world' });

格式

¥Formats

可以从 winston.format 访问 winston 中的格式。它们是在 logform 中实现的,logform 是独立于 winston 的模块。如果你希望在传输中包含默认格式,这可以让你在编写自己的传输时具有灵活性。

¥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.

在现代版本的 node 中,模板字符串非常高效,是进行大多数终端用户格式化的推荐方式。如果你想定制日志格式,winston.format.printf 适合你:

¥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()]
});

要查看可用的内置格式并了解有关创建自己的自定义日志格式的更多信息,请参阅 logform

¥To see what built-in formats are available and learn more about creating your own custom logging formats, see logform.

组合格式

¥Combining formats

可以使用 format.combine 将任意数量的格式组合成一种格式。由于 format.combine 不接受 opts,为了方便起见,它会返回组合格式的预先创建的实例。

¥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' }

字符串插值

¥String interpolation

log 方法使用 util.format 提供字符串插值。必须使用 format.splat() 启用它。

¥The log method provides the string interpolation using util.format. It must be enabled using format.splat().

下面是一个示例,它使用 format.splat 定义带有消息字符串插值的格式,然后使用 format.simple 序列化整个 info 消息。

¥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 对象

¥Filtering info Objects

如果你希望在记录时完全过滤掉给定的 info 对象,则只需返回一个 falsey 值。

¥If you wish to filter out a given info Object completely when logging then simply return a falsey value.

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.'
});

使用 format.combine 将尊重任何返回的 falsey 值并停止评估系列中后续格式。例如:

¥Use of format.combine will respect any falsey values return and stop evaluation of later formats in the series. For example:

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

格式是定义单个方法的原型对象(即类实例):transform(info, opts) 并返回修改的 info

¥Formats are prototypal objects (i.e. class instances) that define a single method: transform(info, opts) and return the mutated info:

  • info:表示日志消息的对象。

    ¥info: an object representing the log message.

  • opts:特定于当前格式实例的设置。

    ¥opts: setting specific to the current instance of the format.

它们预计会返回以下两项之一:

¥They are expected to return one of two things:

  • 表示修改后的 info 参数的 info 对象。如果希望不可变性,则无需保留对象引用。所有当前内置格式都认为 info 是可变的,但 [immutablejs] 正在考虑用于未来版本。

    ¥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.

  • 一个 falsey 值,表示调用者应忽略 info 参数。(参见:过滤 info 对象)如下。

    ¥A falsey value indicating that the info argument should be ignored by the caller. (See: Filtering info Objects) below.

winston.format 的设计尽可能简单。要定义新格式,只需将其传递给 transform(info, opts) 函数即可获得新的 Format

¥winston.format is designed to be as simple as possible. To define a new format, simply pass it a transform(info, opts) function to get a new Format.

返回的命名 Format 可用于根据需要创建给定 Format 的任意数量的副本:

¥The named Format returned can be used to create as many copies of the given Format as desired:

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

winston 中的日志记录级别符合 RFC5424 指定的严重性顺序:假定所有级别的严重性都按数字从最重要到最不重要的顺序递增。

¥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.

每个 level 都被赋予特定的整数优先级。优先级越高,消息被认为越重要,相应的整数优先级越低。例如,如 RFC5424 中所述,syslog 级别的优先级从 0 到 7(从高到低)。

¥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).

{
emerg: 0,
alert: 1,
crit: 2,
error: 3,
warning: 4,
notice: 5,
info: 6,
debug: 7
}

同样,npm 日志记录级别的优先级从 0 到 6(从高到低):

¥Similarly, npm logging levels are prioritized from 0 to 6 (highest to lowest):

{
error: 0,
warn: 1,
info: 2,
http: 3,
verbose: 4,
debug: 5,
silly: 6
}

如果你没有明确定义 winston 应使用的级别,则将使用上面的 npm 级别。

¥If you do not explicitly define the levels that winston should use, the npm levels above will be used.

使用日志记录级别

¥Using Logging Levels

可以通过两种方式之一设置日志记录消息的级别。你可以将表示日志记录级别的字符串传递给 log() 方法,或使用每个 winston Logger 上定义的级别指定方法。

¥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.

//
// 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 允许你在每个传输上定义一个 level 属性,该属性指定传输应记录的最大消息级别。例如,使用 syslog 级别,你可以只将 error 消息记录到控制台,将 info 及以下的所有内容记录到文件(包括 error 消息):

¥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):

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:

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 支持可自定义的日志记录级别,默认为 npm 样式的日志记录级别。必须在创建日志器时指定级别。

¥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

除了 winston 中可用的预定义 npmsyslogcli 级别之外,你还可以选择定义自己的级别:

¥In addition to the predefined npm, syslog, and cli levels available in winston, you can also choose to define your own:

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');

虽然此数据结构中存在轻微重复,但如果你不想使用颜色,它可以实现简单的封装。如果你确实希望有颜色,除了将级别传递给 Logger 本身之外,还必须让 winston 知道它们:

¥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:

winston.addColors(myCustomLevels.colors);

这使使用 colorize 格式化程序的日志器能够适当地为自定义级别的输出着色和设置样式。

¥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,

baz: 'italic yellow',
foobar: 'bold red cyanBG'

可能的选项如下。

¥Possible options are below.

  • 字体样式:bold, dim, italic, underline, inverse, hidden, strikethrough.

    ¥Font styles: bold, dim, italic, underline, inverse, hidden, strikethrough.

  • 字体前景色:black, red, green, yellow, blue, magenta, cyan, white, gray, grey.

    ¥Font foreground colors: black, red, green, yellow, blue, magenta, cyan, white, gray, grey.

  • 背景颜色:blackBG, redBG, greenBG, yellowBG, blueBG magentaBG, cyanBG, whiteBG

    ¥Background colors: blackBG, redBG, greenBG, yellowBG, blueBG magentaBG, cyanBG, whiteBG

为标准日志级别着色

¥Colorizing Standard logging levels

要为标准日志级别着色,请添加

¥To colorize the standard logging level add

winston.format.combine(
winston.format.colorize(),
winston.format.simple()
);

其中 winston.format.simple() 是你想要使用的任何其他格式化程序。colorize 格式化程序必须位于添加你希望着色的文本的任何格式化程序之前。

¥where winston.format.simple() is whatever other formatter you want to use. The colorize formatter must come before any formatters adding text you wish to color.

使用 json 格式日志时为整行日志着色

¥Colorizing full log line when json formatting logs

要使用 json 格式化程序为整个日志行着色,你可以应用以下内容

¥To colorize the full log line with the json formatter you can apply the following

winston.format.combine(
winston.format.json(),
winston.format.colorize({ all: true })
);

传输

¥Transports

winston 中包含多个 核心传输,它们利用 Node.js 核心提供的内置网络和文件 I/O。此外,还有社区成员编写的 附加传输

¥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

在构建传输时,可以使用相同类型的多个传输,例如 winston.transports.File

¥It is possible to use multiple transports of the same type e.g. winston.transports.File when you construct the transport.

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.:

const combinedLogs = logger.transports.find(transport => {
return transport.filename === 'combined.log'
});

logger.remove(combinedLogs);

添加自定义传输

¥Adding Custom Transports

添加自定义传输很容易。你需要做的就是接受你需要的任何选项,实现 log() 方法,然后使用 winston 使用它。

¥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.

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();
}
};

常见传输选项

¥Common Transport options

由于每个传输都从 winston-transport 继承,因此可以分别在每个传输上设置自定义格式和自定义日志级别:

¥As every transport inherits from winston-transport, it's possible to set a custom format and a custom log level on each transport separately:

const logger = winston.createLogger({
transports: [
new winston.transports.File({
filename: 'error.log',
level: 'error',
format: winston.format.json()
}),
new winston.transports.Http({
level: 'warn',
format: winston.format.json()
}),
new winston.transports.Console({
level: 'info',
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
})
]
});

异常

¥Exceptions

使用 winston 处理未捕获的异常

¥Handling Uncaught Exceptions with winston

使用 winston,可以从你的进程中捕获和记录 uncaughtException 事件。使用你自己的日志器实例,你可以在创建时或在应用生命周期的稍后启用此行为:

¥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:

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' })
);

如果你想将此功能与默认日志器一起使用,只需使用传输实例调用 .exceptions.handle()

¥If you want to use this feature with the default logger, simply call .exceptions.handle() with a transport instance.

//
// 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

默认情况下,winston 将在记录 uncaughtException 后退出。如果这不是你想要的行为,请设置 exitOnError = false

¥By default, winston will exit after logging an uncaughtException. If this is not the behavior you want, set exitOnError = false

const logger = winston.createLogger({ exitOnError: false });

//
// or, like this:
//
logger.exitOnError = false;

使用自定义日志器实例时,你可以将单独的传输传递给 exceptionHandlers 属性或在任何传输上设置 handleExceptions

¥When working with custom logger instances, you can pass in separate transports to the exceptionHandlers property or set handleExceptions on any transport.

示例 1

¥Example 1

const logger = winston.createLogger({
transports: [
new winston.transports.File({ filename: 'path/to/combined.log' })
],
exceptionHandlers: [
new winston.transports.File({ filename: 'path/to/exceptions.log' })
]
});
示例 2

¥Example 2

const logger = winston.createLogger({
transports: [
new winston.transports.Console({
handleExceptions: true
})
],
exitOnError: false
});

exitOnError 选项也可以是防止仅在某些类型的错误上退出的函数:

¥The exitOnError option can also be a function to prevent exit on only certain types of errors:

function ignoreEpipe(err) {
return err.code !== 'EPIPE';
}

const logger = winston.createLogger({ exitOnError: ignoreEpipe });

//
// or, like this:
//
logger.exitOnError = ignoreEpipe;

拒绝

¥Rejections

使用 winston 处理未捕获的 Promise 拒绝

¥Handling Uncaught Promise Rejections with winston

使用 winston,可以从你的进程中捕获和记录 unhandledRejection 事件。使用你自己的日志器实例,你可以在创建时或在应用生命周期的稍后启用此行为:

¥With winston, it is possible to catch and log unhandledRejection 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:

const { createLogger, transports } = require('winston');

// Enable rejection handling when you create your logger.
const logger = createLogger({
transports: [
new transports.File({ filename: 'combined.log' })
],
rejectionHandlers: [
new transports.File({ filename: 'rejections.log' })
]
});

// Or enable it later on by adding a transport or using `.rejections.handle`
const logger = createLogger({
transports: [
new transports.File({ filename: 'combined.log' })
]
});

// Call rejections.handle with a transport to handle rejections
logger.rejections.handle(
new transports.File({ filename: 'rejections.log' })
);

如果你想将此功能与默认日志器一起使用,只需使用传输实例调用 .rejections.handle()

¥If you want to use this feature with the default logger, simply call .rejections.handle() with a transport instance.

//
// You can add a separate rejection logger by passing it to `.rejections.handle`
//
winston.rejections.handle(
new winston.transports.File({ filename: 'path/to/rejections.log' })
);

//
// Alternatively you can set `handleRejections` to true when adding transports
// to winston.
//
winston.add(new winston.transports.File({
filename: 'path/to/combined.log',
handleRejections: true
}));

分析

¥Profiling

除了记录消息和元数据之外,winston 还为任何日志器实现了一个简单的分析机制:

¥In addition to logging messages and metadata, winston also has a simple profiling mechanism implemented for any logger:

//
// 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);

你还可以启动一个计时器并保留一个可以调用 .done() 的引用:

¥Also you can start a timer and keep a reference that you can call .done() on:

 // 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);

所有配置文件消息默认设置为 'info' 级别,消息和元数据都是可选的。对于单个配置文件消息,你可以通过提供具有 level 属性的元数据对象来覆盖默认日志级别:

¥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:

logger.profile('test', { level: 'debug' });

查询日志

¥Querying Logs

winston 支持使用类似 Loggly 的选项查询日志。查看 Loggly Search API。具体来说:FileCouchdbRedisLogglyNssocketHttp

¥winston supports querying of logs with Loggly-like options. See Loggly Search API. Specifically: File, Couchdb, Redis, Loggly, Nssocket, and Http.

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.

//
// Start at the end.
//
winston.stream({ start: -1 }).on('log', function(log) {
console.log(log);
});

进一步阅读

¥Further Reading

使用默认日志器

¥Using the Default Logger

默认日志器可直接通过 winston 模块访问。你可以在日志器实例上调用的任何方法都可以在默认日志器上使用:

¥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:

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!');

默认情况下,默认日志器上未设置任何传输。你必须通过 add()remove() 方法添加或删除传输:

¥By default, no transports are set on the default logger. You must add or remove transports via the add() and remove() methods:

const files = new winston.transports.File({ filename: 'combined.log' });
const console = new winston.transports.Console();

winston.add(console);
winston.add(files);
winston.remove(console);

或者通过一次调用 configure() 来执行此操作:

¥Or do it with one call to configure():

winston.configure({
transports: [
new winston.transports.File({ filename: 'somefile.log' })
]
});

有关使用 winston 支持的每个单独传输的更多文档,请参阅 winston 传输 文档。

¥For more documentation about working with each individual transport supported by winston see the winston Transports document.

等待在 winston 中写入日志

¥Awaiting logs to be written in winston

在退出进程之前等待日志写入通常很有用。每个 winston.Logger 实例也是一个 [Node.js 流]。当流结束后,所有日志都已刷新到所有传输时,将引发 finish 事件。

¥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.

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();

还值得一提的是,如果日志器本身发生错误,日志器也会发出 'error' 事件,如果你不想要未处理的异常,则应该处理或抑制该错误:

¥It is also worth mentioning that the logger also emits an 'error' event if an error occurs within the logger itself which you should handle or suppress if you don't want unhandled exceptions:

//
// Handle errors originating in the logger itself
//
logger.on('error', function (err) { /* Do Something */ });

在 winston 中使用多个日志器

¥Working with multiple Loggers in winston

通常在更大、更复杂的应用中,需要具有不同设置的多个日志器实例。每个日志器负责不同的功能区域(或类别)。这在 winston 中以两种方式公开:通过 winston.loggerswinston.Container 的实例。事实上,winston.loggers 只是 winston.Container 的一个预定义实例:

¥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:

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 })
]
});

现在你的日志器已设置好,你可以在应用中的任何文件中要求 winston 并访问这些预配置的日志器:

¥Now that your loggers are setup, you can require winston in any file in your application and access these pre-configured loggers:

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');

如果你更喜欢自己管理 Container,你可以简单地实例化一个:

¥If you prefer to manage the Container yourself, you can simply instantiate one:

const winston = require('winston');
const { format } = winston;
const { combine, label, 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');

将控制台传输消息路由到控制台而不是 stdout 和 stderr

¥Routing Console transport messages to the console instead of stdout and stderr

默认情况下,winston.transports.Console 传输将消息发送到 stdoutstderr。在大多数情况下这都可以;但是,在某些情况下这并不可取,包括:

¥By default the winston.transports.Console transport sends messages to stdout and stderr. This is fine in most situations; however, there are some cases where this isn't desirable, including:

  • 使用 VSCode 进行调试并附加到 Node.js 进程(而不是启动该进程)

    ¥Debugging using VSCode and attaching to, rather than launching, a Node.js process

  • 在 AWS Lambda 中编写 JSON 格式的消息

    ¥Writing JSON format messages in AWS Lambda

  • 使用 --silent 选项在 Jest 测试期间记录

    ¥Logging during Jest tests with the --silent option

要使传输日志改用 console.log()console.warn()console.error(),请将 forceConsole 选项设置为 true

¥To make the transport log use console.log(), console.warn() and console.error() instead, set the forceConsole option to true:

const logger = winston.createLogger({
level: 'info',
transports: [new winston.transports.Console({ forceConsole: true })]
});

安装

¥Installation

npm install winston
yarn add winston

运行测试

¥Run Tests

所有 winston 测试都是用 mochanycassume 编写的。它们可以与 npm 一起运行。

¥All of the winston tests are written with mocha, nyc, and assume. They can be run with npm.

npm test

作者:[Charlie Robbins]

¥Author: Charlie Robbins

贡献者:[Jarrett Cruger]、[David Hyde]、[Chris 奥尔德森]

¥Contributors: Jarrett Cruger, David Hyde, Chris Alderson