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.


Installation
pnpm add @eleven-labs/nest-profiler-rabbitmq@alphaThere is no stable release yet — install every
@eleven-labs/nest-profiler*package with the@alphadist-tag (@latestresolves 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:
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 whenPROFILER_ENABLEDis on. Wire the coreProfilerModuleonce at the root — the recommended setup bundles the root-level profiler modules into a singleProfilingModulebehind aConditionalModulegate (see Enabling and disabling the profiler and the example app). A top-levelenabledoption 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):
| Field | Description |
|---|---|
exchange | Exchange the message was published to |
routingKey | Routing key the message was published with |
handler | Class.method of the @RabbitSubscribe handler |
redelivered | true when the broker redelivered the message |
consumerTag | AMQP consumer tag |
deliveryTag | AMQP delivery tag |
messageId | messageId AMQP property, when set |
appId | appId 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.