@middleware.io/nestjs-apm v1.1.0
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.