
Security News
Deno 2.6 + Socket: Supply Chain Defense In Your CLI
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.
@beincom/nestjs-trace
Advanced tools
OpenTelemetry tracing module for NestJS applications. Provides automatic instrumentation, decorators for manual tracing, and flexible configuration options.
yarn add @beincom/nestjs-trace
# or
npm install @beincom/nestjs-trace
Note: This package includes all required dependencies. Just install @beincom/nestjs-trace.
For better IDE support (auto-import suggestions): Add @opentelemetry/api to your devDependencies:
yarn add -D @opentelemetry/api
This will help your IDE suggest imports for OpenTelemetry types like SpanKind, Span, etc.
This package is compiled with ES2022 target, which requires Node.js 18+ for optimal compatibility.
import { Module } from '@nestjs/common';
import { TraceModule } from '@beincom/nestjs-trace';
@Module({
imports: [
TraceModule.forRoot({
isEnabled: true, // Enable/disable tracing
serviceName: 'my-service', // Service name for traces
env: process.env.NODE_ENV || 'development', // Environment name
collectorUrl: 'http://localhost:4318/v1/traces', // Collector URL
// For OTLP/HTTP (default): use port 4318 (e.g., http://localhost:4318/v1/traces)
// For OTLP/GRPC: use port 4317 (e.g., http://localhost:4317)
samplerRatio: 1.0, // Sampling ratio (0.0 to 1.0)
traceDebug: false, // Enable OTel diagnostic logging (for debugging)
}),
],
})
export class AppModule {}
TraceModule.forRoot({
isEnabled: true, // Enable/disable tracing
serviceName: 'my-service', // Service name for traces
env: 'production', // Environment name
collectorUrl: 'http://...', // Collector URL (see below)
samplerRatio: 1.0, // Sampling ratio (0.0 to 1.0)
concurrencyLimit: 10, // OTLP exporter concurrency
timeout: 10000, // Export timeout in ms
enableAutoInstrumentations: true, // Enable auto-instrumentations (default: true)
traceDebug: false, // Enable diagnostic logging
});
OTLP/HTTP (Default):
4318http://localhost:4318/v1/tracesTraceModule.forRoot({
// ...
collectorUrl: 'http://localhost:4318/v1/traces',
traceExporter: 'otlp', // or 'otlp-http' (both are equivalent)
});
OTLP/gRPC:
4317http://localhost:4317TraceModule.forRoot({
// ...
collectorUrl: 'http://localhost:4317',
traceExporter: 'otlp-grpc',
});
Zipkin:
local or development environments (unless overridden)traceExporter: 'zipkin' or useLocalExporter: trueTraceModule.forRoot({
// ...
collectorUrl: 'http://localhost:9411/api/v2/spans',
traceExporter: 'zipkin',
});
If you want to use only manual tracing (decorators) without auto-instrumentations:
TraceModule.forRoot({
// ... other config ...
enableAutoInstrumentations: false, // Disable all auto-instrumentations
});
import { Span } from '@opentelemetry/api';
import { ElasticsearchInstrumentation } from 'opentelemetry-instrumentation-elasticsearch';
TraceModule.forRoot({
// ... basic config ...
// Auto-instrumentation options
// Uses InstrumentationConfigMap type from @opentelemetry/auto-instrumentations-node
// Pass configuration directly to OpenTelemetry's getNodeAutoInstrumentations()
// Must use full package names (e.g., '@opentelemetry/instrumentation-http')
// Supported instrumentations include: http, fs, pino, express, fastify, mongodb, redis, pg, etc.
autoInstrumentations: {
'@opentelemetry/instrumentation-http': {
enabled: true,
requireParentforOutgoingSpans: true,
applyCustomAttributesOnSpan: (span: Span, request, response) => {
span.setAttribute('status_code', response.statusCode);
// Add custom logic here
},
ignoreIncomingPaths: ['/health/readyz', '/health/livez', '/metrics'],
},
'@opentelemetry/instrumentation-fs': {
enabled: true,
requireParentSpan: true,
},
'@opentelemetry/instrumentation-pino': {
enabled: true,
logHook: (span, record) => {
// Custom log hook
},
},
// Note: Only instrumentations from OpenTelemetry's InstrumentationMap are supported here
// For custom or third-party instrumentations, use additionalInstrumentations instead
},
// Additional instrumentation instances
// Use for instruments not included in getNodeAutoInstrumentations()
// (e.g., ElasticsearchInstrumentation, custom instruments)
additionalInstrumentations: [
new ElasticsearchInstrumentation({
moduleVersionAttributeName: 'elasticsearchClient.version',
}),
],
// Additional resource attributes
resourceAttributes: {
'service.version': '1.0.0',
'deployment.region': 'us-east-1',
},
// Custom trace exporter
// Options: 'otlp' | 'otlp-http' | 'otlp-grpc' | 'zipkin' | SpanExporter instance
// 'otlp' is an alias for 'otlp-http'
traceExporter: 'otlp', // or 'otlp-http', 'otlp-grpc', 'zipkin', or custom SpanExporter
// Use local exporter (Zipkin) instead of OTLP
// If true, uses Zipkin exporter regardless of traceExporter setting
// Default: false (auto-detect based on env: uses Zipkin for 'local' or 'development')
useLocalExporter: false,
// Filter patterns for span names to exclude
// Default: ['connect', 'create nest app', 'nestfactory.create', 'nestfactory', 'bootstrap', 'app.listen', 'application startup', 'nest application']
filterSpanPatterns: ['connect', 'bootstrap'],
// Custom span processor (wraps the exporter)
customSpanProcessor: myCustomProcessor,
// Custom resource detectors
customResourceDetectors: [myCustomDetector],
// Custom propagators
customPropagators: [myCustomPropagator],
});
If autoInstrumentations is not provided:
getNodeAutoInstrumentations())If autoInstrumentations is provided:
InstrumentationConfigMap type from @opentelemetry/auto-instrumentations-nodegetNodeAutoInstrumentations() without any merging'@opentelemetry/instrumentation-http' (shorthand keys are not supported)InstrumentationMap are supported@opentelemetry/instrumentation-http, @opentelemetry/instrumentation-fs, @opentelemetry/instrumentation-pino, @opentelemetry/instrumentation-express, @opentelemetry/instrumentation-fastify, @opentelemetry/instrumentation-mongodb, @opentelemetry/instrumentation-redis, @opentelemetry/instrumentation-pg, and many moreExample:
TraceModule.forRoot({
// ... other config ...
autoInstrumentations: {
// Use full package name
'@opentelemetry/instrumentation-http': {
enabled: true,
requireParentforOutgoingSpans: true,
applyCustomAttributesOnSpan: (span, request, response) => {
span.setAttribute('status_code', response.statusCode);
},
ignoreIncomingPaths: ['/health', '/metrics'],
},
'@opentelemetry/instrumentation-fs': {
enabled: true,
requireParentSpan: true,
},
},
});
For instruments that are not included in OpenTelemetry's InstrumentationMap (like Elasticsearch, custom instruments, or third-party instrumentations), use additionalInstrumentations:
Note: If an instrumentation is not part of getNodeAutoInstrumentations(), you cannot configure it via autoInstrumentations. Instead, instantiate it directly and add it to additionalInstrumentations.
import { ElasticsearchInstrumentation } from 'opentelemetry-instrumentation-elasticsearch';
TraceModule.forRoot({
// ... other config ...
additionalInstrumentations: [
new ElasticsearchInstrumentation({
moduleVersionAttributeName: 'elasticsearchClient.version',
}),
],
});
import { TraceClass } from '@beincom/nestjs-trace';
@TraceClass({ spanPrefix: 'UserService' })
export class UserService {
async createUser(data: CreateUserDto) {
// Automatically traced as 'UserService.createUser'
}
@NoTrace
async privateMethod() {
// This method won't be traced
}
}
import { TraceMethod } from '@beincom/nestjs-trace';
export class OrderService {
@TraceMethod('OrderService.processPayment', (args) => ({
orderId: args[0],
amount: args[1]?.amount,
}))
async processPayment(orderId: string, payment: PaymentInfo) {
// Traced with custom attributes
}
}
import { TraceEventLog } from '@beincom/nestjs-trace';
export class UserListener {
@TraceEventLog({
eventNames: ['user.created', 'user.updated'],
traceOptions: { spanPrefix: 'UserListener' },
})
async handleUserEvent(event: UserEvent) {
// Automatically traced when events are emitted
}
}
import { startTraceSpan, TraceService } from '@beincom/nestjs-trace';
// Start a span manually
const result = await startTraceSpan(
'MyOperation',
{ userId: '123' },
async (span) => {
span.setAttribute('custom.attr', 'value');
return await doSomething();
},
{
attributes: { layer: 'service' },
useLinks: true, // For async operations
}
);
// Inject trace context for propagation
// Note: injectToPropagation is an instance method, inject TraceService via DI
import { Injectable } from '@nestjs/common';
import { TraceService } from '@beincom/nestjs-trace';
@Injectable()
export class MyService {
constructor(private readonly traceService: TraceService) {}
async sendToAnotherService() {
const payload = {};
this.traceService.injectToPropagation(payload);
// Send payload to another service
}
}
// Or use static methods for tracer access
const tracer = TraceService.getTracer();
const activeSpan = TraceService.getActiveSpan();
Attributes are key-value metadata attached to spans. They help you:
@TraceMethod('UserService.createUser', { layer: 'service', action: 'createUser' })
async createUser(userId: string) {
// Every span will have layer and action attributes
}
@TraceMethod('OrderService.payOrder', args => ({
orderId: args[0],
paymentType: args[1]?.type,
}))
payOrder(orderId: string, payment: PaymentInfo) {
// Attributes are set based on actual arguments
}
@TraceMethod('UserService.updateUser', async args => {
const meta = await getUserMeta(args[0]);
return { userId: args[0], ...meta };
})
async updateUser(userId: string) {
// Attributes can be fetched asynchronously
}
Used for synchronous operations within the same request lifecycle:
Used for asynchronous operations where parent span may have already ended:
@TraceClass({ useLinks: true })
export class QueueProcessor {
@Process('job.name')
async handleJob(job: Job) {
// This span will be a root span with link to producer span
}
}
The trace module initializes immediately when imported to ensure instrumentation is active before other modules (like Redis) load. This is handled automatically by TraceModule.forRoot().
import { TraceModule } from '@beincom/nestjs-trace';
@Module({
imports: [
TraceModule.forRoot({
isEnabled: true,
serviceName: 'my-service',
// ... other options
}),
],
})
export class AppModule {}
If you need manual control, you can use:
import { initializeTrace } from '@beincom/nestjs-trace';
// Initialize before other imports
const sdk = initializeTrace({
isEnabled: true,
serviceName: 'my-service',
// ... other options
});
You can create a separate bootstrap file and import it in main.ts:
Create trace.bootstrap.ts:
import { initializeTrace } from '@beincom/nestjs-trace';
import { getTraceConfig } from './config'; // Your config helper
import config from './config'; // Your config object
export default initializeTrace(getTraceConfig(config().trace));
In main.ts:
// Import trace bootstrap FIRST, before other imports
import './trace.bootstrap';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
Note:
The TraceModule automatically handles graceful shutdown of the OpenTelemetry SDK when your NestJS application shuts down. This ensures that:
Graceful shutdown is automatically handled in all cases as long as TraceModule is present in your application (even if you're using initializeTrace() directly).
The TraceModule implements NestJS's OnApplicationShutdown lifecycle hook and uses the same SDK instance created by initializeTrace(). The SDK will automatically shut down when:
app.close() is calledThis works automatically for:
TraceModule.forRoot() - Automatic shutdown ✓initializeTrace() + TraceModule in app - Automatic shutdown ✓TraceModule in app - Automatic shutdown ✓Example with TraceModule:
import { Module } from '@nestjs/common';
import { TraceModule } from '@beincom/nestjs-trace';
@Module({
imports: [
TraceModule.forRoot({
isEnabled: true,
serviceName: 'my-service',
// ... other options
}),
],
})
export class AppModule {}
// Graceful shutdown is handled automatically - no additional code needed!
Example with Manual Initialization + TraceModule:
// main.ts - Initialize trace first
import './trace.bootstrap'; // Calls initializeTrace()
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
// app.module.ts - Import TraceModule (without forRoot) for TraceService and automatic shutdown
import { Module } from '@nestjs/common';
import { TraceModule } from '@beincom/nestjs-trace';
@Module({
imports: [
TraceModule, // Module is present for TraceService and automatic shutdown handling
// TraceModule.onApplicationShutdown() will automatically call getTraceSDK().shutdown()
// ... other modules
],
})
export class AppModule {}
// Graceful shutdown still works automatically because TraceModule is in the app!
Only needed if: You're using initializeTrace() directly (Method 2 or Method 3) AND you don't have TraceModule anywhere in your application.
If you don't have TraceModule in your app, you need to manually handle shutdown:
Option 1: Using NestJS App Lifecycle
import { Injectable, OnApplicationShutdown } from '@nestjs/common';
import { getTraceSDK } from '@beincom/nestjs-trace';
@Injectable()
export class TraceShutdownService implements OnApplicationShutdown {
async onApplicationShutdown() {
const sdk = getTraceSDK();
if (sdk) {
await sdk.shutdown();
}
}
}
Option 2: Using Process Signals
import { getTraceSDK } from '@beincom/nestjs-trace';
async function shutdown() {
const sdk = getTraceSDK();
if (sdk) {
await sdk.shutdown();
}
process.exit(0);
}
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
During shutdown:
timeout configuration)Note: Ensure your application allows enough time for the shutdown process to complete. The shutdown timeout is controlled by your timeout configuration option (default: 10000ms).
TraceModule.forRoot(options: TraceModuleOptions): Configure the trace module synchronouslyTraceModule.forRootAsync(options): Configure the trace module asynchronously with ConfigService@TraceClass(options?): Automatically trace all methods in a class@TraceMethod(spanNameOrOptions?, attributesOrOptions?): Trace a specific method@TraceEventLog({ eventNames, traceOptions?, onEventOptions? }): Trace event handlers@NoTrace: Exclude a method from tracing (used with TraceClass)startTraceSpan<T, R>(spanName, args, handler, options?): Manually start a trace spansetErrorAttributes(span, err): Set error attributes on a spanhasPropagationHeaders(obj): Check if object has trace propagation headersfindHeadersFromArgs(args): Find headers from function argumentscreateLinkFromHeaders(headers, linkAttributes?): Create span link from headersStatic Methods:
TraceService.getTracer(): Get the OpenTelemetry tracerTraceService.getActiveSpan(): Get the currently active spanInstance Methods:
traceService.injectToPropagation(payload, span?): Inject trace context into payload (requires DI)MIT
FAQs
OpenTelemetry tracing module for NestJS applications
We found that @beincom/nestjs-trace demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 14 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.

Security News
New DoS and source code exposure bugs in React Server Components and Next.js: what’s affected and how to update safely.

Security News
Socket CEO Feross Aboukhadijeh joins Software Engineering Daily to discuss modern software supply chain attacks and rising AI-driven security risks.