Observability · how we see inside our apps

Observability · correlation

How one click gets you to the next signal

Logs, spans and profiles share the trace id, so Grafana jumps between them exactly. Metrics carry no trace id here, so a metric links to traces only by shared labels and time: you get a list of likely traces, not the exact one.

  • Trace ↔ log ↔ profile links were proven on a real local stack. TESTED · Q15
  • VictoriaMetrics drops exemplars, and Bun's OpenTelemetry has none. TESTED · Q1; Q13
  • How to link metrics to traces is open pick 1. OPEN · A193 pick 1

Tap a starting signal. The diagram shows each jump, what it rides on, and how strong it is.

Start from
Links between metric, trace, log, profile and FlutterMetricVictoriaMetricsTraceTempoLogLokiProfilePyroscopeFlutterFaro
  • exact one id, one target
  • good same service and name
  • rough labels + time window
exact

Trace → Log

You click: "Logs for this span" on a span

You get: only the log lines with that trace id

TESTED · Q15

exact

Trace → Profile

You click: "Profile" on a span (Go, .NET)

You get: a flame graph of only that span's samples

TESTED · Q8; Q9

good

Trace → Metric

You click: "Metrics" on a span

You get: span metrics for that service and span name

DECIDED · A193 tab 5 NOT TESTED · not in the spike config

The trace context: one id, carried by itself

  • Each request gets a trace id. Every span, log line and (in Go and .NET) profile sample in that request carries it. DECIDED · A193 tab 1 #2
  • You never pass it by hand. Bun keeps it in AsyncLocalStorage, Go in ctx, .NET in Activity.Current, Dart in a Zone. DECIDED · A193 tab 3
  • Bun proved it survives await, timers and setImmediate. TESTED · Q2
  • Between services it travels in the W3C traceparent header. Example: 00-4bf92f35…-00f067aa…-01. DECIDED · A193 tab 4
  • Go sets no propagator today, so incoming traces are ignored. The new Register() fixes it. DECIDED · A193 tab 4
  • Flutter: Faro puts trace_id and span_id on every signal and sends traceparent on HTTP calls. TESTED · Q17
  • Faro's fields land in Loki under other names, and obs must map them. The record says event_data_*; the spike shows log context arrives as context_*. TESTED · Q17, loki-faro-response.json OPEN · fix the mapping

Each jump, honestly

JumpRides onStrengthStatus
log → tracetrace_id on the log lineexact, if the trace was keptTESTED · Q15 PROPOSED · sampling caveat
trace → logstrace id search in LokiexactTESTED · Q15
trace → profilespan id on each sample (Go, .NET)exact, one spanTESTED · Q8; Q9; Q15
trace → profile, Bunservice name + time windowroughTESTED · Q4
trace → metricsspan metrics made by Tempogood: same service and spanDECIDED · A193 tab 5 NOT TESTED · jaeda: not shown in a spike table
metric → tracessame LPSM and route labels + timerough: many tracesOPEN · A193 pick 1 NOT TESTED · not tested
Flutter span → backend spantraceparent headerexactTESTED · Q17, wire only

Sampling can break the exact links. With raichu's ratio 0.1, the app keeps about 1 in 10 traces. Log lines still carry the trace id, so up to 9 in 10 log → trace clicks may find nothing. The record leaves "keep every error trace" to a collector, but a collector never sees spans the app dropped. See weak spot 1. DECIDED · A193 tab 2; tab 1, jaeda call 13 PROPOSED · jaeda reading NOT TESTED · not tested

Why no exemplars

  • An exemplar is a trace id stored on a metric point. Click a spike on a graph → open that one trace. DECIDED · A193 terms
  • VictoriaMetrics accepts exemplars and silently drops them. query_exemplars always returns []. Its maintainer says it does not support them. TESTED · Q13
  • OpenTelemetry for JavaScript has no exemplar code at all. No trace id bytes in the payload. So Bun would have none, even with a store that keeps them. TESTED · Q1
  • Mimir keeps exemplars (50,000 stored in a test), but only for Go and .NET, and its query was about 2× slower. TESTED · A193 tab 5, Mimir test 09 Nov; numbers only in a193-mimir.md, no results file
  • A metric point and its spans carry the same labels: the 4 LPSM keys plus the route or provider. OPEN · A193 pick 1, recommended
  • Example: a spike in checkout_charges_total{durian_cloud_service="checkout", payment_provider="stripe", outcome="error"} at 14:07. PROPOSED · this site
  • You click it → Grafana searches Tempo for spans of checkout with payment.provider=stripe and status error, between 14:02 and 14:12. PROPOSED · this site
  • You get a list of traces, not the one trace. Usually good enough; worse when many requests look alike. PROPOSED · this site
  • Nothing forces the metric and span to use the same label names. If a metric says provider and the span says payment.provider, the link finds nothing. obs query --live runs each link on the seed and fails a broken one. DECIDED · A193 tab 5

The 4 LPSM keys: one query finds any signal

PartStandard keyOur keyStored as
Landscapedeployment.environment.namedurian.cloud.landscapedurian_cloud_landscape
Platformservice.namespacedurian.cloud.platformdurian_cloud_platform
Serviceservice.namedurian.cloud.servicedurian_cloud_service
Module—durian.cloud.moduledurian_cloud_module
  • Both keys are sent, so standard tools work and one query on our keys alone finds any signal. DECIDED · A193 tab 1 #4
  • Our keys use dots (your call on 09 Nov). DECIDED · A193 tab 1 #19
  • Example LogQL: {durian_cloud_landscape="pichu", durian_cloud_service="checkout"}. DECIDED · A193 tab 5
  • Old atomi.* keys are dropped. Never atomi.*. DECIDED · A193 tab 1 #4
  • Not yet checked: how each store turns these keys into labels. NOT TESTED · A193 tab 5 "check how each store exposes"

The record disagrees with itself here. Decision 19 says dots (durian.cloud.landscape). The main view and tab 5 still say a slash (durian.cloud/landscape). This site follows decision 19, the newer ruling. The event key is still written durian.cloud/event everywhere. Both store as underscores, so queries do not change. DECIDED · A193 tab 1 #19 OPEN · fix the record

  • The seed writes one linked set per request: a trace, its log lines, metric points with the same LPSM keys, and a span profile. DECIDED · A193 tab 2
  • obs query --live runs every jump. A broken link fails step 4. DECIDED · A193 tab 5
  • Example failure: link trace→logs: 0 log lines for trace 4bf92f35… (datasource derived field uses "traceID", logs carry "trace_id"). DECIDED · A193 tab 5
  • obs assert (e2e) checks 100% of log events carry trace ids. DECIDED · A193 tab 2

Open pick 1 lives here: keep VictoriaMetrics and link by shared labels, or move to Mimir for exemplars. Options and trade-offs. OPEN · A193 pick 1

All pages