OpenTelemetry & Grafana

42OpenTelemetry & Grafana

What you will master here

  • The three pillars: logs, metrics, traces
  • OpenTelemetry (OTel) — the open standard
  • Spans, traces, propagation
  • Instrumenting Node / Java / Python apps
  • OTel Collector pipeline
  • Grafana + Prometheus + Loki + Tempo stack
  • SLI / SLO / error budget
  • SDET-specific use: assert on telemetry from tests

42.1 Three pillars of observability

PillarWhatTool stack
LogsDiscrete events with contextLoki, Elasticsearch, CloudWatch Logs
MetricsNumeric time-series (latency, count, rate)Prometheus, Datadog, CloudWatch Metrics
TracesRequest flow across services as a tree of spansTempo, Jaeger, Datadog APM

42.2 OpenTelemetry

OTel is the open standard (CNCF) for generating telemetry. One SDK in your app emits traces+metrics+logs in OTLP format; you can swap backends without rewriting instrumentation.

Components

42.3 Spans and traces

// Node manual instrumentation
import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('orders-service');

async function placeOrder(input) {
  const span = tracer.startSpan('placeOrder');
  span.setAttribute('user.id', input.userId);
  try {
    const order = await db.create(input);
    span.setAttribute('order.id', order.id);
    return order;
  } catch (e) {
    span.recordException(e);
    span.setStatus({ code: 2 /* ERROR */ });
    throw e;
  } finally {
    span.end();
  }
}

A trace = a tree of spans sharing the same traceId. Propagation happens via the traceparent HTTP header (W3C Trace Context standard). Downstream services join the same trace.

42.4 Collector pipeline

# otel-collector.yaml
receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:
    timeout: 1s
  attributes:
    actions:
      - key: env
        value: production
        action: insert

exporters:
  prometheus:
    endpoint: 0.0.0.0:8889
  otlp/tempo:
    endpoint: tempo:4317
    tls: { insecure: true }
  loki:
    endpoint: http://loki:3100/loki/api/v1/push

service:
  pipelines:
    traces:  { receivers: [otlp], processors: [batch, attributes], exporters: [otlp/tempo] }
    metrics: { receivers: [otlp], processors: [batch],            exporters: [prometheus]  }
    logs:    { receivers: [otlp], processors: [batch],            exporters: [loki]        }

42.5 Grafana

Visualisation layer. Connects to Prometheus (metrics), Loki (logs), Tempo (traces), and others. Dashboards combine all three in one view.

PromQL essentials

rate(http_requests_total[5m])                                # req/s
sum by (status) (rate(http_requests_total[5m]))              # by status
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m])))  # p95
1 - (sum(rate(http_requests_total{status=~"5.."}[5m])) /
     sum(rate(http_requests_total[5m])))                     # availability

42.6 SLI / SLO / error budget

TermMeaning
SLI (Service Level Indicator)A measurable signal (e.g. p95 latency, availability)
SLO (Service Level Objective)Target for an SLI ("99.9% availability over 30 days")
SLA (Service Level Agreement)Contractual promise to customers (often weaker than internal SLO)
Error budget1 - SLO. 99.9% SLO → 0.1% allowed downtime = ~43 min/month

If error budget is spent for the month, new feature deploys halt — only reliability work continues until next window. Mechanism to balance velocity vs reliability.

42.7 SDET use cases for OTel

test('order flow generates correct trace', async ({ request }) => {
  const r = await request.post('/api/orders', {
    data: {/*...*/},
    headers: { 'x-test-correlation': 'abc-123' },
  });
  expect(r.status()).toBe(201);

  // Wait for trace ingestion
  await expect.poll(async () => {
    const traces = await fetch('http://tempo:3200/api/search?tags=test=abc-123').then(r => r.json());
    return traces?.traces?.length ?? 0;
  }, { timeout: 10_000 }).toBeGreaterThan(0);

  const trace = await fetch('http://tempo:3200/api/traces/...').then(r => r.json());
  const spans = trace.batches.flatMap(b => b.scopeSpans).flatMap(s => s.spans);
  const names = spans.map(s => s.name);
  expect(names).toContain('checkout-handler');
  expect(names).toContain('charge-stripe');
  expect(names).toContain('email-confirmation');
});

42.8 Logs vs metrics vs traces — when each?

UsePick
"What happened at this exact moment for user X?"Logs
"Is the system slow/erroring overall?"Metrics
"How did this request flow through services?"Traces

Module 66 — OTel/Grafana Q&A

What are the three pillars of observability?
Logs (discrete events), metrics (numeric time-series), traces (request flow across services). Each answers different questions; modern observability stacks correlate them via trace ids.
What is OpenTelemetry?
Open CNCF standard for generating telemetry. SDK + auto-instrumentation + collector + OTLP wire format. Decouples instrumentation from backend — write code once, swap backends without changes.
What's a span?
A single unit of work in a trace — a service call, a DB query, an internal function. Has start/end time, attributes, status, optional events. Spans nest into a tree to form a trace.
What's W3C Trace Context?
Standard HTTP headers (traceparent, tracestate) that propagate trace identity across services. Lets the receiving service add its spans to the same trace as the caller.
What's the OTel Collector?
Standalone agent/gateway that receives telemetry from apps, batches/processes (sampling, filtering, redaction), and exports to backends. Lets you swap backends or fan out to many without changing the app.
What's PromQL rate()?
Per-second rate of increase of a counter over a window — rate(http_requests_total[5m]) gives requests/sec averaged over 5 min. Counters are monotonic; rate makes them human-readable.
SLI vs SLO vs SLA?
SLI: measurable signal. SLO: internal target on that signal. SLA: external contractual promise (usually weaker than SLO). Error budget = 1-SLO; spent budget pauses feature work.
How do you compute p95 latency in PromQL?
histogram_quantile(0.95, sum by (le) (rate(http_request_duration_seconds_bucket[5m]))). Requires the metric to be a histogram (bucketed).
How would an SDET verify a request hit the expected services?
Tag the test request with a unique correlation id. Wait for trace ingestion in Tempo/Jaeger. Query traces by that tag; assert expected span names + attributes appear. Catches "the request was supposed to call service X" failures that black-box tests miss.
What is the LGTM stack?
Grafana Labs' acronym: Loki (logs), Grafana (UI), Tempo (traces), Mimir/Prometheus (metrics). Open-source observability backend, one of the dominant stacks.