Skip to content

middleware-labs/nestjs-apm

Repository files navigation

Middleware NestJS APM

Introduction

@middleware.io/nestjs-apm is the official Middleware APM client for NestJS applications that automatically instruments your application with OpenTelemetry, sending runtime metrics, traces/spans, and console logs to Middleware.io.

Installation

npm install @middleware.io/nestjs-apm

Usage

Import the MiddlewareApmModule in your app.module.ts:

import { MiddlewareApmModule } from "@middleware.io/nestjs-apm";

@Module({
  imports: [
    MiddlewareApmModule.forRoot({
      projectName: "Your application name",
      serviceName: "Your service name",
    }),
    // ... other modules
  ],
})
export class AppModule {}

Features

  • Automatic instrumentation of NestJS controllers and services
  • Console log capture (info, warn, error)
  • Distributed tracing
  • Performance metrics
  • Error tracking
  • Custom span attributes
  • Advanced instrumentation decorators
  • OpenTelemetry log integration
  • Exception tracking with OTEL events

Basic Usage

Ignoring Routes

You can use the @IgnoreApmTrace() decorator to exclude specific routes from OpenTelemetry tracing and prevent them from being exported:

import { IgnoreApmTrace } from "@middleware.io/nestjs-apm";

@Controller("users")
export class UsersController {
  // This endpoint will not create or export any OpenTelemetry traces
  @IgnoreApmTrace()
  @Get("health")
  healthCheck() {
    return "OK";
  }

  // This endpoint will still be traced normally
  @Get(":id")
  getUser(@Param("id") id: string) {
    return { id, name: "John Doe" };
  }
}

The @IgnoreApmTrace() decorator can be applied to individual methods or entire controllers:

// Ignore tracing for the entire controller
@IgnoreApmTrace()
@Controller("internal")
export class InternalController {
  @Get("status")
  getStatus() {
    return "Internal status";
  }

  @Get("metrics")
  getMetrics() {
    return "Internal metrics";
  }
}

Alternative: Manual Route Registration

You can also manually register routes to be ignored using the registerIgnoredRoutes function:

import { registerIgnoredRoutes } from "@middleware.io/nestjs-apm";

// In your application initialization
registerIgnoredRoutes([
  '/health',
  '/metrics', 
  '/status',
  '/users/:id/health', // Routes with parameters
  '/internal/*'        // Wildcard patterns
]);

This approach is useful when you want to:

  • Configure ignored routes in one central location
  • Ignore routes that don't use decorators
  • Set up ignored routes during application bootstrap

⚠️ Performance Recommendation

For production applications, we recommend using registerIgnoredRoutes() instead of @IgnoreApmTrace() for better performance.

Why registerIgnoredRoutes() is more efficient:

  • Prevents span creation entirely at the HTTP instrumentation level
  • Lower CPU overhead - no reflection or span manipulation needed
  • Better for high-traffic routes like health checks and metrics endpoints
  • Earlier filtering - operates before NestJS request processing

When to use @IgnoreApmTrace():

  • For fine-grained, method-level control
  • When ignored routes are not high-traffic
  • For development or low-traffic scenarios
// ✅ Recommended for production (better performance)
registerIgnoredRoutes(['/health', '/metrics', '/status']);

// ⚠️ Use sparingly for high-traffic routes
@IgnoreApmTrace()
@Get('health')
healthCheck() { ... }

Advanced Instrumentation

Custom Attributes

Add custom attributes to spans:

import { WithAttributes } from "@middleware.io/nestjs-apm";

@Controller("orders")
export class OrdersController {
  @WithAttributes({ "business.type": "order", "business.tier": "premium" })
  @Post()
  createOrder() {
    // Your code here
  }
}

Custom Spans

Create custom spans with specific names and attributes:

import { CreateSpan } from "@middleware.io/nestjs-apm";

@Injectable()
export class UserService {
  @CreateSpan("user.registration", { "user.type": "new" })
  async registerUser(userData: any) {
    // Your code here
  }
}

Parameter Recording

Automatically record method parameters as span attributes:

import { RecordParams } from "@middleware.io/nestjs-apm";

@Controller("users")
export class UsersController {
  @RecordParams(["userId", "action"])
  @Post(":userId/action")
  performAction(userId: string, action: string) {
    // Parameters will be recorded as span attributes
  }
}

Logging Integration

The module automatically records all NestJS logger output to OpenTelemetry. Just use the standard NestJS logger:

import { Logger, Injectable } from "@nestjs/common";

@Injectable()
export class UserService {
  private readonly logger = new Logger(UserService.name);

  async createUser(userData: any) {
    try {
      this.logger.log("Creating new user", { userId: userData.id });
      // ... user creation logic
    } catch (error) {
      this.logger.error("Failed to create user", error);
      throw error;
    }
  }
}

You can combine multiple decorators for comprehensive instrumentation:

@Controller("payments")
export class PaymentsController {
  @CreateSpan("payment.process")
  @WithAttributes({ "payment.type": "credit-card" })
  @RecordParams(["amount", "currency"])
  async processPayment(amount: number, currency: string) {
    // Your code here
  }
}

Configuration

The MiddlewareApmModule accepts various configuration options to customize the APM behavior:

@Module({
  imports: [
    MiddlewareApmModule.forRoot({
      projectName: "Your application name",
      serviceName: "Your service name",
      
      // Optional configuration options
      enableFsInstrumentation: false,  // Enable filesystem instrumentation (disabled by default for performance)
      consoleLog: false,               // Capture console.log outputs
      consoleError: true,              // Capture console.error outputs
      enableSelfInstrumentation: false, // Enable self-instrumentation
      consoleExporter: false,          // Export to console instead of OTLP
      disabledInstrumentations: "",    // Comma-separated list of instrumentations to disable
      customResourceAttributes: {},    // Custom resource attributes
      // ... other options
    }),
  ],
})
export class AppModule {}

Environment Variables

You can also configure the module using environment variables:

Environment Variable Config Option Description Default
MW_FS_INSTRUMENTATION enableFsInstrumentation Enable filesystem instrumentation false
MW_SELF_INSTRUMENTATION enableSelfInstrumentation Enable self-instrumentation false
MW_CONSOLE_EXPORTER consoleExporter Export to console instead of OTLP false
MW_APM_TRACES_ENABLED pauseTraces Enable/disable trace collection true
MW_APM_METRICS_ENABLED pauseMetrics Enable/disable metrics collection true
MW_API_KEY accessToken Middleware API key -
MW_SERVICE_NAME serviceName Service name -
MW_PROJECT_NAME projectName Project name -
MW_TARGET target OTLP endpoint URL http://localhost:9319

Filesystem Instrumentation

⚠️ Performance Warning: Filesystem instrumentation is disabled by default as it can have a severe impact on application performance, especially in I/O-intensive applications.

To enable filesystem instrumentation:

Via configuration object:

MiddlewareApmModule.forRoot({
  // ... other config
  enableFsInstrumentation: true
})

Via environment variable:

export MW_FS_INSTRUMENTATION=true

Only enable this if you specifically need to trace filesystem operations and are aware of the potential performance implications.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •