Nodejs Agent NestJS and Typeorm


(Juan Colo) #1

Hello everyone,

I'm currently testing the NodeJs APM agent. I did a basic integration of the agent in my NestJS API, but the automatic registration request transactions did not work. Therefore, create a custom interceptor to create transactions in each incoming request:

   intercept(context: ExecutionContext, call$: Observable<any>): Observable<any> {
        const [IncomingMessage, ...res] = context.getArgs();
        let transaction = this.apmService.startTransaction(`${IncomingMessage.method} ${IncomingMessage.url}`, 'Request');

        return call$.pipe(
            tap(() => {
                transaction.end('HTTP 2xx');
            }),
            catchError(error => {
                transaction.end('HTTP 5xx');
                this.apmService.captureError(error);
                throw error;
            }),
        );
    }

With this implementation I see the requests in kibana.

Now I'm trying to use the query logs in MSSQL (azure), I'm using typeorm, and this uses the mssql (https://www.npmjs.com/package/mssql) and this uses Tedious driver to connect.

From what I understood, the agent of Nodejs automatically logs the queries of Tedious , but I can not see the spans in kibana.

I'm starting the APM agent like this:

Import ...
import * as APM from 'elastic-apm-node';

@Injectable()
export class ApmService {
    apm: any;

    constructor(@Inject(APM_OPTIONS) private readonly options: ApmOptions) {
        this.apm = APM.isStarted() ? APM : APM.start({
            serviceName: options.serviceName || '',
            secretToken: options.secureToken || '',
            serverUrl: options.url || '' 
        });
    }
}

What am I doing wrong?
Regards,

NodeJs: 10.15.0
NestJs: 5.5.0
TypeORM: 0.2.9
mssql: 4.3.0
Tedious: 2.7.1


(Thomas Watson) #2

Hi @Juan_Colo

Thanks for trying out the Node.js agent and Elastic APM. You're right that we don't support NestJS directly. But we should automatically pick up the incoming HTTP requests. So without your custom interceptor you should have seen a lot of GET unknown route transactions - did you see that?

If you saw those, it means that we correctly registered the incoming HTTP requests and automatically started and ended transactions for them.

The only thing your custom intercepter then needs to do is give them a more meaningful name than simply GET unknown route. You do that by calling this.apmService.setTransactionName(<name>) (see its documentation here). You shouldn't manually have to start and end a new transaction. In fact I would think this might mess some things up as there already should be an existing transaction.

This is all documented in the "Get started with a custom Node.js stack" part of our documentation which I recommend that you read :slight_smile:

But regardless, when a transaction is active, you're right that we should automatically pick up MSSQL queries made using the tedious module. I see now that there's a slight typo in our Supported Technologies docs, as it says we only support version ^0.0.5 of Tedious, but I can verify that we do support the entire 2.x range as well.

There's a few reasons why we might not pick them up however. To see exactly what's going on I recommend setting the log level to debug and sending an HTTP request to the app that will trigger an MSSQL query and see what the agent logs. See our Troubleshooting docs for details. If you like, I'd be happy to take a look at the resulting logs.

/thomas


(Thomas Watson) #3

Follow up: I missed this when reading your message the first time, but I see that you're using import instead of require. And it seems like you're using TypeScript. When you compile the code to JavaScript, this will most likely mess with order of when start() is called on the agent. And this in turn will explain why the Tedious queries are not picked up. See the "Babel / ES Modules support" section in our docs for details on how to fix that.


(Juan Colo) #4

Hi Thomas, thanks for your answer.

Change the call to the start method as indicated by the documentation for BABEL and set the log Level to debug:


import * as apm from 'elastic-apm-node/start';

"scripts": {
 "start": "ELASTIC_APM_LOG_LEVEL=debug ts-node -r tsconfig-paths/register src/main.ts",
}

Now, when a request is sent to my API, the console displays an error:

intercepted call to generic-pool.PriorityQueue.prototype.enqueue { id: null }
no active transaction found - cannot build new span

Any idea what is happening?

Regards,


(Juan Colo) #5

I uploaded a repository with an example with nestJs that is giving me the same error.
This one does not have Typeorm but it has the same error of the transaction:

no active transaction found - cannot build new span

Thanks


(Thomas Watson) #6

Thanks for sharing the code. It looks like you're not calling start() on the APM agent until after a lot of the imports have been called. That means that the agent will not have a chance to load before the rest of the app have loaded. And this in turn mean that it cannot add its hooks to get notified about database queries etc.

As far as I could see this project is actually based on Express, which means that we should support automatic naming of transactions. The reason why it's not working is probably the same as described above.

If you move the call to import 'elastic-apm-node/start' up to either be the very first thing in src/main.ts - or maybe even simpler start node with -r elastic-apm-node/start instead of importing elastic-apm-node/start in the source code - that should fix the problem.


(Juan Colo) #7

Thomas, that was the problem.
I moved import to the start of the file and everything works fine without interceptor.

Thank you very much for your help.


(system) closed #8

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.