NodeJS Custom Instrumentation
This guide explains how to implement Custom Instrumentation in NodeJS applications using the Motadata Custom Instrumentation package.
Custom instrumentation enables you to inject business-specific attributes into the currently active span using OTel-compatible APIs, without affecting the existing auto-instrumentation flow. The package ensures attribute validation, consistent namespacing, and safe runtime behavior.
Prerequisites
- The application must already be instrumented using Motadata Auto Instrumentation so that span context is available.
- Node.js version ^18.19.0 or >= 20.6.0
- npm version 7 or higher
- Motadata Agent 8.1.2 or higher
- The application must already be generating traces in APM
Configuration Steps
Step 1: Install the NodeJS Custom Instrumentation Package
Using CLI
npm install "git+https://github.com/motadata2025/motadata-apm-custom-instrumentation-nodejs.git"
Using package.json
Add the dependency to your package.json:
{
"dependencies": {
"motadata-apm-custom-instrumentation-nodejs": "github:motadata2025/motadata-apm-custom-instrumentation-nodejs"
}
}
Then install:
npm install
Step 2: Import the Package
const motadata = require('motadata-apm-custom-instrumentation-nodejs');
Step 3: Add Custom Attributes to the Active Span
Use the package APIs to inject custom attributes into the currently active span.
Basic Example
const motadata = require('motadata-apm-custom-instrumentation-nodejs');
motadata.apm.CustomInstrumentation.set('apm.user.id', 12345);
motadata.apm.CustomInstrumentation.set('apm.user.name', 'john.doe');
motadata.apm.CustomInstrumentation.set('apm.request.success', true);
motadata.apm.CustomInstrumentation.setStringList('apm.tags', ['api', 'production', 'critical']);
Keys are automatically prefixed with apm. if the prefix is not provided. However, it is recommended to explicitly use the prefix for consistency across services.
Since the package throws runtime errors for invalid input or missing span context, wrap calls in try/catch in production code.
Recommended Production Pattern
const motadata = require('motadata-apm-custom-instrumentation-nodejs');
try {
motadata.apm.CustomInstrumentation.set('apm.order.id', orderId);
} catch (error) {
logger.warn(`Failed to set apm.order.id: ${error.message}`);
}
Supported Methods
Scalar Attribute Setter
| Method | Parameter |
|---|---|
set(key, value) | string, number, or boolean |
Collection Attribute Setters
| Method | Parameter |
|---|---|
setStringList(key, values) | string[] |
setNumberList(key, values) | number[] |
setBooleanList(key, values) | boolean[] |
Attribute Behavior and Validation Rules
The NodeJS Custom Instrumentation package validates both keys and values before attaching attributes to the span.
Key Handling
- Keys are automatically prefixed with
apm.when absent. - Keys are normalized to lowercase.
- Keys allow only alphanumeric characters and dots.
- Keys must not be null, undefined, empty, or whitespace-only.
Value Handling
- Scalar values must not be null or undefined.
- Empty string values are ignored and are not added to the trace.
- Numbers must be finite.
NaNandInfinityare not allowed. - List methods drop null, undefined, and invalid-type values.
- Number lists drop
NaNandInfinity. - Lists must retain at least one valid value after filtering.
Runtime Behavior
- Throws
Errorfor invalid input. - Throws
Errorwhen no active span is present. - Designed to work alongside existing Motadata auto-instrumentation.
Example Scenarios
Add Business Context to a Request
try {
motadata.apm.CustomInstrumentation.set('apm.customer.id', 1001);
motadata.apm.CustomInstrumentation.set('apm.customer.name', 'john.doe');
motadata.apm.CustomInstrumentation.set('apm.customer.tier', 'gold');
} catch (error) {
logger.warn(`Failed to set customer attributes: ${error.message}`);
}
Add Transaction Details
try {
motadata.apm.CustomInstrumentation.set('apm.order.id', 987654);
motadata.apm.CustomInstrumentation.set('apm.order.amount', 2599.75);
motadata.apm.CustomInstrumentation.set('apm.order.currency', 'USD');
motadata.apm.CustomInstrumentation.set('apm.order.success', true);
} catch (error) {
logger.warn(`Failed to set order attributes: ${error.message}`);
}
Add Collection Attributes
try {
motadata.apm.CustomInstrumentation.setStringList(
'apm.order.items',
['router', 'switch', 'firewall']
);
} catch (error) {
logger.warn(`Failed to set order items: ${error.message}`);
}
Add Failure Context
try {
await paymentService.process(paymentRequest);
} catch (error) {
try {
motadata.apm.CustomInstrumentation.set('apm.payment.status', 'failed');
motadata.apm.CustomInstrumentation.set('apm.payment.error_message', error.message);
} catch (instrumentationError) {
logger.warn(`Failed to set payment error attributes: ${instrumentationError.message}`);
}
throw error;
}
Advanced Example
The following example demonstrates enterprise-style usage where custom attributes are added across the lifecycle of a request.
const motadata = require('motadata-apm-custom-instrumentation-nodejs');
async function processOrder(order) {
try {
motadata.apm.CustomInstrumentation.set('apm.order.id', order.id);
motadata.apm.CustomInstrumentation.set('apm.order.customer_id', order.customerId);
motadata.apm.CustomInstrumentation.set('apm.order.amount', order.amount);
motadata.apm.CustomInstrumentation.set('apm.order.currency', order.currency);
motadata.apm.CustomInstrumentation.setStringList(
'apm.order.tags',
['sales', 'priority', 'online']
);
if (order.amount > 10000) {
motadata.apm.CustomInstrumentation.set('apm.order.high_value', true);
}
motadata.apm.CustomInstrumentation.set('apm.order.stage', 'validation');
await validateOrder(order);
motadata.apm.CustomInstrumentation.set('apm.order.stage', 'inventory_check');
await checkInventory(order);
motadata.apm.CustomInstrumentation.set('apm.order.stage', 'payment_processing');
await processPayment(order);
motadata.apm.CustomInstrumentation.set('apm.order.stage', 'dispatch');
await dispatchOrder(order);
motadata.apm.CustomInstrumentation.set('apm.order.status', 'completed');
} catch (error) {
try {
motadata.apm.CustomInstrumentation.set('apm.order.status', 'failed');
motadata.apm.CustomInstrumentation.set('apm.order.error_message', error.message);
} catch (instrumentationError) {
logger.warn(`Failed to set failure attributes: ${instrumentationError.message}`);
}
throw error;
}
}
async function validateOrder(order) {}
async function checkInventory(order) {}
async function processPayment(order) {}
async function dispatchOrder(order) {}
This approach enables correlation of business metadata such as order ID, customer, stage, and failure reason directly with traces.
Best Practices
- Use descriptive, hierarchical keys such as
apm.order.id. - Prefer explicitly using the
apm.prefix. - Use correct data types (number for IDs and metrics, boolean for flags).
- Use list setters for collections and allow filtering within the package.
- Keep naming consistent across services for easier querying and dashboards.
- Treat instrumentation as non-blocking and always handle errors.
What to Avoid
- Do not inject sensitive data such as passwords, tokens, or secrets.
- Do not use inconsistent key naming across services.
- Do not pass empty or whitespace-only string values.
- Do not use invalid keys with unsupported characters.
- Do not assume attributes will be added when no active span exists.
Verify the Attributes
After implementing custom instrumentation:
- Go to APM Explorer
- Open the required trace
- Select the relevant span
- Confirm that the custom attributes are visible under span attributes
Support
- Email:
engg@motadata.com - Issues: GitHub Issues on the repository
License
Copyright (c) 2026 Motadata. All rights reserved.
Proprietary software; see LICENSE for full terms.
Summary
The Motadata NodeJS Custom Instrumentation package provides a lightweight and enterprise-ready mechanism to inject validated, namespaced, business-specific attributes into the active span. It enhances trace visibility while maintaining compatibility with existing auto-instrumentation.