目录
简介
winston实践
1. 基本配置说明
2. 日志传输配置
3. 开发环境控制台输出
4. 实用日志方法封装
5. 使用示例
6. 日志级别说明
完整代码
封装方法
koa 日志中间件
在app使用
简介
Winston 是强大、灵活的 Node.js
开源日志库之一,理论上, Winston 是一个可以记录所有信息的记录器。这是一个高度直观的工具,易于定制。可以通过更改几行代码来调整其背后的逻辑。它使对数据库或文件等持久存储位置的日志记录变得简单容易。
Winston 提供以下功能:
- 集中控制日志记录的方式和时间:在一个地方更改代码即可
- 控制日志发送的位置:将日志同步保存到多个目的地(如Elasticsearch、MongoDB、Postgres等)。
- 自定义日志格式:带有时间戳、颜色日志级别、JSON格式等前缀。
winston实践
实践代码将在项目中增加日志功能,安装依赖:
npm install winston --save
1. 基本配置说明
const { createLogger, format, transports } = require("winston");const logger = createLogger({level: "info", // 设置日志级别format: format.json(), // 设置日志格式为JSONdefaultMeta: { service: "user-service" }, // 添加默认元数据transports: [// 配置日志输出目标]
});
这里使用 createLogger() 创建了一个日志实例,主要配置包括:
- level: 设置日志级别,低于此级别的日志不会记录
- format: 设置日志格式,这里使用JSON格式
- defaultMeta: 为所有日志添加默认的元数据
- transports: 配置日志输出的目标
2. 日志传输配置
// 错误日志文件配置new transports.File({ filename: "error.log", level: "error" }),// 信息日志文件配置new transports.File({filename: "info.log",level: "info",format: format.combine(format((info) => (info.level === "info" ? info : false))()),}),
]
这里配置了两个文件传输:
- error.log - 只记录 error 级别的日志
- info.log - 只记录 info 级别的日志,使用format.combine()组合格式化器
3. 开发环境控制台输出
if (process.env.NODE_ENV !== "production") {logger.add(new transports.Console({format: format.combine(format.colorize(), // 添加颜色format.simple() // 简单格式),}));
}
在非生产环境下添加控制台输出,并:
- 使用 format.colorize() 为日志添加颜色
- 使用 format.simple() 采用简单的输出格式
4. 实用日志方法封装
const logInfo = (message, meta = {}) => {logger.info(message, { timestamp: new Date().toISOString(), ...meta });
};const logError = (message, error, meta = {}) => {const errorObj = error instanceof Error ? error : null;logger.error(message, {timestamp: new Date().toISOString(),message: errorObj ? errorObj.message : error.message,stack: errorObj ? errorObj.stack : error.message,...meta,});
};const logWarn = (message, meta = {}) => {logger.warn(message, { timestamp: new Date().toISOString(), ...meta });
};
封装了三个常用的日志方法:
1. logInfo() - 记录信息日志
- logError() - 记录错误日志,包含错误堆栈
- logWarn() - 记录警告日志
每个方法都:
- 自动添加时间戳
- 支持传入额外的元数据
- 错误日志特别处理了Error对象
5. 使用示例
// 记录普通信息
logInfo("用户登录成功", { userId: "123" });// 记录错误
try {throw new Error("数据库连接失败");
} catch (err) {logError("操作失败", err, { operation: "db_connect" });
}// 记录警告
logWarn("磁盘空间不足", { available: "20GB" })
6. 日志级别说明
Winston 支持以下日志级别(优先级从高到低):
{error: 0, // 错误warn: 1, // 警告info: 2, // 信息http: 3, // HTTP请求verbose: 4, // 详细信息debug: 5, // 调试信息silly: 6 // 追踪信息
}
设置某个级别后,只有优先级相同或更高的日志会被记录。
这个日志工具的实现提供了:
- 分级别的日志记录
- 多目标输出
- 灵活的格式化
- 开发/生产环境区分
- 错误堆栈跟踪
- 时间戳和元数据支持
是一个功能完整的生产级日志解决方案。
完整代码
封装方法
const { createLogger, format, transports } = require("winston");const logger = createLogger({// level 表示日志级别 例如 info warn error 等// 默认info级别 低于info级别的日志不会显示level: "info",// format 日志格式 例如 json格式format: format.json(),// defaultMeta 表示日志的元数据 例如 服务名称defaultMeta: { service: "user-service" },// transports 表示日志的输出方式 例如 输出到文件 输出到控制台transports: [// 输出到error.log文件 级别为error(error fatal)new transports.File({ filename: "error.log", level: "error" }),new transports.File({filename: "info.log",level: "info",format: format.combine(format((info) => (info.level === "info" ? info : false))()),}),],
});// 如果当前不是生产环境 则将日志输出到控制台
// 格式为 (level: message) json.stringify({ ...rest })
if (process.env.NODE_ENV !== "production") {logger.add(new transports.Console({// 使用组合格式format: format.combine(format.colorize(), // 添加颜色format.simple() // simple 为 简洁格式),}));
}// 添加一些实用的日志方法
const logInfo = (message, meta = {}) => {logger.info(message, { timestamp: new Date().toISOString(), ...meta });
};const logError = (message, error, meta = {}) => {const errorObj = error instanceof Error ? error : null;logger.error(message, {timestamp: new Date().toISOString(),message: errorObj ? errorObj.message : error.message,stack: errorObj ? errorObj.stack : error.message,...meta,});
};const logWarn = (message, meta = {}) => {logger.warn(message, { timestamp: new Date().toISOString(), ...meta });
};module.exports = {logger,logInfo,logError,logWarn,
};
koa 日志中间件
const { logInfo, logError } = require("../utils/logger");const loggerMiddleware = async (ctx, next) => {const start = Date.now();const requestId =ctx.request.headers["x-request-id"] || Date.now().toString(36);try {logInfo("请求开始", {requestId,method: ctx.method,url: ctx.url,query: ctx.query,body: ctx.request.body,ip: ctx.ip,userAgent: ctx.headers["user-agent"],});await next();const ms = Date.now() - start;logInfo("请求完成", {requestId,method: ctx.method,url: ctx.url,status: ctx.status,duration: `${ms}ms`,responseBody: ctx.body,});} catch (err) {const ms = Date.now() - start;logError("请求异常", err, {requestId,method: ctx.method,url: ctx.url,status: ctx.status || 500,duration: `${ms}ms`,body: ctx.request.body,});throw err; // 继续抛出错误,让错误处理中间件处理}
};module.exports = loggerMiddleware;
在app使用
const loggerMiddleware = require("../middleware/logger.middleware");const app = new Koa();// 注册日志中间件
app.use(loggerMiddleware);