Nanobots Protecting Microbots

    TypeScript icon, indicating that this package has built-in type declarations

    3.0.4 • Public • Published

    Nest Logo

    NestJS OpenTelemetry (OTEL)

    Build Status NPM


    OpenTelemetry module for Nest.


    For questions and support please use the official Discord channel.


    Setting up observability metrics with nestjs requires multiple libraries and patterns. OpenTelemetry has support for multiple exporters and types of metrics such as Prometheus Metrics.


    Please read this comprehensive whitepaper if that's your first time working with metrics, tracing, and logs.



    $ npm i nestjs-otel @opentelemetry/sdk-node --save


    Some peers dependencies are required when some configurations are enabled.

    1. Create tracing file (tracing.ts):
    import {
    } from '@opentelemetry/core';
    import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
    import { JaegerExporter } from '@opentelemetry/exporter-jaeger';
    import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
    import { JaegerPropagator } from '@opentelemetry/propagator-jaeger';
    import { B3InjectEncoding, B3Propagator } from '@opentelemetry/propagator-b3';
    import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
    import { NodeSDK } from '@opentelemetry/sdk-node';
    import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
    import * as process from 'process';
    const otelSDK = new NodeSDK({
      metricExporter: new PrometheusExporter({
        port: 8081,
      metricInterval: 1000,
      spanProcessor: new BatchSpanProcessor(new JaegerExporter()),
      contextManager: new AsyncLocalStorageContextManager(),
      textMapPropagator: new CompositePropagator({
        propagators: [
          new JaegerPropagator(),
          new W3CTraceContextPropagator(),
          new W3CBaggagePropagator(),
          new B3Propagator(),
          new B3Propagator({
            injectEncoding: B3InjectEncoding.MULTI_HEADER,
      instrumentations: [getNodeAutoInstrumentations()],
    export default otelSDK;
    // You can also use the shutdown method to gracefully shut down the SDK before process shutdown
    // or on some operating system signal.
    process.on('SIGTERM', () => {
          () => console.log('SDK shut down successfully'),
          err => console.log('Error shutting down SDK', err)
        .finally(() => process.exit(0));
    1. Import the metric file and start otel node SDK:
    import otelSDK from './tracing';
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { Logger } from 'nestjs-pino';
    async function bootstrap() {
      // Start SDK before nestjs factory create
      await otelSDK.start();
      const app = await NestFactory.create(AppModule);
      await app.listen(3000);
    1. Configure nest-otel:
    const OpenTelemetryModuleConfig = OpenTelemetryModule.forRoot({
      metrics: {
        hostMetrics: true, // Includes Host Metrics
        defaultMetrics: true, // Includes Default Metrics
        apiMetrics: {
          enable: true, // Includes api metrics
          timeBuckets: [], // You can change the default time buckets
          defaultAttributes: {
            // You can set default labels for api metrics
            custom: 'label',
          ignoreRoutes: ['/favicon.ico'], // You can ignore specific routes (See for options)
          ignoreUndefinedRoutes: false, //Records metrics for all URLs, even undefined ones
      imports: [OpenTelemetryModuleConfig],
    export class AppModule {}

    Span Decorator

    If you need, you can define a custom Tracing Span for a method. It works async or sync. Span takes its name from the parameter; but by default, it is the same as the method's name

    import { Span } from 'nestjs-otel';
    async getBooks() {
        return [`Harry Potter and the Philosopher's Stone`];

    Tracing Service

    In case you need to access native span methods for special logics in the method block:

    import { TraceService } from 'nestjs-otel';
    export class BookService {
      constructor(private readonly traceService: TraceService) {}
      async getBooks() {
        const currentSpan = this.traceService.getSpan(); // --> retrives current span, comes from http or @Span
        await this.doSomething();
        currentSpan.addEvent('event 1');
        currentSpan.end(); // current span end
        const span = this.traceService.startSpan('sub_span'); // start new span
        span.setAttributes({ userId: 1 });
        await this.doSomethingElse();
        span.end(); // new span ends
        return [`Harry Potter and the Philosopher's Stone`];

    Metric Service

    OpenTelemetry Metrics allow a user to collect data and export it to metrics backend like Prometheus.

    import { MetricService } from 'nestjs-otel';
    import { Counter } from '@opentelemetry/api-metrics';
    export class BookService {
      private customMetricCounter: Counter;
      constructor(private readonly metricService: MetricService) {
        this.customMetricCounter = this.metricService.getCounter('custom_counter', {
          description: 'Description for counter',
      async getBooks() {
        return [`Harry Potter and the Philosopher's Stone`];

    Metric Decorators

    Metric Class Instances

    If you want to count how many instance of a specific class has been created:

    @OtelInstanceCounter() // It will generate a counter called: app_MyClass_instances_total.
    export class MyClass {}

    Metric Class Method

    If you want to increment a counter on each call of a specific method:

    export class MyService {
      doSomething() {}
    export class AppController {
      @OtelMethodCounter() // It will generate `app_AppController_doSomething_calls_total` counter.
      doSomething() {
        // do your stuff

    Metric Param Decorator

    You have the following decorators:

    • @OtelCounter()
    • @OtelUpDownCounter()
    • @OtelHistogram()
    • @OtelObservableGauge()
    • @OtelObservableCounter()
    • @OtelObservableUpDownCounter()

    Example of usage:

    import { OtelCounter } from 'nestjs-otel';
    import { Counter } from '@opentelemetry/api-metrics';
    export class AppController {
        @OtelCounter('app_counter_1_inc', { description: 'counter 1 description' }) counter1: Counter
      ) {

    API Metrics with Middleware

    Impl Metric Description Labels Metric Type
    http_request_total Total number of HTTP requests. method, path Counter
    http_response_total Total number of HTTP responses. method, status, path Counter
    http_response_success_total Total number of all successful responses. - Counter
    http_response_error_total Total number of all response errors. - Counter
    http_request_duration_seconds HTTP latency value recorder in seconds. method, status, path Histogram
    http_client_error_total Total number of client error requests. - Counter
    http_server_error_total Total number of server error requests. - Counter
    http_server_aborts_total Total number of data transfers aborted. - Counter
    http_request_size_bytes Current total of incoming bytes. - Histogram
    http_response_size_bytes Current total of outgoing bytes. - Histogram

    Prometheus Metrics

    When metricExporter is defined in otel SDK with a PrometheusExporterit will start a new process on port 8081 (default port) and metrics will be available at http://localhost:8081/metrics.

    Using with a logger

    Pino with instrumentation

    This approach uses otel instrumentation to automatically inject spanId and traceId.

    import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
    const otelSDK = new NodeSDK({
      instrumentations: [new PinoInstrumentation()],

    Pino with custom formatter

    This approach uses the global trace context for injecting SpanId and traceId as a property of your structured log.

    import Pino, { Logger } from 'pino';
    import { LoggerOptions } from 'pino';
    import { trace, context } from '@opentelemetry/api';
    export const loggerOptions: LoggerOptions = {
      formatters: {
        log(object) {
          const span = trace.getSpan(;
          if (!span) return { ...object };
          const { spanId, traceId } = trace.getSpan(;
          return { ...object, spanId, traceId };
    export const logger: Logger = Pino(loggerOptions);


    A full working example can be found in /examples/nestjs-prom-grafana-tempo. This includes a nestjs application fully integrated with prometheus, grafana, loki and tempo.

    A dashboard example is also available:

    Grafana Dashboard

    Logs are automatically associated with tracing (Loki + Tempo):


    Stargazers over time

    Stargazers over time


    npm i nestjs-otel

    DownloadsWeekly Downloads






    Unpacked Size

    4.44 MB

    Total Files


    Last publish


    • pragmaticivan