DEV Community

Cover image for OpenTelemetry for Beginners: Traces, Metrics, and Your First Setup
FlatlinerAtheist | CLI-NERDS
FlatlinerAtheist | CLI-NERDS

Posted on Edited on Originally published at clinerds.com

OpenTelemetry for Beginners: Traces, Metrics, and Your First Setup

"Something is slow" is the least actionable sentence in operations. Is it the app? The database? The network between them? OpenTelemetry (OTel) is how you stop guessing: a vendor-neutral standard for collecting traces, metrics, and logs from your applications — so you can see exactly where time and errors go.

It has become the default instrumentation layer for new projects, and for good reason: you instrument once and send data to Jaeger, Grafana Tempo, Prometheus, Datadog, or whatever backend you choose. No vendor lock-in at the SDK level.

The three signals, in plain English

  • Traces — the path of a single request through your system: which services it touched and how long each step took. This is what answers "slow where?"
  • Metrics — numbers over time: request rate, error rate, latency percentiles, memory. This is what dashboards and alerts are made of.
  • Logs — the events you already know, but correlated with traces so you can jump from "this slow request" to its exact log lines.

You don't need all three on day one. Start with traces — they deliver the biggest "oh, that's the problem" moments.

The architecture in 30 seconds

Your app uses an OTel SDK (or auto-instrumentation) to produce telemetry. It exports to the OpenTelemetry Collector — a small agent that receives, batches, and forwards data to your backend. The Collector is the key piece: it keeps your app config trivial and your backend choices flexible.

App (SDK) → OTel Collector → Jaeger / Grafana / Prometheus / vendor
Enter fullscreen mode Exit fullscreen mode

A minimal working setup with Docker

Run Jaeger (an all-in-one trace backend with a UI) and the Collector:

services:
  otel-collector:
    image: otel/opentelemetry-collector:latest
    command: ["--config=/etc/otelcol/config.yaml"]
    volumes:
      - ./otelcol-config.yaml:/etc/otelcol/config.yaml
    ports:
      - "4318:4318"   # OTLP HTTP receiver

  jaeger:
    image: jaegertracing/all-in-one:latest
    environment:
      COLLECTOR_OTLP_ENABLED: "true"
    ports:
      - "16686:16686" # Jaeger UI
Enter fullscreen mode Exit fullscreen mode

The Collector config (otelcol-config.yaml) receives OTLP and exports to Jaeger:

receivers:
  otlp:
    protocols:
      http:
exporters:
  otlp:
    endpoint: jaeger:4317
    tls:
      insecure: true
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlp]
Enter fullscreen mode Exit fullscreen mode

Instrument a Node.js app

Auto-instrumentation covers HTTP, Express, and many libraries out of the box:

npm install @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node
Enter fullscreen mode Exit fullscreen mode
// tracing.js — import this BEFORE anything else
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');

const sdk = new NodeSDK({
  instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
Enter fullscreen mode Exit fullscreen mode

Point the exporter at the Collector (default is OTLP HTTP on localhost:4318), restart the app, generate some traffic, and open http://localhost:16686. Your requests appear as traces: every HTTP call, every DB query, with durations. That slow endpoint now shows you exactly which hop eats the time.

Python, Go, Java, and .NET have equivalent auto-instrumentation packages — same pattern, same backend.

What to do with it

  1. Find your slowest endpoint in the trace list, open a trace, look at the longest span. That's your optimization target, with proof.
  2. Add custom spans around your own expensive operations (image processing, report generation) so they show up too.
  3. Add metrics later: the same SDKs expose latency and error-rate metrics for dashboards and alerts.

Keep the telemetry bill under control

Tracing every request forever can create more data than you can afford to store, especially once several services are instrumented. Start with a small service and learn what a useful trace looks like. Then configure sampling appropriate to your traffic: enough successful requests to understand normal behavior, while preserving errors and slow outliers where your backend supports it.

Be careful with span attributes and metric labels. Values such as user IDs, full URLs with unique identifiers, or request IDs create high-cardinality data that is expensive to index. Never put passwords, authorization headers, session tokens, or personal request payloads into telemetry just because the instrumentation makes it easy.

The Collector is also part of your production system. Monitor its queue, dropped data, memory use, and exporter errors. If it cannot reach the backend, your application should keep serving users rather than becoming a casualty of the observability pipeline.

The practical takeaway

OpenTelemetry isn't a monitoring product — it's the plumbing that makes any monitoring product useful without locking you in. Start with one service, one Collector, and Jaeger. The first time you watch a slow request break down hop by hop, you'll wonder how you ever debugged with logs alone.

Top comments (0)