Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Trace ID: формирование и использование
Trace ID — это уникальный идентификатор для отслеживания запроса через всю распределённую систему. Это критичный компонент наблюдаемости (observability).
Что такое Trace ID
Trace ID — это уникальный идентификатор, который:
- Создается при входе запроса в систему
- Передается через все микросервисы и компоненты
- Позволяет собрать полный путь запроса через систему
- Помогает дебагать проблемы в распределённых системах
Архитектура распределённого трейсирования
Client Request
|
v
[API Gateway] ← создает Trace ID
|
v
[Auth Service] ← получает Trace ID, создает Span
|
v
[Order Service] ← получает Trace ID, создает Span
|
v
[Payment Service] ← получает Trace ID, создает Span
|
v
Trace ID собирается в Jaeger/Zipkin/Datadog
Где формируется Trace ID
Trace ID формируется в одном из этих мест:
-
API Gateway (самое частое место)
- Входная точка всех запросов
- Генерирует уникальный ID для нового запроса
- Передает его дальше
-
Client side (если есть клиент)
- Иногда клиент генерирует ID
- Отправляет в header запроса
-
Service side (если нет входной точки)
- Сервис генерирует, если не получил
- Это fallback вариант
Как генерируется Trace ID
Обычно это UUID4 или аналог:
import uuid
import secrets
import base64
# Способ 1: UUID4
trace_id = str(uuid.uuid4())
# Результат: "550e8400-e29b-41d4-a716-446655440000"
# Способ 2: Random hex (более компактный)
trace_id = secrets.token_hex(16)
# Результат: "a3f2b8c9d0e1f2a3b4c5d6e7f8a9b0c1"
# Способ 3: Jaeger формат (128 bit = 32 hex символа)
trace_id = "{:032x}".format(secrets.randbits(128))
# Результат: "a3f2b8c9d0e1f2a3b4c5d6e7f8a9b0c1"
Реализация в FastAPI
from fastapi import FastAPI, Request, Header
from typing import Optional
import uuid
import logging
app = FastAPI()
logger = logging.getLogger(__name__)
# Middleware для создания/передачи Trace ID
@app.middleware("http")
async def trace_id_middleware(request: Request, call_next):
# Проверяем, есть ли уже Trace ID (от upstream сервиса)
trace_id = request.headers.get(
"X-Trace-ID",
str(uuid.uuid4())
)
# Логируем с Trace ID
request.state.trace_id = trace_id
logger.info(
f"Request started",
extra={"trace_id": trace_id}
)
# Обработка запроса
response = await call_next(request)
# Возвращаем Trace ID в ответе
response.headers["X-Trace-ID"] = trace_id
return response
@app.get("/api/order")
async def get_order(request: Request):
trace_id = request.state.trace_id
logger.info(
f"Processing order",
extra={"trace_id": trace_id}
)
return {"order_id": 123, "trace_id": trace_id}
Передача Trace ID между сервисами
import httpx
async def call_downstream_service(
trace_id: str,
url: str
) -> dict:
"""Вызов другого сервиса с传递 Trace ID"""
headers = {
"X-Trace-ID": trace_id, # Передаем Trace ID
"X-Span-ID": str(uuid.uuid4()) # Создаем Span ID для этого вызова
}
async with httpx.AsyncClient() as client:
response = await client.get(
url,
headers=headers
)
return response.json()
# Использование
trace_id = request.state.trace_id
result = await call_downstream_service(
trace_id=trace_id,
url="http://payment-service/api/pay"
)
Span ID vs Trace ID
Это разные концепции:
Trace ID: "550e8400-e29b-41d4-a716-446655440000"
|
+-- Span ID: "a3f2b8c9" (запрос к API Gateway)
+-- Span ID: "c5d6e7f8" (запрос к Auth Service)
+-- Span ID: "d0e1f2a3" (запрос к Order Service)
+-- Span ID: "b4c5d6e7" (вложенный вызов БД)
Стандарты и спецификации
W3C Trace Context (современный стандарт):
# Header format
headers = {
"traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
# version trace-id parent-id flags
}
# Парсинг
from w3c_trace_context import TraceContext
tracecontext = TraceContext.from_header(
"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
)
print(tracecontext.trace_id) # 4bf92f3577b34da6a3ce929d0e0e4736
print(tracecontext.parent_id) # 00f067aa0ba902b7
Инструменты для трейсирования
Jaeger — популярный choice для микросервисов:
from jaeger_client import Config
from opentelemetry import trace
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
# Конфигурация Jaeger
jaeger_exporter = JaegerExporter(
agent_host_name="localhost",
agent_port=6831,
)
jaeger_provider = TracerProvider()
jaeger_provider.add_span_processor(
BatchSpanProcessor(jaeger_exporter)
)
trace.set_tracer_provider(jaeger_provider)
# Получение tracer
tracer = trace.get_tracer(__name__)
# Создание span
with tracer.start_as_current_span("database_query") as span:
span.set_attribute("trace_id", trace_id)
result = db.query()
Logging с Trace ID
Импортант связать логи с Trace ID:
import logging
import json
class TraceIDFormatter(logging.Formatter):
def format(self, record):
# Добавляем Trace ID в каждое сообщение лога
trace_id = getattr(record, "trace_id", "unknown")
record.trace_id = trace_id
return super().format(record)
# Конфигурация логирования
logger = logging.getLogger(__name__)
handler = logging.StreamHandler()
formatter = TraceIDFormatter(
'[%(trace_id)s] %(levelname)s: %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
# Использование
logger.info(
"Order processed",
extra={"trace_id": "550e8400-e29b-41d4-a716-446655440000"}
)
Практический пример: полный flow
from fastapi import FastAPI, Request
from contextlib import asynccontextmanager
import uuid
import httpx
app = FastAPI()
# Context variable для хранения Trace ID
from contextvars import ContextVar
trace_id_var: ContextVar[str] = ContextVar('trace_id')
@app.middleware("http")
async def add_trace_id(request: Request, call_next):
# Создаем или получаем Trace ID
trace_id = request.headers.get(
"X-Trace-ID",
str(uuid.uuid4())
)
trace_id_var.set(trace_id)
# Обработка
response = await call_next(request)
response.headers["X-Trace-ID"] = trace_id
return response
@app.get("/api/order/{order_id}")
async def get_order(order_id: int):
trace_id = trace_id_var.get()
# Вызов Payment Service
async with httpx.AsyncClient() as client:
resp = await client.get(
f"http://payment-service/check/{order_id}",
headers={"X-Trace-ID": trace_id}
)
return {
"order_id": order_id,
"trace_id": trace_id,
"payment_status": resp.json()["status"]
}
Best Practices
- Всегда передавай Trace ID — в headers всех http запросов
- Создавай в одном месте — обычно в API Gateway
- Логируй с Trace ID — связи все логи воедино
- Используй стандарты — W3C Trace Context
- Отправляй в хранилище — Jaeger, Datadog, Elasticsearch
- Анализируй patterns — найди проблемы в распределённых запросах