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();
node --require ./tracing.ts server.ts
# Now ALL HTTP requests, DB queries, and external calls are traced automatically
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();
}
});
}
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);
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
});
Monitor your scraping pipelines? My Apify tools are built with observability in mind.
Custom monitoring solution? Email spinov001@gmail.com
Top comments (0)