# How the APM agent for Node.js works?

**URL:** <https://discuss.elastic.co/t/how-the-apm-agent-for-node-js-works/272697>\
**Category:** APM\
**Tags:** nodejs, ruby\
**Created:** [May 11, 2021, 12:31pm UTC](https://discuss.elastic.co/t/how-the-apm-agent-for-node-js-works/272697 "2021-05-11T12:31:28Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![m-s-santos](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/m-s-santos/32/88599_2.png) [@m-s-santos](https://discuss.elastic.co/u/m-s-santos)\
**Post date:** [May 11, 2021, 12:31pm UTC](https://discuss.elastic.co/t/how-the-apm-agent-for-node-js-works/272697/1 "2021-05-11T12:31:28Z")

</div>

Hello, I'm trying to add APM to a Ruby application running with Eventmachine. However, since the Ruby agent doesn't support Eventmachine, I'm trying to see what I can do to make it work.

The problem is that since it works based on an event-loop, it tries to create multiple transactions in the same thread, which is not possible for the Ruby implementation at the moment.

However, I think I can get some inspiration on how the APM agent works for Node.js and try to implement something similar for Ruby.

I wondered if there is something anywhere that could help me understand, or if there is someone here that could explain how they overcame this issue in the Node.js agent.

---

<div class="post-metadata">

**Author:** ![alanstorm](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/alanstorm/32/77134_2.png) [@alanstorm](https://discuss.elastic.co/u/alanstorm)\
**Post date:** [May 11, 2021, 4:30pm UTC](https://discuss.elastic.co/t/how-the-apm-agent-for-node-js-works/272697/2 "2021-05-11T16:30:28Z")

</div>

Great question @m-s-santos -- tracking async state is one of the harder problems we have to solve in the Elastic Node.js APM Agent.

Our basic strategy is to keep track of the "current" transaction. That is, the Instrumentation object [has a currentTransaction property](https://github.com/elastic/apm-agent-nodejs/blob/c8d15e3315736b52077ae123c9891d5f3c43eb0b/lib/instrumentation/index.js#L57). Whenever we need to know what transaction to associate some work with, we reference this property.

How the agent manages to keep `currentTransaction` up to date is tricky -- in modern times Node.js provides a module named [async\_hooks](https://nodejs.org/api/async_hooks.html) which allows an end-user-programmer (in this case the agent is the end-user-programmer) to register a listener object that will fire whenever node is about to create a new async context, or is about to enter a new async context, or is finished with a particular async context.. We use this listener object to keep track of which transaction is associated with each async context, and [the `currentTransaction`](https://github.com/elastic/apm-agent-nodejs/blob/master/lib/instrumentation/async-hooks.js#L20) property is replaced with a dynamic getter that will return the correct transaction.

Without async-hooks (older versions of the Node.js agent, or if users disable it due to perf. concerns) it gets even trickier. We have [a module named patch-async](https://github.com/elastic/apm-agent-nodejs/blob/master/lib/instrumentation/patch-async.js) which attempts to wrap every function that _may_ create a new async context. This wrapping includes calling the `Instrumentation.bindFunction` method ([here's an example of this](https://github.com/elastic/apm-agent-nodejs/blob/c8d15e3315736b52077ae123c9891d5f3c43eb0b/lib/instrumentation/patch-async.js#L144)). The `bindFunction` method wraps _the callback_ of a function that schedules async work. When Node calls that callback function, [we swap the transaction](https://github.com/elastic/apm-agent-nodejs/blob/c8d15e3315736b52077ae123c9891d5f3c43eb0b/lib/instrumentation/index.js#L351). There's also some [similar wrapping for native promises](https://github.com/elastic/apm-agent-nodejs/blob/c8d15e3315736b52077ae123c9891d5f3c43eb0b/lib/instrumentation/patch-async.js#L342), and various places in individual module instrumentation where we need to wrap a callback that's not caught by patch-async. This works OK, but will occasionally result in lost or conflated transaction state if we fail to wrap and bind a callback that's performing async work.

So, at a very high level, it sounds like you'll need to figure out if EventMachine exposes an API that lets you keep track of when new contexts are created/entered/destroyed and/or manually patch/wrap the methods EventMachine's classes/objects to do the same. I hope that helps and good luck!

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [June 1, 2021, 12:31pm UTC](https://discuss.elastic.co/t/how-the-apm-agent-for-node-js-works/272697/3 "2021-06-01T12:31:01Z")

</div>

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