[4편] OTEL Java Agent + Tempo + 슬로우 쿼리 연동

minpractice_jhj·2025년 8월 20일

Side Projects

목록 보기
5/16
post-thumbnail

들어가며

앞선 3편에서는 보이는 것(메트릭·로그) 을 다듬었다면, 이번 4편은 보이지 않던 요청 흐름(Trace) 을 꺼내는 단계다.

나는 SDK 대신 Java Agent를 택했다. 코드 수정 없이 시작·중단이 가능해서, 실험 → 측정 → 개선 루프를 빠르게 돌리기에 유리했기 때문이다.


1) OpenTelemetry Java Agent 적용 (내 선택과 시행착오)

처음엔 SDK로 붙여보려 했지만, 배포 주기가 길어지는 게 싫었다.

그래서 JVM 옵션만으로 켜고 끌 수 있는 Agent 방식을 선택했다.

실행 옵션 (내 로컬 예시):

-javaagent:C:/dev/otel-agent/opentelemetry-javaagent.jar ^
-Dotel.service.name=pinup-service ^
-Dotel.exporter.otlp.endpoint=http://localhost:4318 ^
-Dotel.resource.attributes=deployment.environment=local
  • service.name 으로 서비스 식별 통일
  • Collector는 HTTP(4318)로 받도록 구성 (아래 2번과 맞춤)

UI 성공 확인 포인트 (로그 안 봄):

애플리케이션 실행 직후 콘솔에 opentelemetry-javaagent - version: ... 문구가 보이면 Agent 로딩 성공.


2) Collector → Tempo/Loki 라우팅 (필요 최소 설정)

나는 traces는 Tempo로, logs는 Loki로 보냈다. (metrics는 Prometheus 그대로 유지)

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

exporters:
  otlphttp/tempo:
    endpoint: http://tempo:4319
  loki:
    endpoint: http://loki:3100/loki/api/v1/push
  debug:
    verbosity: detailed   # 로컬 실험용(필수 아님)

processors:
  batch: {}

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp/tempo]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [loki]

UI 성공 확인 포인트:

Grafana → Connections → Data sources → Tempo → Save & test → ✅ Success


3) Grafana Explore에서 Trace 바로 확인 (TraceQL)

이제 진짜로 트레이스를 눈으로 본다.

  • 좌측 메뉴 → Explore → Tempo 선택
  • Time range: 최근 15분
  • TraceQL 예시:
service.name = "pinup-service"

혹은

span.name = "GET /actuator/prometheus"

UI 성공 확인 포인트:

  • 검색 결과에 최근 요청들이 보이면 수집/저장/조회 OK
  • 아무 트레이스 클릭 → 상위/하위 스팬 트리와 Timeline 표시


4) 로그 ↔ 트레이스 연결 (원클릭 점프)

로그에 traceId 를 실어두면, Grafana에서 로그 → 트레이스로 원클릭 이동이 가능하다.

나는 Logback + loki4j 로 JSON 로그에 traceId를 넣었다.

logback-spring.xml (요약):

<appender name="LOKI" class="com.github.loki4j.logback.Loki4jAppender">
  <http>
    <url>http://localhost:3100/loki/api/v1/push</url>
  </http>
  <format>
    <json>
      <field name="timestamp" pattern="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}" />
      <field name="level" pattern="%level" />
      <field name="message" pattern="%msg" />
      <field name="traceId" pattern="%X{traceId}" />
    </json>
  </format>
</appender>

Grafana Derived Fields 설정:

  • Connections → Data sources → Loki → Derived fields → Add
  • Name: traceId
  • Regex: "traceId":"([a-f0-9]{32})"
  • Data source: Tempo
  • URL label/value: ${__value.raw}

UI 성공 확인 포인트:

  • Explore → Loki에서 {job="pinup-service"} | json
  • 로그에 traceId 필드 오른쪽 링크/아이콘 클릭 → Tempo 트레이스로 점프

시행착오: traceId 가 대문자/하이픈 포함일 땐 정규식이 안 맞았다.

내 케이스는 소문자 32hex 기준이라 위 정규식이 잘 맞음.


5) PostgreSQL 슬로우 쿼리 시각화

슬로우 쿼리는 두 갈래로 본다.

  1. Trace (Tempo): 요청 안에서 DB 스팬의 타이밍/관계
  2. Top-N 쿼리 (Grafana+Postgres): 누적/평균 실행 시간 상위

PostgreSQL 준비:

# postgresql.conf
shared_preload_libraries = 'pg_stat_statements'

최초 1회:

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

Grafana 쿼리 예시 (Table 패널):

SELECT query, total_exec_time, mean_exec_time, calls
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

UI 성공 확인 포인트:

  • Grafana 대시보드에 Top 슬로우 쿼리 테이블 표시


측정 → 분석 → 개선 루프

  • Tempo: 느린 요청 Trace → DB 스팬 타이밍 확인
  • Grafana: 누적 상위 쿼리 확인
  • 개선 (인덱스/쿼리 힌트/재작성) → Before/After 지표 기록 (다음 편에 정리 예정)

마무리 (내가 배운 점)

  • Agent 방식은 도입/회수 속도가 빨라서 실험-측정-개선 루프에 최적.
  • UI 기반 검증 루틴(Tempo OK → TraceQL 결과 → 로그-트레이스 링크)은 로그만 뒤지는 것보다 훨씬 빠르고 직관적.
  • 슬로우 쿼리 분석은 Trace(맥락) + Top-N(누적) 두 관점이 모두 있어야 개선이 정확하다.
profile
운동처럼 개발도 작은 실천이 성장의 힘이 된다고 믿는 개발자 minpractice_jhj 기록

0개의 댓글