Skip to main content

On This Page

Observability Practices: The 3 Pillars with a Node.js + OpenTelemetry Example

2 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

Observability Practices: The 3 Pillars with a Node.js + OpenTelemetry Example

In the modern era of distributed systems, relying solely on traditional monitoring is like diagnosing a complex illness with only a thermometer. Observability provides the tools to understand why problems occur, not just when.

Why This Matters

Traditional monitoring tells you “CPU usage is high” but fails to explain why or where the issue originated. In microservices, a single user action can trigger dozens of interdependent calls across languages and platforms. Without observability, debugging unknown failures becomes a guessing game, increasing Mean Time To Resolution (MTTR) and operational costs. Observability bridges this gap by enabling arbitrary questions about system behavior through metrics, logs, and traces.

Key Insights

  • “Metrics, logs, and traces form the three pillars of observability” (Wsalas651, 2025)
  • “OpenTelemetry enables vendor-neutral instrumentation for traces and metrics” (OpenTelemetry documentation)
  • “Prometheus and Grafana provide real-time visualization of system health” (Prometheus, Grafana)

Working Example

// app/src/tracer.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');

function setupTracing(serviceName = 'observability-demo-app') {
  const exporter = new JaegerExporter({
    endpoint: 'http://jaeger:14268/api/traces'
  });
  const sdk = new NodeSDK({
    traceExporter: exporter,
    instrumentations: [getNodeAutoInstrumentations()],
    serviceName
  });
  sdk.start()
    .then(() => console.log('OpenTelemetry initialized'))
    .catch(err => console.error('Error starting OpenTelemetry SDK', err));
  process.on('SIGTERM', () => sdk.shutdown().catch(e => console.log('Error terminating tracing', e)));
}
module.exports = { setupTracing };
// app/src/metrics.js
const client = require('prom-client');

client.collectDefaultMetrics({ timeout: 5000 });
const register = client.register;

const httpRequestCounter = new client.Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests',
  labelNames: ['method', 'route', 'status']
});

const httpRequestDuration = new client.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status'],
  buckets: [0.005, 0.01, 0.05, 0.1, 0.3, 1, 3, 5]
});

function metricsMiddleware(req, res, next) {
  const end = httpRequestDuration.startTimer();
  res.on('finish', () => {
    const route = req.route ? req.route.path : req.path;
    httpRequestCounter.inc({ method: req.method, route, status: res.statusCode });
    end({ method: req.method, route, status: res.statusCode });
  });
  next();
}
module.exports = { register, metricsMiddleware };

Practical Applications

  • Use Case: Node.js Express API with OpenTelemetry tracing and Prometheus metrics
  • Pitfall: Skipping instrumenting critical services leads to incomplete traces and undetected bottlenecks

References:


Continue reading

Next article

Online Meetings Can't Eliminate the Need to Be in the Office

Related Content