Traces tell you which service was slow. Continuous profiling tells you which line of code was slow, in production, all the time — the fourth signal is finally practical.

You have narrowed the incident to one service and one endpoint. The trace says the time went "inside the handler." Then what? Continuous profiling answers the question traces cannot: which line.

The fourth signal

For years, profiling meant reproducing a problem locally and running a profiler by hand. Continuous profiling flips that: a low-overhead sampler runs in production all the time, so the flame graph for any moment is already there when you need it.

What it adds to the stack

  • Metrics tell you something is slow.
  • Traces tell you which service and span.
  • Profiles tell you which function and line, down to the CPU cycle.

Correlate it with the rest

The magic is joining a profile to the trace that triggered it. A slow span becomes a flame graph of exactly that request's execution:

traces
| where duration > 3s and service == "render"
| join profiles on trace_id
| top_functions by self_cpu
| limit 15

The overhead question

The reason this is practical now is cost. Modern sampling profilers add a couple of percent of CPU — cheap enough to leave on everywhere. That is the whole unlock: profiling stopped being a special expedition and became just another always-on signal, ready before the incident starts.