Skip to content

Node.js

Before using OTEL to send Trace / Metric data to DataKit, make sure you have configured the collector.

OpenTelemetry Node.js collects Trace and Metric data from Node.js applications and reports it to DataKit through OTLP for unified display in Guance.

In addition to standard OpenTelemetry capabilities, Guance provides a Profile extension for Node.js. Based on @cloudcare/profiler-nodejs and @datadog/pprof, which are maintained and released by Guance, it collects wall / heap profiles and reports them to Guance through DataKit's /profiling/v1/input endpoint.

Version Support

  • Node.js: ^18.19.0 or >=20.6.0
  • OpenTelemetry: the current stable version is recommended
  • Profile extension package: @cloudcare/profiler-nodejs
  • Default Profiling endpoint: http://127.0.0.1:9529/profiling/v1/input

Automatic Instrumentation

Automatic instrumentation is the most common way to quickly integrate Node.js applications.

1) Install Dependencies

npm install \
  @opentelemetry/api \
  @opentelemetry/sdk-node \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-proto \
  @opentelemetry/exporter-metrics-otlp-proto

2) Configure with Environment Variables

export OTEL_SERVICE_NAME="nodejs-demo"
export OTEL_TRACES_EXPORTER="otlp"
export OTEL_METRICS_EXPORTER="otlp"
export OTEL_LOGS_EXPORTER="none"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://127.0.0.1:9529/otel"
export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"

Then start your application directly:

node app.js

When the default HTTP paths are used, the actual Trace and Metric endpoints are:

  • Trace:http://127.0.0.1:9529/otel/v1/traces
  • Metric:http://127.0.0.1:9529/otel/v1/metrics

3) Start from the Command Line

If you do not want to inject the configuration through the environment, set it directly in the startup command:

OTEL_SERVICE_NAME=nodejs-demo \
OTEL_TRACES_EXPORTER=otlp \
OTEL_METRICS_EXPORTER=otlp \
OTEL_LOGS_EXPORTER=none \
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \
OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:9529/otel \
NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register" \
node app.js

4) PM2 / Systemd

If the application is started by PM2, Systemd, or another process manager, add the preceding OTEL_* environment variables and NODE_OPTIONS to its startup configuration.

The two essential settings are usually:

  • OTEL_SERVICE_NAME
  • NODE_OPTIONS=--require @opentelemetry/auto-instrumentations-node/register

Add the remaining OTLP parameters based on the actual DataKit address.

Programmatic Integration

If automatic instrumentation is unsuitable, integrate the OpenTelemetry SDK programmatically.

Install Dependencies

npm install \
  @opentelemetry/api \
  @opentelemetry/sdk-node \
  @opentelemetry/resources \
  @opentelemetry/semantic-conventions \
  @opentelemetry/exporter-trace-otlp-proto \
  @opentelemetry/exporter-metrics-otlp-proto \
  @opentelemetry/instrumentation-http

Example

const { NodeSDK } = require('@opentelemetry/sdk-node');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const {
  ATTR_SERVICE_NAME,
  ATTR_SERVICE_VERSION,
  SEMRESATTRS_DEPLOYMENT_ENVIRONMENT,
} = require('@opentelemetry/semantic-conventions');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const {
  OTLPTraceExporter,
} = require('@opentelemetry/exporter-trace-otlp-proto');

