Prerequisites
- Node.js version 18 or higher
- An instance of SigNoz (either Cloud or Self-Hosted)
- A Node.js application that uses the Bunyan logger
Send logs to SigNoz
Auto-instrumentation automatically captures:
- Bunyan logs with trace correlation
- HTTP requests (incoming and outgoing)
Step 1: Install dependencies
Install the required OpenTelemetry dependencies:
npm install --save @opentelemetry/api \
@opentelemetry/auto-instrumentations-node
yarn add @opentelemetry/api \
@opentelemetry/auto-instrumentations-node
Step 2: Run the application
Set the environment variables and run your application using the NODE_OPTIONS environment variable:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
export OTEL_SERVICE_NAME="<service_name>"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
node index.js # Replace with your entry file
Replace the following:
<service_name>: Your service name.<region>: Your SigNoz Cloud region.<your-ingestion-key>: Your ingestion key.
If using a local OTel Collector (VM), use:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
export OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
export OTEL_SERVICE_NAME="<service_name>"
# Do not set OTEL_EXPORTER_OTLP_HEADERS when using the local collector
export NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
node index.js # Replace with your entry file
Add these environment variables to your deployment manifest:
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: 'https://ingest.<region>.signoz.cloud:443'
- name: OTEL_EXPORTER_OTLP_HEADERS
value: 'signoz-ingestion-key=<your-ingestion-key>'
- name: OTEL_NODE_RESOURCE_DETECTORS
value: 'env,host,os'
- name: OTEL_SERVICE_NAME
value: '<service_name>'
- name: NODE_OPTIONS
value: '--require @opentelemetry/auto-instrumentations-node/register'
Replace the placeholders:
<region>: Your SigNoz Cloud region.<your-ingestion-key>: Your ingestion key.<service_name>: Your service name.
Avoiding Duplicate Logs
If using the k8s-infra chart (which auto-collects container logs), disable log collection for this application to prevent duplicates.
Create a Dockerfile in your project root:
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies including OpenTelemetry auto-instrumentation
RUN npm install
RUN npm install @opentelemetry/api @opentelemetry/auto-instrumentations-node
# Copy application code
COPY . .
# Set OpenTelemetry environment variables
ENV OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
ENV OTEL_SERVICE_NAME="<service_name>"
ENV OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<your-ingestion-key>"
ENV NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
# Expose application port
EXPOSE 3000
# Start the application
CMD ["node", "index.js"]
Build and run the container:
# Build the image
docker build -t nodejs-bunyan-app .
# Run the container
docker run -p 3000:3000 nodejs-bunyan-app
Replace the placeholders in the Dockerfile:
<service_name>: Your service name.<region>: Your SigNoz Cloud region.<your-ingestion-key>: Your ingestion key.
Set environment variables in PowerShell:
$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://ingest.<region>.signoz.cloud:443"
$env:OTEL_NODE_RESOURCE_DETECTORS = "env,host,os"
$env:OTEL_SERVICE_NAME = "<service_name>"
$env:OTEL_EXPORTER_OTLP_HEADERS = "signoz-ingestion-key=<your-ingestion-key>"
$env:NODE_OPTIONS = "--require @opentelemetry/auto-instrumentations-node/register"
node index.js # Replace with your entry file
Replace the placeholders:
<service_name>: Your service name.<region>: Your SigNoz Cloud region.<your-ingestion-key>: Your ingestion key.
Note
By default, auto-instrumentation collects traces and metrics. If you want to collect only logs, you can disable traces and metrics by setting the following environment variables:
export OTEL_TRACES_EXPORTER="none"
export OTEL_METRICS_EXPORTER="none"
Step 1: Install dependencies
Install the required OpenTelemetry dependencies:
npm install @opentelemetry/auto-instrumentations-node@^0.67.2 \
@opentelemetry/sdk-node@^0.208.0 \
@opentelemetry/exporter-logs-otlp-http@^0.208.0 \
@opentelemetry/instrumentation-bunyan@^0.54.0 \
bunyan@^1.8.15 \
express@^5.1.0
yarn add @opentelemetry/auto-instrumentations-node@^0.67.2 \
@opentelemetry/sdk-node@^0.208.0 \
@opentelemetry/exporter-logs-otlp-http@^0.208.0 \
@opentelemetry/instrumentation-bunyan@^0.54.0 \
bunyan@^1.8.15 \
express@^5.1.0
Step 2: Set environment variables
Set the following environment variables to configure the OpenTelemetry exporter:
export OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
export OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<SIGNOZ_INGESTION_KEY>"
export OTEL_SERVICE_NAME="nodejs-bunyan-logger"
Replace the following:
<region>: Your SigNoz Cloud region.<SIGNOZ_INGESTION_KEY>: Your SigNoz ingestion key.
If using a local OTel Collector (VM), use:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
# Do not set OTEL_EXPORTER_OTLP_HEADERS
Add these environment variables to your deployment manifest:
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: 'https://ingest.<region>.signoz.cloud:443'
- name: OTEL_EXPORTER_OTLP_HEADERS
value: 'signoz-ingestion-key=<SIGNOZ_INGESTION_KEY>'
- name: OTEL_SERVICE_NAME
value: 'nodejs-bunyan-logger'
Replace the following:
<region>: Your SigNoz Cloud region.<SIGNOZ_INGESTION_KEY>: Your SigNoz ingestion key.
Avoiding Duplicate Logs
If using the k8s-infra chart (which auto-collects container logs), disable log collection for this application to prevent duplicates.
Create a Dockerfile in your project root:
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy instrumentation file and application code
COPY logging-instrumentation.js ./
COPY . .
# Set OpenTelemetry environment variables
ENV OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<region>.signoz.cloud:443"
ENV OTEL_EXPORTER_OTLP_HEADERS="signoz-ingestion-key=<SIGNOZ_INGESTION_KEY>"
ENV OTEL_SERVICE_NAME="nodejs-bunyan-logger"
# Expose application port
EXPOSE 3000
# Start the application
CMD ["node", "index.js"]
Build and run the container:
# Build the image
docker build -t nodejs-bunyan-app .
# Run the container
docker run -p 3000:3000 nodejs-bunyan-app
Replace the placeholders in the Dockerfile:
<region>: Your SigNoz Cloud region.<SIGNOZ_INGESTION_KEY>: Ingestion key for your SigNoz Cloud, see Ingestion Keys.
Set environment variables in PowerShell:
$env:OTEL_EXPORTER_OTLP_ENDPOINT = "https://ingest.<region>.signoz.cloud:443"
$env:OTEL_EXPORTER_OTLP_HEADERS = "signoz-ingestion-key=<SIGNOZ_INGESTION_KEY>"
$env:OTEL_SERVICE_NAME = "nodejs-bunyan-logger"
Replace the placeholders:
<region>: Your SigNoz Cloud region.<SIGNOZ_INGESTION_KEY>: Ingestion key for your SigNoz Cloud, see Ingestion Keys.
Step 3: Configure OpenTelemetry SDK
Create a file named logging-instrumentation.js or logging-instrumentation.ts in your project root. This code reads the configuration from the environment variables set in the previous step.
const { NodeSDK } = require("@opentelemetry/sdk-node");
const { OTLPLogExporter } = require("@opentelemetry/exporter-logs-otlp-http");
const {
BunyanInstrumentation,
} = require("@opentelemetry/instrumentation-bunyan");
const { resourceFromAttributes } = require("@opentelemetry/resources");
const { ATTR_SERVICE_NAME } = require("@opentelemetry/semantic-conventions");
const { BatchLogRecordProcessor } = require("@opentelemetry/sdk-logs");
// Configure the OTLP log exporter
// It automatically reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS
const logExporter = new OTLPLogExporter({});
// Initialize the OpenTelemetry SDK
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]:
process.env.OTEL_SERVICE_NAME || "nodejs-bunyan-logger",
}),
logRecordProcessor: new BatchLogRecordProcessor(logExporter),
instrumentations: [
new BunyanInstrumentation({
// configure the instrumentation
}),
],
});
sdk.start();
// Graceful shutdown
process.on("SIGTERM", () => {
sdk
.shutdown()
.then(() => console.log("OpenTelemetry SDK shut down successfully"))
.catch((error) => console.error("Error shutting down SDK", error))
.finally(() => process.exit(0));
});
module.exports = sdk;
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
import { BunyanInstrumentation } from "@opentelemetry/instrumentation-bunyan";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs";
// Configure the OTLP log exporter
// It automatically reads OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_EXPORTER_OTLP_HEADERS
const logExporter = new OTLPLogExporter({});
// Initialize the OpenTelemetry SDK
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]:
process.env.OTEL_SERVICE_NAME || "nodejs-bunyan-logger",
}),
logRecordProcessor: new BatchLogRecordProcessor(logExporter),
instrumentations: [
new BunyanInstrumentation({
// configure the instrumentation
}),
],
});
sdk.start();
// Graceful shutdown
process.on("SIGTERM", () => {
sdk
.shutdown()
.then(() => console.log("OpenTelemetry SDK shut down successfully"))
.catch((error) => console.error("Error shutting down SDK", error))
.finally(() => process.exit(0));
});
module.exports = sdk;
Check out the Bunyan instrumentation options here.
Step 4: Initialize OpenTelemetry SDK
Import the instrumentation at the very top of your main application file (e.g., index.js or index.ts).
require("./logging-instrumentation"); // Must be imported first
---
const bunyan = require("bunyan");
const express = require("express");
// Create your Bunyan logger as usual
const logger = bunyan.createLogger({
name: "express-app",
level: "info",
});
// Your application code
const app = express();
app.get("/", (req, res) => {
logger.info({ path: req.path, method: req.method }, "Request received");
res.send("Hello World!");
});
app.get("/error-log", (req, res) => {
logger.error(
{ path: req.path, method: req.method },
"Error log endpoint hit",
);
res.status(500).send("Error log endpoint");
});
app.listen(3000, () => {
logger.info("Server listening on port 3000");
});
import "./logging-instrumentation"; // Must be first!
---
import * as bunyan from "bunyan";
import express, { Request, Response } from "express";
// Now create your Bunyan logger as usual
const logger = bunyan.createLogger({
name: "express-app",
level: "info",
});
// Your application code
const app: express.Express = express();
app.get("/", (req: Request, res: Response) => {
logger.info({ path: req.path, method: req.method }, "Request received");
res.send("Hello World!");
});
app.get("/error-log", (req: Request, res: Response) => {
logger.error(
{ path: req.path, method: req.method },
"Error log endpoint hit",
);
res.status(500).send("Error log endpoint");
});
app.listen(3000, () => {
logger.info("Server listening on port 3000");
});
Step 5: Log Correlation (Optional)
Bunyan logs created within a tracing span are automatically enriched with span and trace IDs, enabling correlation. The added fields are:
trace_idspan_idtrace_flags
You can use tracer.startActiveSpan to correlate logs with traces. Here is an example:
const api = require('@opentelemetry/api')
const tracer = api.trace.getTracer('example')
tracer.startActiveSpan('process-order', (span) => {
logger.info({ orderId: '12345' }, 'Processing order')
// Log record will include: trace_id, span_id, trace_flags
span.end()
})
import * as api from '@opentelemetry/api'
const tracer = api.trace.getTracer('example')
tracer.startActiveSpan('process-order', (span) => {
logger.info({ orderId: '12345' }, 'Processing order')
// Log record will include: trace_id, span_id, trace_flags
span.end()
})
Step 6: Run the Application
Run your application:
npm run dev
# or your application's start command
Validate Logs
Open your SigNoz instance and navigate to the Logs section to view the logs sent from your application.


