로그와 트레이스를 연결하기: Grafana에서 trace_id로 로그↔트레이스 점프하기
로그와 트레이스를 따로 보면 가치의 절반이고, trace_id로 연결하면 에러 로그에서 그 요청의 전체 트레이스로, 느린 스팬에서 그 시점의 로그로 한 번에 점프할 수 있습니다. 연결의 대전제는 앱 계측 시 trace_id가 로그에도 포함되는 것이며, 이것만 되면 Grafana 설정은 어렵지 않습니다. VictoriaLogs는 OpenTelemetry preset으로 trace_id Derived Field를 자동 설정하고, 트레이스→로그는 Tempo의 tracesToLogsV2나 Jaeger 데이터소스 설정으로 구성합니다. 이 글은 “OTel 트레이스 확장” 시리즈 4편(연결)이자 마지막 편으로, 로그와 트레이스를 모두 구축한 사람을 위한 “보상” 편입니다.
🔗 왜 연결이 관측성의 핵심인가 #
로그와 트레이스를 따로 보면 디버깅이 반쪽짜리입니다. 연결하면 다음이 가능해집니다.
- 로그 → 트레이스 — 에러 로그 한 줄에서 그 요청이 거친 전체 트레이스로 점프.
- 트레이스 → 로그 — 느린 스팬에서 그 순간 발생한 로그로 점프.
이 양방향 점프가 근본 원인 분석 시간을 크게 줄입니다. 트레이스를 추가한 진짜 이유가 바로 이 연결입니다.
flowchart TB
APP["앱 (계측됨)<br/>trace_id 공유"]
APP -->|"로그에 trace_id 포함"| L["VictoriaLogs<br/>(로그)"]
APP -->|"트레이스"| T["Tempo / VictoriaTraces<br/>(트레이스)"]
L <-->|"trace_id로 Grafana 링크"| T
GF["Grafana"] --- L
GF --- T🔑 대전제: trace_id가 로그에 있어야 한다 #
연결의 기반은 로그와 트레이스가 같은 trace_id를 공유하는 것입니다. 이게 안 되면 어떤 Grafana 설정도 무의미합니다.
- 앱 계측 시
trace_id(와span_id)를 로그에도 출력해야 합니다 — OTel SDK의 로그-트레이스 상관 기능이나 로깅 라이브러리에 trace context를 주입합니다. - OTel 시맨틱 컨벤션(
trace_id,span_id)을 따르면 자동 연결이 쉬워집니다.
⚠️ 로그에
trace_id가 없으면 연결은 불가능합니다. 연결이 안 될 때 가장 흔한 원인이 바로 이것이니, 계측 단계에서 반드시 확보하세요.
➡️ 로그 → 트레이스 (VictoriaLogs OTel preset) #
가장 쉬운 길은 VictoriaLogs 데이터소스의 “OpenTelemetry preset” 토글입니다(플러그인 v0.27.1+). 켜면 trace_id Derived Field와 severity용 Log level 규칙을 자동 생성합니다.
설정 절차는 다음과 같습니다.
- VictoriaLogs 데이터소스 URL을 먼저 저장합니다(Save & test). preset이 필드 형식을 감지하려면 도달 가능한 백엔드가 필요합니다.
- OpenTelemetry preset 토글을 켭니다.
- Traces 데이터소스(Tempo 또는 Jaeger)를 지정하면, 생성된
trace_idDerived Field가 해당 트레이스로 직접 링크됩니다.
💡 preset은 최근 로그를 쿼리해 자동 감지합니다:
trace_id형식(snake_casetrace_id/ camelCasetraceId), severity 필드(severity_text/SeverityText등), severity 값의 대소문자. 형식이 특이해 인식이 안 되면 수동 Derived Fields로 보정할 수 있습니다.
결과적으로 로그 라인의 trace_id를 클릭하면 해당 트레이스로 점프합니다.
⬅️ 트레이스 → 로그 (백엔드별) #
스팬에서 로그로 가는 길은 트레이스 백엔드에 따라 갈립니다.
Tempo — Tempo 데이터소스의 tracesToLogsV2를 설정합니다. 로그 데이터소스(VictoriaLogs)를 지정하고 태그 매핑·시간 시프트·쿼리를 줍니다.
1# Tempo 데이터소스 provisioning 발췌
2jsonData:
3 tracesToLogsV2:
4 datasourceUid: <victorialogs-datasource-uid>
5 spanStartTimeShift: "-1h"
6 spanEndTimeShift: "1h"
7 tags:
8 - key: "service.name"
9 value: "service_name"
10 filterByTraceID: true
11 customQuery: true
12 query: '_time:$__range trace_id:"$${__trace.traceId}"' # LogsQL 예(환경에 맞게)VictoriaTraces — Grafana에서 Jaeger 데이터소스로 연결되므로, Jaeger 데이터소스의 trace-to-logs 설정을 활용합니다.
공통 원리는 같습니다: 스팬의
trace_id(또는 태그)로 로그 데이터소스를 쿼리해 해당 로그를 찾아 링크합니다. 결과적으로 스팬의 “Logs for this span” 링크로 로그에 점프합니다.
⚙️ 데이터소스 구성 한눈에 #
두 데이터소스가 같은 Grafana에 있고 서로 datasourceUid를 참조해야 상호 링크가 됩니다.
| 방향 | 설정 위치 | 핵심 옵션 |
|---|---|---|
| 로그 → 트레이스 | VictoriaLogs 데이터소스 | OTel preset(자동) 또는 Derived Fields |
| 트레이스 → 로그 | Tempo / Jaeger 데이터소스 | tracesToLogsV2 / trace-to-logs |
- 로그: VictoriaLogs 데이터소스(
victoriametrics-logs-datasource). - 트레이스: Tempo면 Tempo 데이터소스, VictoriaTraces면 Jaeger 데이터소스.
🧪 검증 #
연결이 됐는지 양방향으로 확인합니다.
- 로그 → 트레이스: Explore에서 로그 조회 → 에러 로그의
trace_id클릭 → 트레이스가 열리는지. - 트레이스 → 로그: 트레이스 뷰에서 스팬의 “Logs for this span” → 로그가 열리는지.
안 되면 다음을 점검합니다.
| 증상 | 점검 |
|---|---|
| 아무 링크도 없음 | 로그에 trace_id가 있나(가장 흔한 원인) |
| Derived Field 인식 안 됨 | 필드 형식(snake/camel) 맞나 |
| 링크는 있는데 안 열림 | 두 데이터소스 UID 참조가 맞나 |
| 트레이스→로그에서 로그 못 찾음 | 태그 매핑(service.name)·시간 시프트·쿼리 |
📐 규모 관점 #
연결 설정 자체는 규모와 무관합니다 — 데이터소스 설정은 클러스터든 단일 노드든 동일합니다. 규모에 따라 다른 것은 그 아래 로그·트레이스 백엔드의 구성(앞 편들에서 다룸)뿐입니다.
💡 소규모라도 로그·트레이스를 둘 다 운영한다면 이 연결은 반드시 켜두세요. 설정 비용은 거의 없는데 디버깅 효율은 크게 올라갑니다.
❓ 자주 묻는 질문 #
Q. 연결이 안 됩니다. 가장 흔한 이유는?
로그에 trace_id가 없어서입니다. 앱 계측에서 trace_id를 로그에 출력하는지 먼저 확인하세요.
Q. VictoriaLogs에서 가장 쉬운 설정은?
OpenTelemetry preset 토글입니다. trace_id Derived Field를 자동 생성합니다.
Q. VictoriaTraces는 어떤 데이터소스인가요? Jaeger 데이터소스입니다. 그 데이터소스의 trace-to-logs 설정을 씁니다.
Q. trace_id 형식이 달라 인식이 안 됩니다.
snake_case/camelCase 차이입니다. preset이 자동 감지하지만, 틀리면 수동 Derived Fields로 조정하세요.
Q. 트레이스→로그에서 로그를 못 찾습니다.
태그 매핑(service.name)·시간 시프트(spanStartTimeShift/spanEndTimeShift)·쿼리를 확인하세요.
🏁 시리즈를 마치며: 통합 관측성 완성 #
여기까지가 “OTel 트레이스 확장” 시리즈의 마지막입니다. 트레이스 경로를 되짚으면:
- 개요 — 트레이스 개념과 두 가지 구축 경로(통합/독립),
- 백엔드 — Tempo 또는 VictoriaTraces,
- 연결 —
trace_id로 로그↔트레이스 점프.
그리고 이 트레이스 확장은 “OTel + VictoriaLogs 로그 스택” 시리즈(완결) 위에 얹힌 것입니다. 둘을 합치면 로그(수집·저장·조회) + 트레이스(수집·저장·조회) + 연결 = 통합 관측성이 됩니다.
핵심 교훈 3줄:
- 수집은 OTel 표준으로 — 로그·트레이스를 같은 파이프라인(같은 Gateway)으로 모읍니다.
- 저장은 신호별 전용 백엔드로 — 로그는 VictoriaLogs, 트레이스는 Tempo/VictoriaTraces.
trace_id로 연결해야 진짜 가치 — 따로 둔 신호를 이어야 디버깅이 빨라집니다.
🧭 시리즈: OTel 트레이스 확장 (완결) #
- 1편 — 분산 트레이스 개요: 두 가지 길
- 2편 — Grafana Tempo 백엔드 구축
- 3편 — VictoriaTraces 백엔드 구축
- 4편 (현재) — 로그 ↔ 트레이스 연결
이 편의 한 줄 요약: “로그와 트레이스는 trace_id로 연결해야 진짜 가치가 난다.” 대전제는 로그에 trace_id가 들어가는 것이고, 로그→트레이스는 VictoriaLogs OTel preset, 트레이스→로그는 Tempo tracesToLogsV2(또는 Jaeger trace-to-logs)로 구성합니다.
🔗 함께 보기: “OTel + VictoriaLogs 로그 스택” 시리즈(완결) — 로그 스택 전체.