const resource = resourceFromAttributes({
  [ATTR_SERVICE_NAME]: 'orders-api',
  [ATTR_SERVICE_VERSION]: '1.0.0',
  [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: 'prod',
});

const sdk = new NodeSDK({
  resource,
  traceExporter: new OTLPTraceExporter({
    url: 'http://127.0.0.1:9529/otel/v1/traces',
  }),
  instrumentations: [new HttpInstrumentation()],
});

async function main() {
  await sdk.start();
  console.log('OpenTelemetry Node.js started');
}

main().catch(err => {
  console.error(err);
  process.exit(1);
});

Node.js Profile Extension

Node.js Profile is an extension provided by Guance for Node.js. It supplements standard OTel Trace / Metric collection with profile data.

This feature is not part of the official OpenTelemetry npm packages. It is extended from OpenTelemetry Node.js and released by Guance.

Supported profile types:

  • wall
  • heap

For an initial integration, enable wall first, verify that the data path is stable, and then enable heap as needed.

Obtain @cloudcare/profiler-nodejs

The Node.js Profile extension package is:

@cloudcare/profiler-nodejs

If the public npm registry is directly accessible, run:

npm install @cloudcare/profiler-nodejs

Install Dependencies

npm install \
  @cloudcare/profiler-nodejs \
  @datadog/pprof \
  @opentelemetry/resources \
  @opentelemetry/semantic-conventions

Minimal Example

const { resourceFromAttributes } = require('@opentelemetry/resources');
const {
  ATTR_SERVICE_NAME,
  ATTR_SERVICE_VERSION,
  SEMRESATTRS_DEPLOYMENT_ENVIRONMENT,
} = require('@opentelemetry/semantic-conventions');
const {
  DatakitProfilingExporter,
  NodeProfiling,
} = require('@cloudcare/profiler-nodejs');

const profiler = new NodeProfiling({
  resource: resourceFromAttributes({
    [ATTR_SERVICE_NAME]: 'orders-api',
    [ATTR_SERVICE_VERSION]: '1.2.3',
    [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: 'prod',
  }),
  exporter: new DatakitProfilingExporter({
    endpoint: 'http://127.0.0.1:9529/profiling/v1/input',
  }),
  profileTypes: ['wall'],
  cpuProfilingEnabled: true,
});

async function main() {
  await profiler.start();
}

main().catch(err => {
  console.error(err);
  process.exit(1);
});

Use with the OpenTelemetry SDK

If the application already uses the OpenTelemetry SDK, initialize the profiler alongside it:

const { NodeSDK } = require('@opentelemetry/sdk-node');
const { resourceFromAttributes } = require('@opentelemetry/resources');
const {
  ATTR_SERVICE_NAME,
  ATTR_SERVICE_VERSION,
  SEMRESATTRS_DEPLOYMENT_ENVIRONMENT,
} = require('@opentelemetry/semantic-conventions');
const {
  DatakitProfilingExporter,
  NodeProfiling,
} = require('@cloudcare/profiler-nodejs');

const resource = resourceFromAttributes({
  [ATTR_SERVICE_NAME]: 'orders-api',
  [ATTR_SERVICE_VERSION]: '1.2.3',
  [SEMRESATTRS_DEPLOYMENT_ENVIRONMENT]: 'prod',
});

const sdk = new NodeSDK({
  resource,
});

const profiling = new NodeProfiling({
  resource,
  exporter: new DatakitProfilingExporter({
    endpoint: 'http://127.0.0.1:9529/profiling/v1/input',
  }),
});

async function main() {
  await sdk.start();
  await profiling.start();
}

process.on('SIGTERM', async () => {
  await profiling.shutdown();
  await sdk.shutdown();
  process.exit(0);
});

main().catch(err => {
  console.error(err);
  process.exit(1);
});

Trigger a Collection Manually

To verify the data path, run:

await profiling.collectOnce();

This is useful for:

  • Verifying the reachability of the Profiling endpoint during initial setup;
  • Checking whether a profile is generated and sent successfully;
  • Collecting a profile at a specific point during a load-test window.

Recommended Profile Configuration

In most cases, only the following key parameters need attention:

Parameter Default Description
endpoint http://127.0.0.1:9529/profiling/v1/input Profile upload endpoint
profileTypes ['wall', 'heap'] Profile types to collect; start with ['wall']
intervalMillis 60000 Collection interval
wallDurationMillis 10000 Duration of each wall profile

Keep the default values unless specific performance tuning is required.

Default Behavior

By default, the Node.js Profile extension:

  • Collects once every 60 seconds;
  • Collects both wall and heap profiles in each cycle;
  • Collects each wall profile for 10 seconds;
  • Enables cpuProfilingEnabled;
  • Sends profiles to http://127.0.0.1:9529/profiling/v1/input.

The exporter sends two pprof attachments:

  • wall.pprof
  • space.pprof

They contain:

  • wall.pprof: sample/count, optional cpu/nanoseconds, and wall/nanoseconds
  • space.pprof: objects/count and space/bytes

An event.json attachment is also included, where:

  • profiler is fixed to ddtrace
  • family is fixed to nodejs
  • format is fixed to pprof

This layout is compatible with the current Node.js Profile processing pipeline in Guance.

Verification

Verify Trace / Metric Reporting

Confirm that the application can reach the DataKit OTLP HTTP endpoints:

curl -i http://127.0.0.1:9529/otel/v1/traces
curl -i http://127.0.0.1:9529/otel/v1/metrics

Verify Profile Reporting

Confirm that the application can reach the Profiling endpoint:

curl -i http://127.0.0.1:9529/profiling/v1/input

Verify Logs

When a profile is reported successfully, the debug log usually contains a message similar to:

Datakit profiling export succeeded for 1 profile(s)

Verify Data in Guance

After integration, check the following in Guance:

  1. Traces are reported successfully by service;
  2. Metrics are available in the corresponding measurement;
  3. Node.js Profiles are displayed by service.

Notes

  1. Node.js Profile is a Guance extension, not an official OpenTelemetry capability;
  2. Only wall and heap profiles are currently supported;
  3. If the runtime does not provide globalThis.fetch, explicitly pass a fetch implementation;
  4. To avoid losing profiles before the process exits, call shutdown() during process termination;
  5. If only Trace / Metric data is needed, @cloudcare/profiler-nodejs is not required.

References

Feedback

Is this page helpful? ×