Django에서 OpenTelemetry를 이용해 Jaeger로 Trace 남기기

이전 포스팅에서 Log생성을 해보았다.
이번에는 Tracing을 해본다.

Trace

Trace는 하나의 작업(요청)이 여러 시스템이나 컴포넌트를 거쳐 처리되는 과정을 기록하고 시각화한 것이다.

  • 시스템 어디서 병목이 생기는지,
  • 어떤 부분에서 오류가 나는지,
  • 요청이 어디서 얼마나 오래 걸렸는지 확인할 수 있다.

일반적인 로그와는 다르다.

  • 로그는 → logging 시스템 (text, line-by-line)
  • 트레이스(Trace)는 → 서비스 호출 흐름을 시각화해서 보여줌 (A가 B를 부르고, B가 C를 부르는 관계)
  • 시스템이 복잡해질수록 (API → DB → 외부서비스) 흐름을 추적하기가 어려워진다.
  • Trace는 요청 흐름을 시간순서대로, 계층적으로 시각화 해준다.

Jaeger와 OpenTelemetry

Jaeger

오픈소스 분산 트레이싱 시스템 (시각화 도구)

OpenTelemetry

코드에 트레이스를 심고 다양한 시스템으로 전송하는 표준 프레임워크

Django에 Trace를 심는 방법

  • Install OpenTelemetry SDK on Django
  • Create SPAN besween Request and Response
  • Then, send the trace to Jaeger.

Install OpenTelemetry SDK on Django

# 1. OpenTelemetry 기본 SDK
# pip install opentelemetry-sdk

# 2. Django용 자동 Instrumentation
pip install opentelemetry-instrumentation-django

# 3. Jaeger로 보내는 OTLP Exporter
pip install opentelemetry-exporter-otlp

{DjangoProject}/wsgi.py 수정

서버 시작 시 트레이스 기능을 사용할수 있도록 구성합니다.

테스트 중인 Django 애플리케이션의 배포환경은 Kubernetes에서 배포되고 있습니다.
따라서, endpoint는 쿠버네티스 service와 namspace를 지정하면됩니다.
endpoint: http://jaeger-collector.istio-system:4317

Django Project에 있는 wsgi.py에 설정합니다.

import os

# ✅ Django 설정을 미리 불러온다
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'butler_ddochi.settings')

from django.conf import settings  # <<<< 이거 추가

# ✅ DEBUG 모드 아닐 때만 OpenTelemetry 초기화
if not settings.DEBUG:
    from opentelemetry import trace
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
    from opentelemetry.instrumentation.django import DjangoInstrumentor

    trace.set_tracer_provider(
        TracerProvider(
            resource=Resource.create({
                "service.name": "butler_ddochi",
            })
        )
    )

    otlp_exporter = OTLPSpanExporter(
        endpoint="http://jaeger-collector.istio-system:4317",
        insecure=True,
    )

    trace.get_tracer_provider().add_span_processor(
        BatchSpanProcessor(otlp_exporter)
    )

    DjangoInstrumentor().instrument()

from django.core.wsgi import get_wsgi_application

application = get_wsgi_application()

테스트 조회

아직 테스트가 끝난 것이 아닙니다. 이벤트로그가 없고 기본적인 span만 나타나는 것을 확인합니다.

  • Trace는 span이 모여서 구성됩니다.
  • span은 “어떤 작업(동작, 프로세스)의 시작부터 끝까지를 하나로 묶어 추적하는 단위”를 말합니다.
    • name, start time, end time, attributes, events, optional.. 등 정보가 들어 있습니다.

조회를 해보면 이벤트 로그가 없다. 직접 생성해야 한다.

View 안에서 Custom Span 만들기

예제 샘플

......
......

import logging
from opentelemetry import trace

logger = logging.getLogger(__name__)
tracer = trace.get_tracer(__name__)  # 트레이서 가져오기

......
......

def add_ip_record(request):
    if request.method == "POST":
        network_nm = request.POST.get("network_nm")
        ip_addrs = request.POST.get("ip_addrs")
        svr_nm = request.POST.get("svr_nm")
        contents = request.POST.get("contents")
        remark = request.POST.get("remark")
        author = request.user

        # 2025-04-14 트레이스 span 생성
        with tracer.start_as_current_span("create_ip_record") as span:
            # 데이터 저장
            IPManagementRecord.objects.create(
                network_nm=network_nm,
                ip_addrs=ip_addrs,
                svr_nm=svr_nm,
                contents=contents,
                remark=remark,
                author=author,
            )

            # 2025-04-14 Jaeger용 Event 추가
            span.add_event(
                "Saved IPManagementRecord",
                attributes={
                    "ip_addrs": ip_addrs,
                    "svr_nm": svr_nm,
                    "desc": "jager logs test",
                }
            )

        # 2025-04-14 로그 등록 (콘솔+FluentBit용)
        logger.info(f"Create_Record_IP_ADDRS: {ip_addrs}")

        return redirect("/ip_mgmt")

......
......

span이 추가되었고 event도 발생한다.