Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.rootprint.io/llms.txt

Use this file to discover all available pages before exploring further.

Rootprint accepts OpenTelemetry logs over OTLP HTTP. Node apps can use the plain OTEL SDK, or plug OTLP into Pino or Winston — all three routes ship records through the same endpoint and land in the OTEL logs index pinned by your ingest API key (default: otel-logs-v0_9).

Setup

1

Install

npm install @opentelemetry/api-logs @opentelemetry/sdk-node @opentelemetry/sdk-logs @opentelemetry/exporter-logs-otlp-proto
Node.js 18+ is required (for native fetch and top-level await).
2

Set environment variables

export OTEL_SERVICE_NAME=my-node-service
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://<your-rootprint>/v1/logs
export OTEL_EXPORTER_OTLP_LOGS_HEADERS=Authorization=Bearer%20<your-ingest-token>
The %20 after Bearer is required — OTEL expects URL-encoded header values.
3

Minimal working example

import { logs } from '@opentelemetry/api-logs';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';

const sdk = new NodeSDK({
    logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter())]
});

sdk.start();

logs.getLogger('hello').emit({ severityText: 'INFO', body: 'Hello from Node to Rootprint' });
await sdk.shutdown();
4

Verify in Rootprint

Open Search, filter on service_name:my-node-service, and your record should appear within ~2 seconds.

Structured logging

Attributes become log-record attributes on the OTEL side — searchable and filterable in Rootprint.
logs.getLogger('app').emit({
	severityText: 'INFO',
	body: 'user signed up',
	attributes: { user_id: 'alice', plan: 'pro' }
});
With Pino, pass an object as the first argument:
log.info({ user_id: 'alice', plan: 'pro' }, 'user signed up');

Framework recipe: Express

Initialise the SDK once at process start, then log from anywhere. Keep the setup in a separate module (e.g. otel.js) and import it before your Express app.
// otel.js
import { logs } from '@opentelemetry/api-logs';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';

const sdk = new NodeSDK({
	logRecordProcessors: [new BatchLogRecordProcessor(new OTLPLogExporter())]
});

sdk.start();

export const logger = logs.getLogger('express-app');
// server.js
import './otel.js';
import express from 'express';
import { logger } from './otel.js';

const app = express();

app.use((req, _res, next) => {
	logger.emit({
		severityText: 'INFO',
		body: 'request',
		attributes: { method: req.method, path: req.path }
	});
	next();
});

app.get('/', (_req, res) => res.json({ ok: true }));
app.listen(3000);

Troubleshooting

  • 401 / 403 — the Bearer token is missing, malformed, or the %20 separator is not URL-encoded. Re-check OTEL_EXPORTER_OTLP_LOGS_HEADERS.
  • Nothing in Search — the batch processor buffers records. For short-lived scripts call await sdk.shutdown() before exit.
  • fetch is not defined — upgrade to Node.js 18+ or pass a polyfilled fetch to the exporter.
  • Pino transport not emittingpino-opentelemetry-transport reads the OTEL_* env vars itself; make sure they are set in the process that spawns the transport worker.
  • TLS errors against a self-signed endpoint — set NODE_EXTRA_CA_CERTS=/path/to/ca.pem.