@m16khb/nestjs-traceable
v1.3.3
Published
NestJS용 Traceable 데코레이터 - 메서드 실행 추적 및 로깅 라이브러리
Downloads
1,363
Maintainers
Readme
@m16khb/nestjs-traceable
TraceId-based distributed tracing library for NestJS applications.
Simplifies debugging and monitoring in distributed systems by connecting requests across various communication channels (HTTP, gRPC, Kafka, Cron, BullMQ) with a single traceId.
Features
- Automatic traceId Propagation: Maintains traceId continuity across HTTP, gRPC, Kafka, and BullMQ
- Winston Logger Integration: Automatically injects traceId into all logs
- Abstract Class Pattern: Eliminates boilerplate code
- Zero Configuration: Ready to use with minimal setup
- Full TypeScript Support: Type-safe implementation
Installation
# npm
npm install @m16khb/nestjs-traceable nestjs-cls
# yarn
yarn add @m16khb/nestjs-traceable nestjs-cls
# pnpm
pnpm add @m16khb/nestjs-traceable nestjs-clsOptional Dependencies
Install additional packages based on the features you need:
# Winston logger (recommended)
pnpm add nest-winston winston dayjs
# BullMQ job tracking
pnpm add @nestjs/bullmq bullmq
# Cron job tracking
pnpm add @nestjs/scheduleQuick Start
1. Basic Setup
// app.module.ts
import { Module } from '@nestjs/common';
import { TraceModule } from '@m16khb/nestjs-traceable';
@Module({
imports: [
TraceModule.forRoot({
headerName: 'X-Trace-Id', // default value
}),
],
})
export class AppModule {}2. Using traceId in Services
// payment.service.ts
import { Injectable } from '@nestjs/common';
import { TraceContextService } from '@m16khb/nestjs-traceable';
@Injectable()
export class PaymentService {
constructor(private readonly traceContext: TraceContextService) {}
async processPayment(orderId: string): Promise<void> {
const traceId = this.traceContext.getTraceId();
console.log(`[${traceId}] Processing payment for order ${orderId}`);
// ...
}
}3. Winston Logger Setup (Recommended)
// app.module.ts
import { Module } from '@nestjs/common';
import { TraceModule, TraceableLoggerModule } from '@m16khb/nestjs-traceable';
@Module({
imports: [
TraceModule.forRoot(),
TraceableLoggerModule.forRoot({
level: 'info',
isLocal: process.env.NODE_ENV !== 'production',
appName: 'MyApp',
traceIdLength: 8, // traceId display length (0: full)
}),
],
})
export class AppModule {}// payment.service.ts
import { Injectable } from '@nestjs/common';
import { TraceableLogger } from '@m16khb/nestjs-traceable';
@Injectable()
export class PaymentService {
private readonly logger: TraceableLogger;
constructor(logger: TraceableLogger) {
this.logger = logger.setContext('PaymentService');
}
async processPayment(orderId: string): Promise<void> {
this.logger.log('Payment processing started', { orderId });
// Output: [MyApp] 12345 - 12/06/2025, 12:30:45 AM LOG [PaymentService] [abc12345] Payment processing started
try {
await this.doPayment();
this.logger.log('Payment completed', { orderId, amount: 10000 });
} catch (error) {
this.logger.error('Payment failed', error);
}
}
}Integration Patterns
HTTP Request Tracking
TraceId is automatically handled for HTTP requests with minimal setup:
- Uses
X-Trace-Idheader value if present - Generates new UUID v4 traceId if not present
- Includes traceId in response headers
BullMQ Job Tracking
// payment.processor.ts
import { Processor } from '@nestjs/bullmq';
import { TraceableProcessor, TraceableJobData, TraceableLogger } from '@m16khb/nestjs-traceable';
import { ClsService } from 'nestjs-cls';
import { Job } from 'bullmq';
interface PaymentJobData extends TraceableJobData {
orderId: string;
amount: number;
}
@Processor('payment')
export class PaymentProcessor extends TraceableProcessor<PaymentJobData, void> {
constructor(
cls: ClsService,
private readonly paymentService: PaymentService,
private readonly logger: TraceableLogger,
) {
super(cls);
}
protected async executeJob(job: Job<PaymentJobData>): Promise<void> {
// traceId is already set in CLS!
this.logger.log(`Processing payment: ${job.data.orderId}`);
await this.paymentService.process(job.data);
}
}// payment-queue.service.ts
import { Injectable } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bullmq';
import { Queue } from 'bullmq';
import { TraceableQueueService } from '@m16khb/nestjs-traceable';
import { ClsService } from 'nestjs-cls';
interface PaymentJobData {
orderId: string;
amount: number;
}
@Injectable()
export class PaymentQueueService extends TraceableQueueService<PaymentJobData> {
constructor(
@InjectQueue('payment') queue: Queue,
cls: ClsService,
) {
super(queue, cls);
}
async addPaymentJob(orderId: string, amount: number): Promise<string> {
// traceId is automatically included in job.data
return this.addJob('process', { orderId, amount });
}
}Cron Job Tracking
// report.cron.ts
import { Injectable } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { TraceableCronService, TraceableLogger } from '@m16khb/nestjs-traceable';
import { ClsService } from 'nestjs-cls';
@Injectable()
export class ReportCronService extends TraceableCronService {
constructor(
cls: ClsService,
private readonly reportService: ReportService,
private readonly logger: TraceableLogger,
) {
super(cls);
}
@Cron('0 0 * * *', { name: 'daily-report', timeZone: 'Asia/Seoul' })
async generateDailyReport(): Promise<void> {
await this.runWithTrace(async () => {
this.logger.log('[Cron] Starting daily report generation');
await this.reportService.generate();
});
}
}gRPC Service Tracking
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { TraceModule, TraceGrpcInterceptor } from '@m16khb/nestjs-traceable';
@Module({
imports: [TraceModule.forRoot()],
providers: [
{ provide: APP_INTERCEPTOR, useClass: TraceGrpcInterceptor },
],
})
export class AppModule {}Kafka Event Tracking
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { TraceModule, TraceKafkaInterceptor } from '@m16khb/nestjs-traceable';
@Module({
imports: [TraceModule.forRoot()],
providers: [
{ provide: APP_INTERCEPTOR, useClass: TraceKafkaInterceptor },
],
})
export class AppModule {}Passing traceId from producer:
import { createKafkaTraceHeaders } from '@m16khb/nestjs-traceable';
await this.kafkaClient.emit('topic', {
key: 'key',
value: JSON.stringify(data),
headers: createKafkaTraceHeaders(traceId),
});Storing Custom Context Values
Store any request-scoped data (userId, requestIp, etc.) alongside traceId:
// auth.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { TraceContextService } from '@m16khb/nestjs-traceable';
@Injectable()
export class UserContextInterceptor implements NestInterceptor {
constructor(private readonly traceContext: TraceContextService) {}
intercept(context: ExecutionContext, next: CallHandler) {
const request = context.switchToHttp().getRequest();
// Store user context
this.traceContext.set('userId', request.user?.id);
this.traceContext.set('requestIp', request.ip);
this.traceContext.set('userAgent', request.headers['user-agent']);
return next.handle();
}
}// order.service.ts
import { Injectable } from '@nestjs/common';
import { TraceContextService } from '@m16khb/nestjs-traceable';
@Injectable()
export class OrderService {
constructor(private readonly traceContext: TraceContextService) {}
async createOrder(orderData: OrderDto) {
const traceId = this.traceContext.getTraceId();
const userId = this.traceContext.get<string>('userId');
const requestIp = this.traceContext.get<string>('requestIp');
console.log({
traceId,
userId,
requestIp,
action: 'create_order',
});
// Order creation logic...
}
}Error Chain Tracking (ES2022 Error.cause)
TraceableLogger automatically tracks Error.cause chains for better debugging:
// service.ts
import { Injectable } from '@nestjs/common';
import { TraceableLogger } from '@m16khb/nestjs-traceable';
@Injectable()
export class PaymentService {
constructor(private readonly logger: TraceableLogger) {
this.logger = this.logger.setContext('PaymentService');
}
async processPayment(orderId: string) {
try {
await this.chargeCard();
} catch (err) {
// Inner error
const inner = new Error('Connection timeout');
// Wrap with cause
const outer = new Error('Payment gateway unavailable', { cause: inner });
// Log with automatic chain tracking
this.logger.error('Payment failed', outer);
// Logged output includes:
// - error: "Payment gateway unavailable"
// - errorChain: "Payment gateway unavailable → Connection timeout"
// - rootCause: "Connection timeout"
// - stack: [full stack trace]
}
}
}Error Utility Functions (exported for custom usage):
import {
getErrorChain,
getRootCause,
getFullErrorDetails,
formatErrorForLogging,
} from '@m16khb/nestjs-traceable';
// Get full error chain as string
const chain = getErrorChain(error);
// "Payment failed → Gateway error → Connection timeout"
// Get root cause
const root = getRootCause(error);
// Original Error object at the end of the chain
// Get comprehensive error details
const details = getFullErrorDetails(error);
// {
// message: "Payment failed",
// chain: "Payment failed → Gateway error → Connection timeout",
// rootCause: { message: "Connection timeout", name: "Error" },
// depth: 3,
// stack: "...",
// name: "Error"
// }API Reference
TraceModule
| Method | Description |
|--------|-------------|
| forRoot(options?) | Basic module configuration |
| forRootAsync(options) | Async module configuration (for ConfigService, etc.) |
| register(options?) | Alias for forRoot |
TraceModuleOptions
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| headerName | string | 'X-Trace-Id' | traceId header name |
TraceContextService
TraceId Management
| Method | Return Type | Description |
|--------|-------------|-------------|
| getTraceId() | string \| undefined | Get current traceId |
| setTraceId(traceId) | void | Set traceId |
| generateTraceId() | string | Generate and set new traceId |
| hasContext() | boolean | Check if traceId exists |
| isActive() | boolean | Check if CLS is active |
| run(fn, traceId?) | T | Run sync function in new context |
| runAsync(fn, traceId?) | Promise<T> | Run async function in new context |
Custom Context Values
| Method | Return Type | Description |
|--------|-------------|-------------|
| set<T>(key, value) | void | Store custom value in CLS |
| get<T>(key) | T \| undefined | Retrieve custom value from CLS |
| has(key) | boolean | Check if key exists |
| delete(key) | void | Delete value from CLS |
TraceableLogger
| Method | Level | Description |
|--------|-------|-------------|
| log(message, meta?) | info | General information log |
| error(message, errorOrMeta?) | error | Error log |
| warn(message, meta?) | warn | Warning log |
| debug(message, meta?) | debug | Debug log |
| verbose(message, meta?) | verbose | Verbose log |
| query(message, meta?) | query | SQL query log |
| slowQuery(message, durationMs, meta?) | warn | Slow query warning |
| fatal(message, errorOrMeta?) | error | Fatal error |
| setContext(context) | TraceableLogger | Set context |
TraceableLoggerModuleOptions
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| level | LogLevel | 'info' | Minimum log level |
| isLocal | boolean | NODE_ENV !== 'production' | Toggle Pretty/JSON output |
| appName | string | 'Nest' | Log prefix |
| traceIdLength | number | 8 | traceId display length |
| timestampFormat | () => string | dayjs-based | Timestamp format |
Output Formats
Local (Pretty)
[MyApp] 12345 - 12/06/2025, 12:30:45 AM LOG [PaymentService] [abc12345] Payment processing startedProduction (JSON)
{"timestamp":"2025-12-06T00:30:45.123+0900","level":"info","context":"PaymentService","traceId":"abc12345-def6-7890","message":"Payment processing started"}Requirements
- Node.js 20+
- NestJS 10.x / 11.x
- TypeScript 5.7+
Dependencies
Required (peerDependencies)
| Package | Version |
|---------|---------|
| @nestjs/common | ^10.0.0 || ^11.0.0 |
| @nestjs/core | ^10.0.0 || ^11.0.0 |
| nestjs-cls | ^4.0.0 || ^5.0.0 |
| rxjs | ^7.0.0 |
Optional (peerDependencies)
| Package | Version | Purpose |
|---------|---------|---------|
| @nestjs/bullmq | ^10.0.0 || ^11.0.0 | BullMQ support |
| bullmq | ^5.0.0 | BullMQ Worker |
| @nestjs/schedule | ^4.0.0 || ^5.0.0 | Cron support |
| nest-winston | ^1.9.0 || ^2.0.0 | Winston integration |
| winston | ^3.0.0 | Logging |
| dayjs | ^1.11.0 | Timestamps |
