winston 是 Node 中一个比较知名的处理日志的库,简单通用,支持多种传输方式。winston 将日志记录的过程进行了分离,分为了日志格式化以及日志消费等多个部分,使用上更加灵活易扩展。
快速开始
const winston = require("winston");
// 创建 logger 实例
const logger = winston.createLogger({
// 输出
transports: [
// console 输出
new winston.transports.Console(),
// 写到文件
new winston.transports.File({ filename: "combined.log" }),
],
});
// 打印 log
logger.log({
level: "info",
message: "Hello distributed log files!",
});
logger.info("Hello again distributed logs");
Formats
Formats 代表 winston 中处理、格式化日志的一些常见方法,可以通过 winston.format
使用。
通过 Formats ,可以实现给日志设置加额外的属性,包含时间、颜色、错误堆栈等,使得日志信息更加丰富。
import { createLogger, format, transports } from 'winston'
import { DBTransport } from './transport'
const { label, combine, timestamp, metadata, json, uncolorize, errors } = format
/**
* 创建 winston log
*/
export const logger = createLogger({
format: combine(
metadata(),
errors({ stack: true }),
label({ label: '_RELOG_' }),
timestamp(),
uncolorize(),
json()
),
handleExceptions: true,
transports: [
new transports.File({
filename: './log/test.log',
}),
],
})
Transport
Transport 是对传输消费日志方法的抽象,如将日志打印到 console,或者日志写入到文件中,都是消费日志的一种方法。
winston 中内置了几种核心的传输方法,使用 Node 的基础功能进行日志传输
-
Console 将日志传输到标准输出中
-
File 将日志写入文件
-
HTTP 通过 HTTP 请求传输日志
-
Stream 通过流传输日志
更多的 winston transport 可以参考官方文档
自定义 Transport
自定义 Transport 需要继承 TransportStream 类,并实现 log 方法,下面是一个 Demo,展示了将日志写入到云开发数据库中的实现
interface IQueryOptions {
/**
* 日志开始时间
* new Date() - (24 * 60 * 60 * 1000)
*/
from: number
/**
* 日志结束时间
* new Date()
*/
until: number
/**
* 查询的日志条数,如 10
*/
limit: number
/**
* 查询日志偏移条数,如 0
*/
offset: number
/**
* 查询排序
*/
order: 'asc' | 'desc'
/**
* 查询字段
*/
fields: string[]
}
/**
* 基于 CloudBase 数据库的 Log Transport
* 参考 winston http transport 实现:
* https://github.com/winstonjs/winston/blob/master/lib/winston/transports/http.js
*/
export class DBTransport extends TransportStream {
collection: Database.CollectionReference
constructor(options = {}) {
super(options)
const app = getCloudBaseApp()
this.collection = app.database().collection('relog')
}
/**
* 实现 winston log 方法
*/
log(info, callback) {
const { label, level, message, stack, timestamp, metadata } = info
// 添加环境 ID 和函数信息
const envId = process.env.SCF_NAMESPACE
const functionName = process.env.SCF_FUNCTIONNAME
const logData = {
...metadata,
label,
level,
stack,
message,
envId,
functionName,
timestamp: new Date(timestamp),
}
this.collection
.add(logData)
.then(() => {
this.emit('logged', info)
})
.catch((e) => {
this.emit('error', e)
})
if (callback) {
setImmediate(callback)
}
}
/**
* 实现日志的查询方法
*/
query(options: Partial<IQueryOptions>, callback) {
const now = Date.now()
const { from = 0, until = now, limit = 10, offset = 0, order = 'asc', fields } = options
const query = this.collection.where({})
this.collection
.where({})
.get()
.then((res) => {
callback(null, res)
})
.catch((e) => {
callback(e)
})
}
}
总结
winston 可以帮助我们更简单的处理 Node 应用中的日志,也能比较容易的进行扩展定制化,是一个不错的工具。这里只对 winston 进行了简单的介绍,更多的能力请保持关注