-
Notifications
You must be signed in to change notification settings - Fork 0
Support background transactions and non-http contexts #2
Description
Overview
When using an asynchronous handler in my app, a new transaction is not registered.
This transaction should be visible in New Relic as a "background" transaction, like a cron or other non-http transaction would.
In my case, I'm using the @golevelup/nestjs-rabbitmq library to register a subscriber on a RabbitMQ topic.
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common';
import type { ConsumeMessage } from 'amqplib';
@Injectable()
export class SubscriberService {
@RabbitSubscribe({
exchange: 'test',
routingKey: 'test.topic',
queue: 'test-queue',
})
async subscribeHandler(msg: unknown, amqpMsg: ConsumeMessage) {
console.log(`Received message: ${JSON.stringify(msg)}`);
}
}
This library is using amqp-connection-manager behind the scenes to connect a RabbitMQ instance and establishes a listener. The listener waits until a message arrives on the queue and then executes. When I use this library, I can see my web traffic registered, but the asynchronous service is not instrumented.
Suggested Fix
My current solution (before finding this library) is
//newrelic.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
const newrelic = require('newrelic');
@Injectable()
export class NewrelicInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const contextType = context.getType();
/* start a new relic background transaction for non-http requests such as async listeners */
if (contextType !== 'http') {
return newrelic.startBackgroundTransaction(context.getHandler().name, function () {
const transaction = newrelic.getTransaction();
return next.handle().pipe(
tap(() => {
return transaction.end();
}),
);
});
}
return newrelic.startWebTransaction(context.getHandler().name, function () {
const transaction = newrelic.getTransaction();
return next.handle().pipe(
tap(() => {
return transaction.end();
}),
);
});
}
}
// main.ts
import { NewrelicInterceptor } from './newrelic.interceptor';
app.useGlobalInterceptors(new NewrelicInterceptor());
When a message is published to the configured topic, the handler executes. The execution is shown in New Relic as a "non-web" transaction.
PS. Awesome work on this library! Thank you!