Setup OpenTelemetry Collector (Optional)
What is the OpenTelemetry Collector?
Think of the OTel Collector as a middleman between your app and SigNoz. Instead of your application sending data directly to SigNoz, it sends everything to the Collector first, which then forwards it along.
Why use it?
- Cleaning up data — Filter out noisy traces you don't care about, or remove sensitive info before it leaves your servers.
- Keeping your app lightweight — Let the Collector handle batching, retries, and compression instead of your application code.
- Adding context automatically — The Collector can tag your data with useful info like which Kubernetes pod or cloud region it came from.
- Future flexibility — Want to send data to multiple backends later? The Collector makes that easy without changing your app.
See Switch from direct export to Collector for step-by-step instructions to convert your setup.
For more details, see Why use the OpenTelemetry Collector? and the Collector configuration guide.
Troubleshooting
Why do I see "404 Not Found" errors?
This usually happens if the endpoint URL is incorrect.
- Cloud:
https://ingest.<region>.signoz.cloud:443 - Self-Hosted:
http://localhost:4318
Why are my logs not appearing in SigNoz?
- Check if the application started successfully and is actually generating logs.
- Verify your
<region>and<SIGNOZ_INGESTION_KEY>are correct.
Next Steps
Get Help
If you need help with the steps in this topic, please reach out to us on SigNoz Community Slack.
If you are a SigNoz Cloud user, please use in product chat support located at the bottom right corner of your SigNoz instance or contact us at cloud-support@signoz.io.