Skip to content

Correlate traces with logs

A trace tells you where a request spent its time. The logs tell you why. Correlation stitches the two together, so a slow span is one click from the lines it emitted.

It works in both directions: the log detail pane shows View trace for any line carrying a trace ID, and a trace can pull up every log line belonging to it.

Include a trace_id field on the log entry. Both of these work, and the top-level form is preferred because it is indexed:

{
"level": "error",
"message": "Failed to charge card",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"metadata": { "span_id": "00f067aa0ba902b7" }
}
{
"level": "error",
"message": "Failed to charge card",
"metadata": {
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7"
}
}

span_id is optional and enables per-span drill-down. Note the asymmetry: span_id is only read from inside metadata, never from the top level.

If you use a log parser to extract fields from unstructured lines, mapping a field to trace_id populates the indexed column too, so parsed logs correlate just as well as structured ones.

Terminal window
curl -G "https://api.siteqwality.com/traces/4bf92f3577b34da6a3ce929d0e0e4736/logs" \
-H "Authorization: Bearer $SITEQWALITY_API_KEY" \
--data-urlencode "limit=200"
ParamDefaultNotes
span_idnoneNarrow to one span. Read from metadata.span_id.
limit100Maximum 500.

Each entry returns timestamp, level, message, host, source, span_id and trace_id, in ascending time order. This is a slimmer shape than GET /logs/query: there is no env, tags or metadata. The total field is the unlimited match count, so it can exceed the number of entries returned when the limit bites.

The trace ID in the path must be pure hexadecimal or the request is rejected with a 400.

Logs written before trace_id became an indexed column are still found: the query falls back to reading trace_id out of the metadata blob for rows that have no value in the column. Nothing needs backfilling and no action is required on your side.