All resources
Observability10 min read

Adopt OpenTelemetry traces that explain latency

A practical tracing model for context propagation, sampling, attributes, and sensitive-data boundaries.

A practical PingFlow guide for developers working at the boundary between systems.

At a glance

Key takeaways

  • Start with the boundary
  • Model the system before choosing a tool
  • Design for failure, misuse, and change
In this guide

Start with the boundary

Adopt OpenTelemetry traces that explain latency is easiest to get right when the boundary is named before the implementation begins. Decide which system owns the decision, which inputs are trusted, what the caller can observe, and what must remain private. That framing prevents a local optimization from quietly becoming an undocumented protocol.

A trace is useful when it connects a user-visible delay to the service, dependency, query, or queue transition that caused it. Adding spans everywhere creates cost and noise without improving an investigation. Start with the critical path, propagate context across supported boundaries, and choose attributes that identify behavior without capturing secrets.

Model the system before choosing a tool

Define a root span around the user or job operation and child spans for meaningful service, database, queue, and external calls. Propagate W3C trace context through HTTP and messaging, and keep a correlation ID for systems that cannot carry tracing. Use semantic conventions and record route, operation, tenant class, outcome, and version without raw payloads.

Write the model down as a small state diagram or table before selecting a library. Identify the durable state, the derived state, and the transitions that may be retried. This makes it easier to compare a managed service with an in-process implementation and to explain why a particular trade-off is acceptable for this workload.

Design for failure, misuse, and change

Missing propagation splits one request into unrelated traces, high-cardinality attributes make the backend expensive, and sampling can hide rare errors. Recording authorization tokens, email addresses, or query parameters creates a privacy incident. A trace that ends at the HTTP edge cannot explain a slow background job unless the handoff is linked.

A resilient design assumes that inputs are incomplete, dependencies are slow, operators make mistakes, and requirements will change. Put limits at the boundary, return errors that a caller can act on, and preserve enough context to distinguish a bad request from an unavailable dependency. Avoid broad fallbacks that make an unsafe state look successful.

Implementation example

Instrument the boundary and a few high-value operations first. Configure parent-based sampling with an error or latency decision, add span status and exception events, and keep instrumentation initialization before request handling. Link asynchronous work with a span link or explicit job ID. Redact headers and attributes through a centralized processor.

Keep the first implementation narrow enough to review line by line. Make inputs, outputs, authorization context, and failure behavior explicit instead of hiding them behind a convenience helper. The example should be safe to run with synthetic data, emit a correlation identifier, and leave a durable artifact that another engineer can inspect after the request has finished.

text
span = tracer.start_span('dns.analyze')
span.set_attribute('route', '/api/v1/dns/analyze')
span.record_exception(error)
span.end()

Verify and troubleshoot

Trace a success, validation error, timeout, retry, queue handoff, database lock, and sampled-out request. Confirm parent and child IDs across services and that an exception does not duplicate a secret in multiple attributes. Compare trace duration with access logs and metrics to find missing or double-counted work.

Use a small test matrix that covers the ordinary path, an empty or missing input, a duplicate request, a timeout, a permission failure, and a version mismatch. Assert both the response and the side effects. When a test fails, compare the observed transition with the model rather than adding a retry or widening a timeout without evidence.

Operations and recovery

Monitor export backlog, sampling rate, collector health, trace size, attribute cardinality, and privacy filter failures. Keep a fallback that preserves logs and metrics when the collector is unavailable. During an incident, temporarily increase sampling for a narrow route or error while respecting cost and data limits.

Give the operator a bounded recovery action: replay a safe event, rebuild a derived view, rotate a credential, drain a queue, or roll back a compatible revision. Record the owner, retention period, alert threshold, and rollback condition next to the implementation. A runbook is useful only when it can be followed without reconstructing the design from production logs.

A practical decision guide

For a small service, prefer the design with the fewest hidden states that still meets the observability requirement. Add a managed dependency when it removes a failure mode you can measure, not simply because it is popular. Keep the interface replaceable by isolating provider-specific code behind a narrow adapter and by testing the behavior your users depend on.

Revisit the decision when traffic shape, data sensitivity, team ownership, or recovery objectives change. A design that is excellent for a single tenant or a low-volume internal tool can be the wrong design for a public multi-tenant path. Record the assumptions so the next change starts with evidence rather than folklore.

An implementation checklist

Before publishing a change related to adopt opentelemetry traces that explain latency, write down the input contract, authorization context, state transitions, limits, and user-visible errors. Identify the smallest synthetic dataset that demonstrates the normal path and the smallest dataset that demonstrates the dangerous path. Add a correlation ID to the example, make retries deliberate, and decide which artifacts can be retained for support without copying secrets or unnecessary personal data. This checklist is deliberately boring: repeatable release evidence is more valuable than a clever demo.

Use a disposable environment to exercise the implementation with realistic concurrency and a dependency failure. Compare the observed result with the contract, then record the measured latency, resource use, and recovery action. If a managed service or library is involved, pin its version and capture the relevant configuration. Ship behind a reversible change when the behavior is new, and schedule a follow-up review after real traffic reveals assumptions that a test fixture could not.

References and further reading

Use the OpenTelemetry specification, semantic conventions, W3C Trace Context, and the collector deployment guidance. Document which attributes are allowed, which are redacted, and how trace IDs connect to support workflows.

Prefer primary protocol specifications, vendor security documentation, and measured behavior from a disposable environment. Read the failure and deprecation sections, not only the happy-path quick start. A short reference list attached to the code gives future maintainers a way to distinguish an intentional constraint from an accidental implementation detail.