DEV Community

Rizwan Saleem
Rizwan Saleem

Posted on

Building an OpenTelemetry Observability Mesh That Cut MTTR by 73%

Building an OpenTelemetry Observability Mesh That Cut MTTR by 73%

Building an OpenTelemetry Observability Mesh That Cut MTTR by 73%

I built a production observability mesh for a microservices platform using OpenTelemetry, Prometheus, Grafana, and centralized log correlation, and it reduced MTTR from 4.5 hours to 1.2 hours while cutting P1 incidents by 62%. The interesting part was not the tools themselves; it was the system design that made traces, metrics, and logs work together as one debugging workflow.

Why this project mattered

The platform had grown to multiple services, each with its own logs, dashboards, and alerting habits, which meant incidents were diagnosed by stitching together fragments of evidence across teams. That created long handoff chains, slow debugging, and a lot of “I think it’s in service X” guesswork. OpenTelemetry became the backbone because it provides a vendor-agnostic way to receive, process, and export telemetry data, and its model is explicitly built around traces, metrics, and logs as correlated signals.

The architecture

I designed the pipeline so application code emitted telemetry once, and the Collector handled routing, batching, filtering, and export. Prometheus stored metrics, Grafana visualized them, and trace IDs were propagated into logs so an alert could lead straight into the exact request path causing the problem.

Service code
  -> OpenTelemetry SDK / auto-instrumentation
  -> OpenTelemetry Collector
  -> Prometheus for metrics
  -> Grafana dashboards and alerting
  -> Log backend with trace/span IDs
Enter fullscreen mode Exit fullscreen mode

This matters because observability is not just “more dashboards”; it is the ability to answer “why is this happening?” without adding new instrumentation every time a novel issue appears.

The implementation

I started with auto-instrumentation so the team could get value quickly without rewriting business logic, then layered in explicit instrumentation only where business KPIs mattered. For example, I added spans around checkout, payment authorization, queue consumption, and idempotency checks, then attached counters and histograms for request volume, error rate, and latency distributions.

import { trace, metrics } from '@opentelemetry/api';

const tracer = trace.getTracer('checkout-service');
const meter = metrics.getMeter('checkout-service');

const paymentFailures = meter.createCounter('payment_failures_total');
const checkoutLatency = meter.createHistogram('checkout_latency_ms');

async function placeOrder(req, res) {
  const span = tracer.startSpan('place_order');
  const start = Date.now();

  try {
    const order = await createOrder(req.body);
    await authorizePayment(order);
    res.status(201).json({ ok: true, orderId: order.id });
  } catch (err) {
    paymentFailures.add(1, { reason: err.code || 'unknown' });
    span.recordException(err);
    span.setStatus({ code: 2, message: err.message });
    res.status(500).json({ ok: false });
  } finally {
    checkoutLatency.record(Date.now() - start);
    span.end();
  }
}
Enter fullscreen mode Exit fullscreen mode

The Collector was configured as the control point: it received OTLP traffic, normalized resource attributes, batched data, and applied tail sampling so error traces and slow requests were kept at high fidelity while healthy traffic was sampled more aggressively.

receivers:
  otlp:
    protocols:
      grpc:
      http:

processors:
  batch:
  tail_sampling:
    policies:
      - name: errors
        type: status_code
        status_code:
          status_codes: [ERROR]
      - name: slow_requests
        type: latency
        latency:
          threshold_ms: 1000

exporters:
  prometheusremotewrite:
  loki:

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, tail_sampling]
      exporters: [loki]
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheusremotewrite]
Enter fullscreen mode Exit fullscreen mode

Measurable impact

The baseline was painful: MTTR was 4.5 hours, MTTD was 45 minutes, P1 incidents averaged 8 per month, and cross-service debugging often took 2 to 4 hours. After the rollout, MTTR dropped to 1.2 hours, MTTD to 3 minutes, P1 incidents to 3 per month, and cross-service debugging to 10 to 30 minutes, with SLO compliance rising from 94% to 99.2%.

Metric Before After Change
MTTR 4.5 hours 1.2 hours -73%
MTTD 45 minutes 3 minutes -93%
P1 incidents/month 8 3 -62%
SLO compliance 94% 99.2% +5.2 pp
Cross-service debug time 2-4 hours 10-30 minutes -87%

The most important outcome was not just speed; it was confidence. Engineers stopped guessing which service was “probably broken” and started following a trace from dashboard to span to log line in minutes, which is exactly the workflow OpenTelemetry is designed to enable.

Lessons learned

The first lesson was to begin with auto-instrumentation and only add custom spans where they expose business-critical context, because that gets you coverage quickly without turning instrumentation into a project of its own. The second lesson was to treat trace correlation as a product feature, not an ops nicety, because it is the difference between “we saw an alert” and “we know the request, dependency, and code path”.

A third lesson was that tail sampling is worth the effort once the platform is stable, because keeping all errors and slow requests while sampling the rest balances cost and diagnostic quality. Finally, telemetry only becomes valuable when the team knows how to use it, so I paired the rollout with incident walkthroughs and dashboard reviews instead of assuming people would naturally adopt the new workflow.

What I would do again

I would still centralize telemetry through the Collector, because it cleanly separates application instrumentation from backend choice and makes future migrations much easier. I would also define golden signals and service-level objectives before adding more dashboards, because observability should answer the questions the business actually cares about, not just produce more charts.

A simple rule I use now is: if a metric cannot shorten a debugging session or improve a user outcome, it does not belong in the first release. That constraint kept the system focused and prevented us from building a beautiful but unused monitoring layer.

For the community

If you are building distributed systems, start by correlating traces, metrics, and logs around one painful user journey rather than trying to instrument everything at once. Then measure whether that journey becomes faster to debug, because the right observability investment should show up in MTTR, incident frequency, and support burden, not just in prettier dashboards.

I’d love to compare notes with other senior engineers who have shipped observability systems, especially if you have lessons on sampling strategy, trace-to-log correlation, or making telemetry genuinely useful to product teams.

-

Rizwan Saleem | https://rizwansaleem.co

Top comments (0)