引言:为什么在 TypeScript 中日志记录很重要
日志记录不仅仅是把事件写入文件。在现代 TypeScript 应用程序中,配置良好的日志记录是调试、监控和性能分析的基础。没有高质量的日志,开发者会花费数小时重现错误,而运维团队也无法快速定位瓶颈。
TypeScript 作为 JavaScript 的超集,提供了严格的类型检查,使得创建更可预测、更安全的日志系统成为可能。然而,在生产环境中使用简单的 console.log() 是一种反模式,会导致信息噪音和重要数据的丢失。
在本文中,我们将探讨 TypeScript 中日志记录的最佳实践:从库的选择到日志的结构化以及上下文处理。
选择日志记录库
在 TypeScript/Node.js 生态系统中,有几个流行的库。我们来看三个主要选项:
- Winston — 最流行的库,具有灵活的传输配置(输出到控制台、文件、数据库)。支持日志级别和格式化。
- Pino — 一个现代库,专注于性能。非常适合高负载系统。
- Bunyan — 一个不太流行但稳定的选项。所有日志记录器都以 JSON 格式输出数据。
对于典型的 TypeScript 应用程序,我们推荐 Winston 或 Pino。使用 TypeScript 配置 Winston 的示例:
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
],
});
logger.info('服务器已启动', { port: 3000 });
与 TypeScript 集成:类型和接口
为了充分利用类型检查,使用接口为日志记录器创建一个包装器:
interface LoggerOptions {
level?: string;
service?: string;
}
interface ILogger {
info(message: string, meta?: unknown): void;
error(message: string, error?: Error): void;
warn(message: string, meta?: unknown): void;
}
这将允许您严格控制传递数据的类型,并在编译阶段避免错误。
日志级别及其正确使用
标准日志级别(从最严重到最不严重):error、warn、info、debug、trace。重要的是遵循统一的语义:
- error — 仅用于需要开发者关注的不可恢复的错误。
- warn — 用于潜在的问题情况,这些情况不会破坏应用程序的运行。
- info — 用于关键事件:服务器启动、请求处理、成功授权。
- debug — 用于开发过程中的调试。不要在生产环境中启用此级别。
根据环境动态更改级别的示例:
const logLevel = process.env.NODE_ENV === 'production' ? 'warn' : 'debug';
const logger = winston.createLogger({ level: logLevel });
结构化日志和上下文
使用 JSON 格式的结构化日志代替字符串消息。这使得可以使用 ELK 栈(Elasticsearch、Logstash、Kibana)或其他系统轻松解析日志。
务必添加上下文信息:请求 ID、用户名、服务名称、时间戳。使用 Pino 的示例:
import pino from 'pino';
const logger = pino({
level: 'info',
mixin() {
return { service: 'my-api' };
},
});
// 添加上