@nest-packages/nestjs-trace-logger
v0.2.0
Published
一个功能强大的 NestJS 日志追踪包,提供完整的 traceId 支持、上下文管理和自定义格式化功能。
Readme
@nest-packages/nestjs-trace-logger
一个功能强大的 NestJS 日志追踪包,提供完整的 traceId 支持、上下文管理和自定义格式化功能。
特性
- ✅ 完全继承 NestJS Logger - 保持所有原生功能,API 完全兼容
- ✅ 自动 traceId 注入 - 使用 AsyncLocalStorage 自动在异步调用链中传递 traceId
- ✅ 支持 LogLevel 配置 - 兼容
getLogLevels函数,支持生产/开发环境不同日志级别 - ✅ 自定义格式化器 - 支持默认、SLS、JSON 等多种格式化方式
- ✅ 中间件和拦截器 - 开箱即用的 TraceMiddleware 和 TraceInterceptor
- ✅ 类型安全 - 完整的 TypeScript 类型定义
- ✅ 零配置 - 默认配置即可使用,也支持深度自定义
安装
npm install @nest-packages/nestjs-trace-logger
# 或
pnpm add @nest-packages/nestjs-trace-logger
# 或
yarn add @nest-packages/nestjs-trace-logger依赖要求
@nestjs/common: ^11.0.0@nestjs/core: ^11.0.0uuid: ^9.0.0fast-safe-stringify: ^2.1.0
快速开始
1. 在 main.ts 中配置
import { NestFactory } from '@nestjs/core';
import {
TraceConsoleLogger,
TraceMiddleware,
TraceInterceptor,
} from '@nest-packages/nestjs-trace-logger';
import { AppModule } from './app.module';
function getLogLevels(isProduction: boolean): LogLevel[] {
if (isProduction) {
return ['warn', 'error', 'log'];
}
return ['error', 'warn', 'log', 'verbose', 'debug'];
}
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: new TraceConsoleLogger({
logLevels: getLogLevels(process.env.NODE_ENV === 'production'),
compact: true,
json: false,
colors: true,
}),
});
// 注册 Trace 中间件(在健康检查之后)
const traceMiddleware = new TraceMiddleware();
app.use((req, res, next) => traceMiddleware.use(req, res, next));
// 注册 Trace 拦截器
app.useGlobalInterceptors(new TraceInterceptor());
await app.listen(3000);
}
bootstrap();2. 在服务中使用
import { Injectable } from '@nestjs/common';
import { TraceLogger } from '@nest-packages/nestjs-trace-logger';
@Injectable()
export class MyService {
private readonly logger = new TraceLogger(MyService.name);
async doSomething() {
// 自动注入 traceId,支持对象上下文
this.logger.log('操作完成', { userId: 123, action: 'create' });
// 支持 NestJS Logger 的所有方法
this.logger.debug('调试信息');
this.logger.warn('警告信息');
this.logger.error('错误信息', 'stack trace');
}
}API 文档
TraceContextService
用于管理请求上下文和 traceId 的服务类。
import { TraceContextService } from '@nest-packages/nestjs-trace-logger';
// 获取当前 traceId
const traceId = TraceContextService.getTraceId();
// 运行函数并设置 traceId(同步)
TraceContextService.run('trace-id-123', () => {
// 在这个函数内部,traceId 自动可用
const id = TraceContextService.getTraceId(); // 'trace-id-123'
});
// 运行函数并设置 traceId(异步)
await TraceContextService.runAsync('trace-id-123', async () => {
// 异步函数内部,traceId 自动可用
const id = TraceContextService.getTraceId(); // 'trace-id-123'
});
// 设置自定义上下文数据
TraceContextService.set('userId', 123);
const userId = TraceContextService.get<number>('userId'); // 123
// 获取完整上下文
const context = TraceContextService.getContext();TraceLogger
完全继承 NestJS Logger 的日志类,自动注入 traceId。
import { TraceLogger } from '@nest-packages/nestjs-trace-logger';
// 创建实例
const logger = new TraceLogger('MyService');
// 使用默认格式化器
logger.log('消息', { key: 'value' });
// 输出: [traceId] 消息 | {"traceId":"abc123","key":"value"}
// 使用自定义格式化器
import { JsonFormatter } from '@nest-packages/nestjs-trace-logger';
logger.setFormatter(new JsonFormatter());
logger.log('消息', { key: 'value' });
// 输出: {"message":"消息","timestamp":"2024-01-01T00:00:00.000Z","traceId":"abc123","key":"value"}TraceConsoleLogger
类似 ConsoleLogger,支持 LogLevel 配置。
import { TraceConsoleLogger, LogLevel } from '@nest-packages/nestjs-trace-logger';
const logger = new TraceConsoleLogger({
logLevels: ['error', 'warn', 'log'] as LogLevel[],
compact: true,
json: false,
colors: true,
formatter: new SlsFormatter(), // 可选:自定义格式化器
});TraceMiddleware
自动从请求头获取或生成 traceId 的中间件。
import { TraceMiddleware } from '@nest-packages/nestjs-trace-logger';
// 在 main.ts 中注册
const traceMiddleware = new TraceMiddleware();
app.use((req, res, next) => traceMiddleware.use(req, res, next));功能:
- 从
x-request-id请求头获取 traceId - 如果没有,自动生成 UUID 并截取前 8 位
- 在响应头中设置
X-Request-ID - 使用 AsyncLocalStorage 存储上下文
TraceInterceptor
确保 NestJS 执行上下文中 traceId 可用的拦截器。
import { TraceInterceptor } from '@nest-packages/nestjs-trace-logger';
// 在 main.ts 中注册
app.useGlobalInterceptors(new TraceInterceptor());格式化器
DefaultFormatter(默认)
格式:[traceId] message | {context}
import { DefaultFormatter } from '@nest-packages/nestjs-trace-logger';
const logger = new TraceLogger('MyService', new DefaultFormatter());
logger.log('操作完成', { userId: 123 });
// 输出: [abc123] 操作完成 | {"traceId":"abc123","userId":123}SlsFormatter(阿里云 SLS)
适配阿里云 SLS 日志格式,traceId 包含在 context 中。
import { SlsFormatter } from '@nest-packages/nestjs-trace-logger';
const logger = new TraceLogger('MyService', new SlsFormatter());
logger.log('操作完成', { userId: 123 });
// 输出: 操作完成 | {"traceId":"abc123","userId":123}JsonFormatter(JSON 格式)
输出完整的 JSON 格式日志。
import { JsonFormatter } from '@nest-packages/nestjs-trace-logger';
const logger = new TraceLogger('MyService', new JsonFormatter());
logger.log('操作完成', { userId: 123 });
// 输出: {"message":"操作完成","timestamp":"2024-01-01T00:00:00.000Z","traceId":"abc123","userId":123}自定义格式化器
import { IFormatter, LogContext } from '@nest-packages/nestjs-trace-logger';
class CustomFormatter implements IFormatter {
format(message: string, traceId?: string, context?: LogContext): string {
return `[${traceId || 'N/A'}] ${message} - ${JSON.stringify(context || {})}`;
}
}
const logger = new TraceLogger('MyService', new CustomFormatter());高级用法
在队列任务中使用
import { TraceContextService } from '@nest-packages/nestjs-trace-logger';
@Processor('my-queue')
export class MyProcessor {
@Process()
async handle(job: Job) {
const traceId = job.data.traceId || 'generated-id';
await TraceContextService.runAsync(traceId, async () => {
// 在这个异步函数中,traceId 自动可用
const logger = new TraceLogger(MyProcessor.name);
logger.log('处理任务', { jobId: job.id });
});
}
}继承 TraceLogger 创建自定义 Logger
import { TraceLogger, LogContext } from '@nest-packages/nestjs-trace-logger';
export class AlarmLogger extends TraceLogger {
constructor(context: string) {
super(context);
}
log(message: string, context?: LogContext | string): void {
if (context && typeof context === 'object') {
super.log(message, context as LogContext);
} else {
super.log(message, context);
}
}
}使用 LoggerManager
import { LoggerManager, TraceLogger } from '@nest-packages/nestjs-trace-logger';
// 设置自定义 Logger 工厂
LoggerManager.setLoggerFactory((context: string) => {
return new TraceLogger(context, new SlsFormatter());
});
// 创建 Logger
const logger = LoggerManager.createLogger('MyService');在模块配置中使用
import { TraceLogger } from '@nest-packages/nestjs-trace-logger';
@Module({
imports: [
SomeModule.forRootAsync({
useFactory: () => ({
loggerFactory: (context: string) => new TraceLogger(context),
}),
}),
],
})
export class AppModule {}配置选项
TraceConsoleLoggerOptions
interface TraceConsoleLoggerOptions {
/**
* 日志级别数组
* @example ['error', 'warn', 'log', 'verbose', 'debug']
*/
logLevels?: LogLevel[];
/**
* 是否使用紧凑格式
* @default false
*/
compact?: boolean;
/**
* 是否输出 JSON 格式
* @default false
*/
json?: boolean;
/**
* 是否使用颜色
* @default true
*/
colors?: boolean;
/**
* 自定义格式化器
*/
formatter?: IFormatter;
}最佳实践
1. 日志级别配置
根据环境配置不同的日志级别:
function getLogLevels(isProduction: boolean): LogLevel[] {
if (isProduction) {
return ['warn', 'error', 'log'];
}
return ['error', 'warn', 'log', 'verbose', 'debug'];
}2. 在异步操作中保持 traceId
使用 TraceContextService.runAsync 确保异步操作中 traceId 可用:
await TraceContextService.runAsync(traceId, async () => {
// 所有异步操作都会自动包含 traceId
await someAsyncOperation();
await anotherAsyncOperation();
});3. 使用对象上下文
传递结构化数据而不是字符串拼接:
// ✅ 推荐
logger.log('用户登录', { userId: 123, ip: '192.168.1.1' });
// ❌ 不推荐
logger.log(`用户登录: userId=${userId}, ip=${ip}`);4. 错误日志记录
try {
// 业务逻辑
} catch (error) {
logger.error('操作失败', {
error: error.message,
stack: error.stack,
userId: 123
});
}类型定义
// TraceContext
interface TraceContext {
traceId: string;
startTime: number;
[key: string]: any;
}
// LogContext
type LogContext = Record<string, unknown>;
// ILogger
interface ILogger {
log(message: string, ...context: any[]): void;
debug(message: string, ...context: any[]): void;
warn(message: string, ...context: any[]): void;
error(message: string, ...context: any[]): void;
}
// IFormatter
interface IFormatter {
format(message: string, traceId?: string, context?: LogContext): string;
}许可证
MIT
贡献
欢迎提交 Issue 和 Pull Request!
