🚨 Shai-Hulud Strikes Again:834 Packages Compromised.Technical Analysis
Socket
Book a DemoInstallSign in
Socket

@beincom/nestjs-trace

Package Overview
Dependencies
Maintainers
14
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@beincom/nestjs-trace

OpenTelemetry tracing module for NestJS applications

latest
npmnpm
Version
1.0.2
Version published
Maintainers
14
Created
Source

@beincom/nestjs-trace

OpenTelemetry tracing module for NestJS applications. Provides automatic instrumentation, decorators for manual tracing, and flexible configuration options.

Installation

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.

Minimum Requirements

  • Node.js: 18.x or higher
  • NestJS: 9.x or higher (10.x recommended)
  • TypeScript: 4.9.x or higher

This package is compiled with ES2022 target, which requires Node.js 18+ for optimal compatibility.

Quick Start

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 {}

Configuration Options

Basic Configuration

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
});

Protocol & Ports

  • OTLP/HTTP (Default):

    • Port: 4318
    • URL: http://localhost:4318/v1/traces
    • Default exporter. Good for most cases.
    • Configuration:
      TraceModule.forRoot({
        // ...
        collectorUrl: 'http://localhost:4318/v1/traces',
        traceExporter: 'otlp', // or 'otlp-http' (both are equivalent)
      });
      
  • OTLP/gRPC:

    • Port: 4317
    • URL: http://localhost:4317
    • Better performance. Requires HTTP/2 support.
    • Configuration:
      TraceModule.forRoot({
        // ...
        collectorUrl: 'http://localhost:4317',
        traceExporter: 'otlp-grpc',
      });
      
  • Zipkin:

    • Used automatically for local or development environments (unless overridden)
    • Can be explicitly enabled with traceExporter: 'zipkin' or useLocalExporter: true
    • Configuration:
      TraceModule.forRoot({
        // ...
        collectorUrl: 'http://localhost:9411/api/v2/spans',
        traceExporter: 'zipkin',
      });
      

Disable Auto-Instrumentations

If you want to use only manual tracing (decorators) without auto-instrumentations:

TraceModule.forRoot({
  // ... other config ...
  enableAutoInstrumentations: false, // Disable all auto-instrumentations
});

Advanced Configuration

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],
});

Auto Instrumentations Behavior

If autoInstrumentations is not provided:

  • OpenTelemetry defaults will be used (all standard instrumentations enabled via getNodeAutoInstrumentations())

If autoInstrumentations is provided:

  • Your configuration uses InstrumentationConfigMap type from @opentelemetry/auto-instrumentations-node
  • Configuration is passed directly to getNodeAutoInstrumentations() without any merging
  • You must provide complete configuration for each instrumentation you want to customize
  • Must use full package names like '@opentelemetry/instrumentation-http' (shorthand keys are not supported)
  • Only instrumentations included in OpenTelemetry's InstrumentationMap are supported
  • Supported instrumentations include: @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 more

Example:

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,
    },
  },
});

Additional Instrumentations

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',
    }),
  ],
});

Usage

Decorators

TraceClass - Automatically trace all methods

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
  }
}

TraceMethod - Trace specific methods

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
  }
}

TraceEventLog - Trace event handlers

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
  }
}

Manual Tracing

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

Attributes are key-value metadata attached to spans. They help you:

  • Add business context (userId, orderId, status)
  • Search/filter traces in your tracing backend
  • Debug and correlate application behavior

Static Attributes

@TraceMethod('UserService.createUser', { layer: 'service', action: 'createUser' })
async createUser(userId: string) {
  // Every span will have layer and action attributes
}

Dynamic Attributes

@TraceMethod('OrderService.payOrder', args => ({
  orderId: args[0],
  paymentType: args[1]?.type,
}))
payOrder(orderId: string, payment: PaymentInfo) {
  // Attributes are set based on actual arguments
}

Async Attributes

@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
}

Parent-Child (Default)

Used for synchronous operations within the same request lifecycle:

  • HTTP requests
  • Internal events
  • Synchronous operations

Used for asynchronous operations where parent span may have already ended:

  • Queue jobs
  • Kafka consumers
  • Scheduled tasks
  • Messaging systems
@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
  }
}

Important: Initialization Order

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 {}

Method 2: Manual Initialization

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
});

Method 3: Bootstrap in Separate File (For Services)

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 trace bootstrap file must be imported before any other imports that might use instrumented libraries (like Redis, HTTP clients, etc.) to ensure proper instrumentation.
  • This method is useful when you want to centralize trace configuration across multiple services.

Graceful Shutdown

The TraceModule automatically handles graceful shutdown of the OpenTelemetry SDK when your NestJS application shuts down. This ensures that:

  • All pending spans are exported before the application terminates
  • The OpenTelemetry SDK is properly cleaned up
  • No data loss occurs during application shutdown

Automatic Shutdown

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:

  • The application receives a termination signal (SIGTERM, SIGINT)
  • app.close() is called
  • The NestJS application lifecycle reaches the shutdown phase

This works automatically for:

  • Method 1: TraceModule.forRoot() - Automatic shutdown ✓
  • Method 2: initializeTrace() + TraceModule in app - Automatic shutdown ✓
  • Method 3: Bootstrap file + 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!

Manual Shutdown (Only When TraceModule is NOT in 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);

Shutdown Behavior

During shutdown:

  • The SDK stops accepting new spans
  • All pending spans in the batch processor are flushed and exported
  • Exporters are given time to send remaining data (respects timeout configuration)
  • The SDK is fully cleaned up

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).

API Reference

TraceModule

  • TraceModule.forRoot(options: TraceModuleOptions): Configure the trace module synchronously
  • TraceModule.forRootAsync(options): Configure the trace module asynchronously with ConfigService

Decorators

  • @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)

Utilities

  • startTraceSpan<T, R>(spanName, args, handler, options?): Manually start a trace span
  • setErrorAttributes(span, err): Set error attributes on a span
  • hasPropagationHeaders(obj): Check if object has trace propagation headers
  • findHeadersFromArgs(args): Find headers from function arguments
  • createLinkFromHeaders(headers, linkAttributes?): Create span link from headers

TraceService

Static Methods:

  • TraceService.getTracer(): Get the OpenTelemetry tracer
  • TraceService.getActiveSpan(): Get the currently active span

Instance Methods:

  • traceService.injectToPropagation(payload, span?): Inject trace context into payload (requires DI)

License

MIT

Keywords

nestjs

FAQs

Package last updated on 24 Nov 2025

Did you know?

Socket

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.

Install

Related posts