NestJS Profiler
Packages

nest-profiler-rabbitmq

Profile RabbitMQ messages handled via @golevelup/nestjs-rabbitmq.

@eleven-labs/nest-profiler-rabbitmq

@eleven-labs/nest-profiler-rabbitmq captures RabbitMQ messages consumed via @RabbitSubscribe (@golevelup/nestjs-rabbitmq) and surfaces each one as its own profile — a dedicated RabbitMQ view on the profiler home and a built-in Message detail tab.

RabbitMQ view — consumed messages with delivery, exchange, routing-key and handler filters

Message detail tab — a consumed review.created delivery with exchange, routing key, handler, delivery metadata and JSON payload

Installation

pnpm add @eleven-labs/nest-profiler-rabbitmq@alpha

There is no stable release yet — install every @eleven-labs/nest-profiler* package with the @alpha dist-tag (@latest resolves to nothing).

Peer dependencies: @golevelup/nestjs-rabbitmq and amqplib (the ones you already use to consume messages). They are optional — when no RabbitMQ consumer runs, the module simply never produces a profile.

Setup

Register the module in the application that consumes your messages (the same process that hosts the profiler), alongside your RabbitMQ module:

app.module.ts
import { ConditionalModule } from '@nestjs/config';
import { RabbitMqCollectorModule } from '@eleven-labs/nest-profiler-rabbitmq';

const isProfilerEnabled = (env: NodeJS.ProcessEnv) => env['PROFILER_ENABLED'] === 'true';

@Module({
  imports: [
    ConditionalModule.registerWhen(RabbitMqCollectorModule.forRoot(), isProfilerEnabled),
    // your RabbitMQModule.forRoot(...) with @RabbitSubscribe handlers
  ],
})
export class AppModule {}

Your @RabbitSubscribe handlers need no changes:

@RabbitSubscribe({ exchange: 'articles.events', routingKey: 'published.*', queue: 'tts.narration' })
async createGeneration(message: ArticleEvent, raw: ConsumeMessage): Promise<void> {
  // …
}

Configuration

RabbitMqCollectorModule.forRoot({
  captureHeaders: true, // default — AMQP headers (sensitive ones masked)
  captureBody: true, // default — deserialized payload (can be large)
  maskHeaders: ['x-tenant-secret'], // merged with the built-in mask list
  // What counts as a failed message. A message has no status code, so the default is
  // simply "the handler threw" — narrow it when a handler throws as flow control.
  error: { exceptions: ['TimeoutError'] },
});

error decides what earns the error tag and what the list's Errors filter keeps. See What counts as an error. Use forRootAsync to resolve any of these from ConfigService.

Enabling / disabling — gate the collector with ConditionalModule.registerWhen(..., isProfilerEnabled) as shown, so it loads only when PROFILER_ENABLED is on. Wire the core ProfilerModule once at the root — the recommended setup bundles the root-level profiler modules into a single ProfilingModule behind a ConditionalModule gate (see Enabling and disabling the profiler and the example app). A top-level enabled option is also supported as an alternative.

What it collects

Each consumed message becomes a profile with a rabbitmq entrypoint (entrypoint.type = 'rabbitmq', with this payload on entrypoint.data):

FieldDescription
exchangeExchange the message was published to
routingKeyRouting key the message was published with
handlerClass.method of the @RabbitSubscribe handler
redeliveredtrue when the broker redelivered the message
consumerTagAMQP consumer tag
deliveryTagAMQP delivery tag
messageIdmessageId AMQP property, when set
appIdappId AMQP property, when set

The masked headers and the payload are stored on entrypoint.data.headers / entrypoint.data.payload.

How it works

A consumed message has no HTTP request/response, so the module registers an IContextAdapter for the rmq execution context that creates a fresh profile per message. The core ProfilerInterceptor wraps the handler in a CLS context — so profile-scoped collectors (HTTP client, database, …) keep capturing — then persists the profile. The module registers the rabbitmq entrypoint type, so the profiler renders it in a dedicated RabbitMQ sidebar view and a built-in Message detail tab; the HTTP Request/Response tabs are hidden, exactly like CLI commands.

Powered & maintained by

On this page