npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@showroomprive/observability

v1.1.1

Published

A library to use Observability in NestJS.

Readme

@showroomprive/observability

A library to configure observability and health checks for a NestJS application. It provides tools for:

  • Auto-instrumentation in Node.js applications.
  • Manual instrumentation for logs, traces, and metrics.
  • Health check endpoints to monitor application health.
  • Trace context propagation for distributed tracing across services and message queues.

The library is built using OpenTelemetry and introduces three health probe endpoints:

  • /health/live: Liveness probe
  • /health/ready: Readiness probe
  • /health/startup: Startup probe

Note: These endpoints are not exposed in Swagger.

Table of Contents

Requirements

To use this library, the following requirements need to be met:

| Library Name | Version | Description | |--------------------|---------|-----------------------------------------------------------------------------| | @nestjs/common | ^11.0.11 | Common utilities used by NestJS applications. | | @nestjs/config | ^4.0.1 | A module for managing application configuration in NestJS. | | @nestjs/core | ^11.0.11 | The core package of the NestJS framework. | | rxjs | ^7.8.2 | A library for reactive programming using Observables, to make it easier to compose asynchronous or callback-based code. | | @nestjs/terminus | ^11.0.0 | A module for adding health checks and readiness/liveness probes to NestJS applications. |

An OpenTelemetry collector is needed to view the traces, logs, metrics if we don't use the console exporter.

For example, the Aspire dashboard can be used.

To launch it in a container, we can use the following docker command :

docker run --rm -it -p 18888:18888 -p 4317:18889 -d --name aspire-dashboard mcr.microsoft.com/dotnet/aspire-dashboard:9.0

And use the link inside the logs of the container to access the dashboard.

Dependencies

| Dependency | Version | |-----------------------------------------|----------| | @nestjs/axios | ^4.0.0 | | @nestjs/common | ^11.0.11 | | @nestjs/core | ^11.0.11 | | @nestjs/swagger | ^11.0.6 | | @nestjs/terminus | ^11.0.0 | | @opentelemetry/api-logs | ^0.57.2 | | @opentelemetry/auto-instrumentations-node | ^0.56.1 | | @opentelemetry/exporter-logs-otlp-grpc | ^0.57.2 | | @opentelemetry/exporter-metrics-otlp-grpc | ^0.57.2 | | @opentelemetry/exporter-trace-otlp-grpc | ^0.57.2 | | @opentelemetry/instrumentation-express | ^0.47.1 | | @opentelemetry/instrumentation-http | ^0.57.2 | | @opentelemetry/instrumentation-nestjs-core | ^0.44.1 | | @opentelemetry/resources | ^1.30.1 | | @opentelemetry/sdk-logs | ^0.57.2 | | @opentelemetry/sdk-metrics | ^1.30.1 | | @opentelemetry/sdk-node | ^0.57.2 | | @opentelemetry/sdk-trace-base | ^1.30.1 | | @opentelemetry/semantic-conventions | ^1.30.0 | | @opentelemetry/exporter-logs-otlp-proto | ^0.57.2 | | @opentelemetry/exporter-metrics-otlp-proto | ^0.57.2 | | @opentelemetry/exporter-trace-otlp-proto | ^0.57.2 | | axios | ^1.8.1 | | dotenv | ^16.4.7 | | rxjs | ^7.8.2 | | koa | ^2.16.0 |

Dev Dependencies

| Dependency | Version | |-----------------------------|----------| | @types/node | ^22.13.9 | | typescript | ^5.8.2 | | @types/koa | ^2.15.0 |

Peer Dependencies

| Dependency | Version | |-----------------------------|----------| | @nestjs/common | ^11.0.11 | | @nestjs/core | ^11.0.11 | | rxjs | ^7.8.2 | | @nestjs/terminus | ^11.0.0 |

Installation

To install the package, run:

npm install @showroomprive/observability

Configuration

To use the library, several environment variables need to be provided in a .env file at the root:

