OpenTelemetry Node.js Installation
Make sure to install the AppSignal collector before proceeding with these instructions.
Install the OpenTelemetry packages
Use npm to add the OpenTelemetry SDK and the OTLP exporter to your application's dependencies:
npm install @opentelemetry/sdk-node \ @opentelemetry/api \ @opentelemetry/sdk-trace-node \ @opentelemetry/sdk-metrics \ @opentelemetry/exporter-trace-otlp-proto \ @opentelemetry/exporter-metrics-otlp-proto \ @opentelemetry/auto-instrumentations-node \ @opentelemetry/semantic-conventions \ @opentelemetry/resources
The @opentelemetry/auto-instrumentations-node
package will automatically instrument most applications and libraries. Check out the package's README for a list of libraries and frameworks that it instruments. You can find additional instrumentations for libraries and frameworks in the OpenTelemetry Registry.
Configure OpenTelemetry in your application
Add this opentelemetry.cjs
file to your application with the OpenTelemetry exporter and AppSignal configuration.
Make sure to update the values below with your AppSignal application name, environment and push API key, and to replace the exporter endpoint with the address of your AppSignal collector if needed.
const os = require("os"); const { NodeSDK } = require("@opentelemetry/sdk-node"); const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node"); const { PeriodicExportingMetricReader, AggregationTemporality, } = require("@opentelemetry/sdk-metrics"); const { Resource } = require("@opentelemetry/resources"); const { SemanticResourceAttributes, } = require("@opentelemetry/semantic-conventions"); const { OTLPTraceExporter, } = require("@opentelemetry/exporter-trace-otlp-proto"); const { OTLPMetricExporter, } = require("@opentelemetry/exporter-metrics-otlp-proto"); const { getNodeAutoInstrumentations, } = require("@opentelemetry/auto-instrumentations-node"); // Replace these values with your AppSignal application name, environment // and push API key. These are used by the resource attributes configuration below. const name = "My app"; const pushApiKey = "0000-0000-0000-0000"; const environment = process.env.NODE_ENV || "production"; // Set the name of the service that is being monitored. A common choice is the // name of the framework used. This is used to group traces and metrics in AppSignal. const serviceName = "My service name"; // Replace `http://localhost:8099` with the address of your AppSignal collector // if it's running on another host. const endpoint = "http://localhost:8099"; const revision = childProcess.execSync("git rev-parse --short HEAD").toString(); const resource = new Resource({ "appsignal.config.name": name, "appsignal.config.environment": environment, "appsignal.config.push_api_key": "0000-0000-0000-0000", "appsignal.config.revision": revision, "appsignal.config.language_integration": "node.js", "appsignal.config.app_path": process.cwd(), "host.name": os.hostname(), [SemanticResourceAttributes.SERVICE_NAME]: "My service name", }); const sdk = new NodeSDK({ resource, traceExporter: new OTLPTraceExporter({ url: `${endpoint}/v1/traces`, }), metricReader: new PeriodicExportingMetricReader({ exportIntervalMillis: 10000, exporter: new OTLPMetricExporter({ url: `${endpoint}/v1/metrics`, }), }), instrumentations: [getNodeAutoInstrumentations()], }); sdk.start();
Reporting errors from an Express app
The Express instrumentation does not report errors from request handlers. If you're using Express for your application and would like to report errors, we recommend adding this error handler to your application:
import { trace, SpanStatusCode } from "@opentelemetry/api"; export function expressErrorHandler() { return function (err, req, res, next) { if (!err.status || err.status >= 500) { setError(err); } return next(err); }; } function setError(error) { if (error && error.name && error.message) { const activeSpan = trace.getActiveSpan(); if (activeSpan) { activeSpan.recordException(error); activeSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message, }); } else { console.log(`There is no active span, cannot set \`${error.name}\``); } } else { console.log("Cannot set error, it is not an `Error`-like object"); } }
Then use it as an error handler in your Express application:
import { expressErrorHandler } from "./error_reporter"; // or: const { expressErrorHandler } = require("./error_reporter"); const app = express(); // add this after all request handlers (routes) // but before any other error handlers app.use(expressErrorHandler());
Start your application with OpenTelemetry
To run your application with OpenTelemetry, you need to use the --require
flag to load the opentelemetry.cjs
file before starting your application:
node --require ./opentelemetry.cjs index.js
Update your package.json
file to add --require
to the command that starts your app.
{ // other configuration... "scripts": { "server": "node --require ./opentelemetry.cjs index.js" } }
You can also use the --require
flag with tools such as ts-node
and nodemon
.
When using a wrapper CLI, such as nest start
or fastify start
, to start your application, use the NODE_OPTIONS
environment variable to pass the flag to the Node.js runtime:
NODE_OPTIONS='--require ./appsignal.cjs' nest start
Test the app!
Now that all the components are connected, start your app with the required command line arguments and test if you see data arrive in AppSignal. Check the "Errors > Issue list" and "Performance > Traces" page specifically.
If after following our installation instructions you still don't see data in AppSignal, let us know and we'll help you finalize your OpenTelemetry installation!