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:

Shell
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.

opentelemetry.cjs
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:

error_reporter.js
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:

index.js
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:

Shell
node --require ./opentelemetry.cjs index.js

Update your package.json file to add --require to the command that starts your app.

package.json
{ // 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:

Shell
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!