| Variable Name | Description | Mandatory | Default Value | |---------------------------------|--------------------------------------------------|-----------|---------------| | OBSERVABILITY_COLLECTOR_URL | The URL of the observability collector. | Yes | - | | OBSERVABILITY_TRACES_EXPORT_CONSOLE | Enable console export for traces, if true disable other exporters. | No | false | | OBSERVABILITY_METRICS_EXPORT_CONSOLE | Enable console export for metrics, if true disable other exporters. | No | false | | OBSERVABILITY_LOGGING_EXPORT_CONSOLE | Enable console export for logging, if true disable other exporters. | No | false | | OBSERVABILITY_SIMPLE_LOGGING_CONSOLE | Enable console log for the message (no exporter used, works like consol.log). | No | false | | OBSERVABILITY_SERVICE_NAME | The name of the service that is display in the observability tool. | Yes | - | | OBSERVABILITY_INDEX_SUFFIX | A filter criterious to group data between several applications | No | Value of OBSERVABILITY_SERVICE_NAME variable | | ENVIRONMENT | The current environment. | Yes | - | | ENABLE_DIAGNOSTICS | Enable diagnostics. | No | false | | GRPC | Enable gRPC, if false the http/protobuf procol will be use with the collector URL | No | true | | OBSERVABILITY_HEADERS | Headers for observability calls. | No | - | | ENABLE_DEBUG_LOGGING | Enable sending debug logging to exporter | No | false |

