Background
The Middleware APM provides automatic instrumentation for various popular frameworks and libraries, such as Express, MongoDB, Kafka, and many others. This feature enables developers to monitor and trace application performance with minimal configuration. Middleware’s ability to auto-instrument these libraries simplifies the process of tracking application performance and helps identify bottlenecks and latency within supported frameworks.
However, auto-instrumentation comes with certain limitations. For example, when using ExpressJS, span details are often restricted to the HTTP request/response cycle. This can confuse new users, as they may expect to see spans of all the functions and operations within their application. The missing work done by other portions of the application is due to custom logic that falls outside of the framework, which the auto-instrumentation does not capture by default.
The Solution
Thankfully, Middleware APM offers a solution through its NodeJS tracer, which allows you to create custom spans.
To use the Middleware APM NodeJS tracer for custom spans, follow these steps:
-
Begin by importing the package into your module:
import * as tracker from "@middleware.io/node-apm";
-
Next, create a tracer instance:
const tracer = tracker.getTracer("<tracer_name>");
It’s a good practice to name the tracer after the module you’re working in. For example, if you’re working with a route located in ./routes/cars.ts
, you can name the tracer “cars”. This way, it’s easier to identify where the spans are being generated.
- Finally, create your custom span with
tracer.startActiveSpan()
. For example:
function getUserPrefrences(email: string) {
return tracer.startActiveSpan(`getUserPrefrences`, (span: Span) => {
const userPreferences = {
"[email protected]": {
favoriteColor: "Blue",
preferredCarMake: "Tesla",
preferredCarType: "SUV",
},
"[email protected]": {
favoriteColor: "Red",
preferredCarMake: "BMW",
preferredCarType: "Sedan",
},
"[email protected]": {
favoriteColor: "Green",
preferredCarMake: "Toyota",
preferredCarType: "Hatchback",
},
};
const result = userPreferences[email as keyof typeof userPreferences];
// Optional: Add an attribute to the span, this can be used to filter spans
// and add important metadata about an operation
span.setAttribute("user.email", email);
// NOTE: Always be sure to end the span
span.end();
return result;
});
}
Conclusion
And that’s it! Now, whenever getUserPrefrences
is called in a route, the span will automatically be associated with the parent HTTP span. Use custom spans wherever you feel that auto-instrumentation is not verbose enough and add attributes to create more detailed filters.