温斯顿日志对象

co*_*ndp 19 node.js winston

我使用 Winston 进行后端日志记录我无法在不使用的情况下记录对象,JSON.stringify这很烦人

logger.debug(`Register ${JSON.stringify(req.body)}`)
Run Code Online (Sandbox Code Playgroud)
const logger: Logger = createLogger({
    // change level if in dev environment versus production
    level: env === 'production' ? 'info' : 'debug',
    format: format.combine(
        format.label({label: path.basename(process.mainModule.filename)}),
        format.timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
        format.prettyPrint()
    ),
    transports: [
        new transports.Console({
            format: format.combine(format.colorize(), logFormat),
        }),
        new transports.File({
            filename,
            format: format.combine(format.json()),
        }),
    ],
    exitOnError: false,
})
Run Code Online (Sandbox Code Playgroud)

你能告诉我用 Winston 记录对象的方法吗?我使用的是 3.2.1 版

She*_*xFR 31

您正在尝试将 JSON 对象直接插入字符串中,因此它将在[Object Object]没有JSON.stringify.

这不能通过配置 Winston 来解决,因为在生成字符串时(在logger.debug函数实际读取它之前)会发生此问题,因此console.log调用将打印相同的内容。

logger.*函数的第一个参数是消息(字符串),然后你可以传递一个元数据对象(JSON)。

要在logFormat函数中使用元数据,请按如下方式更新 Logger 实例:

const winston = require('winston')
const { format, transports } = winston
const path = require('path')

const logFormat = format.printf(info => `${info.timestamp} ${info.level} [${info.label}]: ${info.message}`)

const logger = winston.createLogger({
  level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
  format: format.combine(
    format.label({ label: path.basename(process.mainModule.filename) }),
    format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
    // Format the metadata object
    format.metadata({ fillExcept: ['message', 'level', 'timestamp', 'label'] })
  ),
  transports: [
    new transports.Console({
      format: format.combine(
        format.colorize(),
        logFormat
      )
    }),
    new transports.File({
      filename: 'logs/combined.log',
      format: format.combine(
        // Render in one line in your log file.
        // If you use prettyPrint() here it will be really
        // difficult to exploit your logs files afterwards.
        format.json()
      )
    })
  ],
  exitOnError: false
})
Run Code Online (Sandbox Code Playgroud)

用法:

const req = {
  body: {
    name: 'Daniel Duuch',
    email: 'daniel.duuch@greatmail.com',
    password: 'myGreatPassword'
  }
}

logger.debug(`Register ${req.body.name} with email ${req.body.email}`, { ...req.body, action: 'register' })
Run Code Online (Sandbox Code Playgroud)

控制台输出:

2019-05-11 17:05:45 debug [index.js]: Register Daniel Duuch with email daniel.duuch@greatmail.com
Run Code Online (Sandbox Code Playgroud)

日志文件输出(手动美化,请参阅传输文件格式中的注释):

2019-05-11 17:05:45 debug [index.js]: Register Daniel Duuch with email daniel.duuch@greatmail.com
Run Code Online (Sandbox Code Playgroud)

希望这能解决您的问题。

此答案的代码

  • 我知道代码只是一个示例,但如果您这样做,您应该确保您实际上没有在任何地方记录人们的密码。 (2认同)
  • “action: register”数据会发生什么? (2认同)

Lir*_*mer 18

我的解决方案是使用这种格式化程序:

const { format } = winston
const consoleFormat = format.combine(
  format.prettyPrint(),
  format.splat(),
  format.printf((info) => {
    if (typeof info.message === 'object') {
      info.message = JSON.stringify(info.message, null, 3)
    }

    return info.message
  })
)
Run Code Online (Sandbox Code Playgroud)

现在所有这些选项都按预期工作:

logger.info('plain text')
logger.info('plain text with object %o', { a:1, b: 2} )
logger.info({ a:1, b: 2 })
Run Code Online (Sandbox Code Playgroud)

  • 这里的 printf 示例是处理将记录器传递到尝试记录对象的第 3 方包的情况的好方法,因为您无法控制作为输入传递的内容。 (2认同)

小智 11

您可以format.splat()在记录器配置中使用:

const logger = createLogger({
    format: combine(
        ...
        format.splat(), // <--
        ...
    ),
    ...
});
Run Code Online (Sandbox Code Playgroud)

...并使用字符串插值记录对象:

let myObj = { /* ... */ };
logger.info('This message will include a complete object: %O', myObj);
Run Code Online (Sandbox Code Playgroud)

  • @anton-pastukhov @coinhndp 很抱歉,但这是一个不好的做法。我建议 OP 使用 `metadata` 参数是有原因的,如果您需要在监控/分析系统中使用日志,最好直接在日志中使用 JSON 对象,而不是通过搜索数据字符串。如果日志由人工操作变为红色,那么最好将该对象解析为可读字符串。这就是为什么我的解决方案提供了针对人类操作可读性进行优化的控制台输出以及针对数据处理进行优化的日志文件输出。请不要鼓励不良做法。 (2认同)

Sir*_*iey 5

我不得不结合@SherloxFR 和@Anton 提供的解决方案。

const Winston = require('winston');
const { format } = Winston;

const options = {
    file: {
        ....
        format: format.combine(
            format.splat(), 
            format.json()
        ),
        ...
    },
    console: {
        ...
        format: format.combine(
            format.splat(),
            format.json()
        ),
        ...
    }
};
Run Code Online (Sandbox Code Playgroud)

您可以看到我在上面的代码中将format.splat()和都添加format.json()到了选项配置中。

const logger = new Winston.createLogger({
    transports: [
        new Winston.transports.File(options.file),
        new Winston.transports.Console(options.console)
    ],
    exitOnError: false // do not exit on handled exceptions
});
Run Code Online (Sandbox Code Playgroud)

这就是我使用选项配置对象的方式。您实际上可以在传输数组中编写格式代码,但我不喜欢那样。反正都是你的选择。

像这样配置后,这就是我在代码中使用它的方式

let myObj = {
   name: "StackOverflow",
};

logger.info('Content: %o', myObj);
Run Code Online (Sandbox Code Playgroud)

如果你愿意,你也可以像这样传播它

logger.info('Content: %o', {...myObj});
Run Code Online (Sandbox Code Playgroud)

就这样。Winston 应该使用此设置记录您的对象。