There is a example for an aspire dashbord run on the local machine (http://localhost:4317):

OBSERVABILITY_COLLECTOR_URL=http://localhost:4317
OBSERVABILITY_SERVICE_NAME=observability.sample.nestjs
ENVIRONMENT=local

A change need to be done on the main.ts file to configure the application to use observability.

Before creating the modules, we need to initialize the observability, by adding the following line:

initializeObservability();

Inside the file :

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { initializeObservability } from '@showroomprive/observability/dist/tracing';

async function bootstrap() {
  initializeObservability();
  
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

This ensure the sdk is well configured before the bootstrap of the application.

For the health checks we can create custom HealthIndicators, the library allows adding custom HealthIndicators on the 2 probes : startup and ready.

We need to create a class that is inherited by HealthIndicatorBase and this classe should be injectable.

There is a sample class:

import { Injectable } from '@nestjs/common';
import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus';
import { HealthIndicatorBase } from '@showroomprive/observability/dist/health/health-indicator-base';

@Injectable()
export class TestHealthIndicator extends HealthIndicatorBase {
  public async isHealthy(key: string): Promise<HealthIndicatorResult> {
    const isHealthy = true; // Add custom logic here
    const result = this.getStatus(key, isHealthy);
    if (isHealthy) {
      return result;
    }
    throw new HealthCheckError('Health check failed', result.error);
  }
}

The isHealthy value in this example is true but you can replace it with a function that do the check you wantthe logic you want to test.

You can create several classes like this.

After creating these classes we need to register them, and this should be done in app.module.ts :

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HealthModule } from '@showroomprive/observability';
import { TestHealthIndicator1 } from './healthIndicators/test1-health-indicator';
import { TestHealthIndicator2 } from './healthIndicators/test2-health-indicator';

@Module({
  imports: [
    HealthModule.register({
      ready: [
        { name: "test1", indicator: Test1HealthIndicator},
      ],
      startup: [
        { name: "test2", indicator: Test2HealthIndicator},
      ],
    })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

In this sample we can register the ready health indicators and the startup ones, we need to give HealthModule an object that contains 2 properties: ready and startup, each of them are arrays and the name property is the name that will be display when we visit the routes: /health/ready, /health/startup for the indicator indicator, the indicator property should reference the class that inherits HealthIndicatorBase.

Note: We log the error occured in the probes using the configuration described previously.

Usage

The probes will be reachable on :

  • /health/live
  • /health/ready
  • /health/startup

To create Logs, Traces, Metrics, we can use the Logger, Tracer, Meter availlable by the library.

To create a log :


import { Logger } from '@showroomprive/observability/dist/tracing';

Logger.info('Hello world', { hello: 'world' });

The second parameter is the custom columns we want to add to our log.

To create a metric :


import { Meter } from '@showroomprive/observability/dist/tracing';

Meter().createCounter('customCounter', { description: 'A custom counter' }).add(1);

To create a trace:

import { Tracer } from '@showroomprive/observability/dist/tracing';
Tracer().startActiveSpan('customTrace_number1', { attributes: { hello: "OK"}}, (span) => {
      Logger.info('Log from Custom trace 1');
      span.end();      
    });

There is an example of a controller that have logs, trace, metrics that we saw:

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
import { Logger, Meter, Tracer } from '@showroomprive/observability/dist/tracing';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get("Hello")
  getHello(): string {
    Meter().createCounter('customCounter', { description: 'A custom counter' }).add(1);
    
    Tracer().startActiveSpan('customTrace_number1', { attributes: { hello: "OK"}}, (span) => {
      Logger.info('Log from Custom trace 1');
      span.end();      
    });
    Logger.info('Hello world', { hello: 'world' });

    return Tracer().startActiveSpan('customTrace_number2', { attributes: { action: "service call"}}, (span) => {
      Logger.info('Log from Custom trace 2');
      span.end();
      return result;
    });
  }
}

Auto-Instrumentation

There are some auto instrumentation that is enabled by default :

  • Node.js
  • Express
  • NestJS

We plan to add more in the future.

Trace Context Propagation

The library supports trace context propagation for distributed tracing across services and message queues. This ensures that traces are correctly linked across different parts of your system, providing a complete view of the request flow.

sendMessageWithTrace

Sends a message with trace context information embedded in the message properties:

import { ServiceBusClient } from '@azure/service-bus';
import { sendMessageWithTrace, Logger } from '@showroomprive/observability/dist/tracing';

// In your controller/service:
const sbClient = new ServiceBusClient(connectionString);
const sender = sbClient.createSender(queueName);

try {
    // Send a message with trace context
    await sendMessageWithTrace(
        async (msg) => await sender.sendMessages(msg),
        { name: 'payload-example' }
    );
    
    Logger.info('Message sent with trace');
} finally {
    await sbClient.close();
}

executeWithTraceContext

Execute code within a trace context extracted from message properties:

import { executeWithTraceContext, Logger, Tracer } from '@showroomprive/observability/dist/tracing';
import { InvocationContext } from "@azure/functions";

export async function azureFunctionHandler(queueItem: unknown, invocationContext: InvocationContext): Promise<void> {
    // Execute code while preserving the trace context from the original message
    await executeWithTraceContext(invocationContext.triggerMetadata, async () => {
        Tracer().startActiveSpan('processing-message', span => {
            Logger.info('Processing queue item', { item: JSON.stringify(queueItem) });
            // Message processing logic
            span.end();
        });
    });
}

TraceContext Class

Object-oriented approach to work with trace context:

import { TraceContext, Logger, Meter, Tracer } from '@showroomprive/observability/dist/tracing';
import { InvocationContext } from "@azure/functions";

export async function azureFunctionHandler(queueItem: unknown, invocationContext: InvocationContext): Promise<void> {
    const context = new TraceContext(invocationContext.triggerMetadata);
    
    await context.with(async () => {
        // All code here will inherit the trace context from the original message
        Meter().createCounter("message_processed", { description: "Count of processed messages" }).add(1);
        
        Tracer().startActiveSpan('processing-message', span => {
            Logger.info('Processing message', { messageId: JSON.stringify(queueItem) });
            // Processing logic
            span.end();
        });
    });
}

Automatic Log and Trace Flushing

The library automatically registers handlers to flush all telemetry data before shutdown:

  • On normal process exit
  • On SIGINT (Ctrl+C)
  • On SIGTERM (Docker, Kubernetes termination signals)

This ensures that all telemetry data is properly sent before the application terminates.

Koa.js support

There is a new middleware that is added for koa.js:

import Koa from 'koa';
import { createKoaTelemetryMiddleware } from '@showroomprive/observability/dist/Instrumentations/koa.instrumentation';
import { initializeObservability } from '@showroomprive/observability/dist/tracing';
import dotenv from 'dotenv'

dotenv.config();

initializeObservability();

const app = new Koa();
app.use(createKoaTelemetryMiddleware());

initializeObservability();

app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});