← Back to blog
Product 4 min read 炬鲸团队

One trace_id Across Logs, Metrics, and Traces: Correlated Debugging in JuJing OBSERVE

How JuJing OBSERVE uses a single trace_id to tie logs, metrics, and traces together—the full path from alert to root cause, plus the three things you must get right for correlation to actually work.

One trace_id Across Logs, Metrics, and Traces: Correlated Debugging in JuJing OBSERVE

Use an observability platform long enough and you hit an awkward truth: logs, metrics, and traces live in three separate systems, so debugging a single incident means hopping between three pages and mentally aligning timestamps. The person on call ends up reconstructing a timeline by hand, which is slow and error-prone at 3 a.m. JuJing OBSERVE takes a different approach—it uses trace_id as the single join key that points all three at the same request. This piece explains how that correlation works end to end, and the things you have to get right at onboarding time for it to actually pay off.

The underlying logic: one key across three data shapes

Logs, metrics, and traces are three different data shapes. Logs are discrete text lines, metrics are aggregated numbers, and traces are span trees with parent-child relationships. Making them line up requires a shared key. JuJing uses trace_id as that key, falling back to request_id when there is no trace:

  • Logs carry a trace_id field, so one search pulls up every log line from a single request.
  • Spans record the same trace_id, so the call graph can pinpoint a specific hop.
  • Metric samples attach trace_id as an exemplar, letting you reverse-lookup raw samples from an aggregated value.

Once all three share a key, debugging stops being "guess the system first, then dig through logs" and becomes a drill-down along the key. A concrete example: a payment order times out, and you type trace_id = "4bf92f3577b34da6" into the search box. The platform returns every log line that order produced as it passed through gateway, account, risk-control, and settlement—in time order—plus the span tree for that chain and the latency metrics for each service. Three views of the same request, no manual cross-referencing required.

When there is no trace: fall back to request_id

Not every system has tracing yet. For legacy systems that haven't adopted OpenTelemetry, use request_id or a business order number to get the same correlation: put the field on both logs and metrics, and a search still reconstructs the context of a single request. Later, as those systems migrate to OTel, map request_id onto trace_id and the correlation upgrades seamlessly—no rebuild required. The point is that correlated debugging does not demand a big-bang migration; you can roll it out system by system.

From alert to root cause: one complete path

A typical debugging run follows this path:

  1. An alert fires: p99_latency > 500ms sustained for three minutes.
  2. On the metrics page you confirm the abnormal dimension—which service, which endpoint is degrading.
  3. You pull the trace_id of a slow request from the metric's exemplar.
  4. In the trace view you walk the span tree and find the hop with the largest latency share.
  5. You filter logs by the same trace_id and see that hop's exception stack directly.

The whole path never switches systems or manually aligns timestamps, which is exactly what shrinks mean time to resolution from hours to minutes. Step 3 is the linchpin: if your metrics carry no exemplar, you fall back to guessing which request was slow, and the trace and log drill-down never happens.

Config details that make correlation work

Correlation is not on by default. Get these wrong and trace_id stays broken:

  1. Logs must inject trace_id. In Java, use Logback MDC and let OpenTelemetry's io.opentelemetry.instrumentation.logback module inject it automatically. In Python and Go you add a trace_id placeholder to the log format yourself. After injecting, spot-check a production log line to confirm the field is actually there—not just green in your local run.
  2. Cross-service propagation must not drop. trace_id travels between services in the traceparent HTTP header or a message header. Gateways, message queues, and scheduled jobs are the easiest places to break the chain—check those boundaries specifically.
  3. Sampling policies must agree. If traces and logs are sampled independently, you end up with traces that have no logs, or the reverse. Keep logs at 100%, and sample traces adaptively by error rate and slow requests, so both are available when you actually debug.
  4. Clocks must be aligned. Span durations, log timelines, and metric timestamps all rely on a consistent clock. If NTP drifts between nodes, span durations come out negative and logs stop matching up. Confirm every node runs NTP when you onboard.

Practical advice

Get these items right on one core chain first, run the full "alert → trace → logs" loop once end to end, then roll it out to every service. Correlation is only real once it has survived a genuine outage—not just a demo screenshot. The single most direct measure of success: on your last real incident, how long did it take from alert to confirmed root cause?