DEV Community

Alex Spinov
Alex Spinov

Posted on

OpenTelemetry JS Has a Free API That Adds Observability to Any Node.js App

OpenTelemetry is the vendor-neutral observability standard. Its JavaScript SDK gives you traces, metrics, and logs — exportable to any backend.

Auto-Instrumentation: Zero Code Changes

// tracing.ts — run before your app
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";

const sdk = new NodeSDK({
  serviceName: "my-scraping-service",
  traceExporter: new OTLPTraceExporter({ url: "http://localhost:4318/v1/traces" }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({ url: "http://localhost:4318/v1/metrics" }),
    exportIntervalMillis: 15000,
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();
Enter fullscreen mode Exit fullscreen mode
node --require ./tracing.ts server.ts
# Now ALL HTTP requests, DB queries, and external calls are traced automatically
Enter fullscreen mode Exit fullscreen mode

Manual Spans: Custom Tracing

import { trace, SpanStatusCode } from "@opentelemetry/api";

const tracer = trace.getTracer("scraping-service");

async function scrapeProduct(url: string) {
  return tracer.startActiveSpan("scrape-product", async (span) => {
    span.setAttribute("scrape.url", url);
    span.setAttribute("scrape.type", "product");

    try {
      const html = await fetch(url).then(r => r.text());
      span.setAttribute("scrape.html_size", html.length);

      const product = parseProduct(html);
      span.setAttribute("scrape.product_found", !!product);
      span.setStatus({ code: SpanStatusCode.OK });

      return product;
    } catch (error) {
      span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
      span.recordException(error);
      throw error;
    } finally {
      span.end();
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

Custom Metrics

import { metrics } from "@opentelemetry/api";

const meter = metrics.getMeter("scraping-service");

const scrapeCounter = meter.createCounter("scrapes_total", {
  description: "Total number of scrape operations",
});

const scrapeLatency = meter.createHistogram("scrape_duration_ms", {
  description: "Scrape operation duration in milliseconds",
});

const activeScrapers = meter.createUpDownCounter("active_scrapers", {
  description: "Currently running scraper instances",
});

// Usage
scrapeCounter.add(1, { status: "success", source: "amazon" });
scrapeLatency.record(1250, { source: "amazon" });
activeScrapers.add(1);
Enter fullscreen mode Exit fullscreen mode

Context Propagation

import { context, propagation } from "@opentelemetry/api";

// Inject trace context into outgoing request
const headers = {};
propagation.inject(context.active(), headers);
await fetch("https://api.example.com/data", { headers });

// Extract trace context from incoming request
const ctx = propagation.extract(context.active(), request.headers);
context.with(ctx, () => {
  // All spans created here are linked to the incoming trace
});
Enter fullscreen mode Exit fullscreen mode

Monitor your scraping pipelines? My Apify tools are built with observability in mind.

Custom monitoring solution? Email spinov001@gmail.com

Top comments (0)