← Назад к вопросам

Кто формирует traceId для трассировки запроса

2.0 Middle🔥 201 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Кто формирует traceId для трассировки запроса

traceId — это уникальный идентификатор, связывающий все логи, метрики и spans одного запроса через распределённую систему. Это критично для debugging в микросервисной архитектуре.

Общая архитектура

Client Request
     |
     v
  API Gateway (СОЗДАЁТ traceId, если его нет)
     |
     | traceId: "abc123" в header
     v
  Microservice 1 (читает traceId из header, передаёт дальше)
     |
     | вызов к Microservice 2
     v
  Microservice 2 (читает трас из header)
     |
     v
  Database (логирует с traceId)
     |
  Logging Service (собирает все логи с одинаковым traceId)

Кто должен создавать traceId

1. API Gateway (entry point системы)

API Gateway отвечает за:

  • Создание traceId если его нет в request
  • Проверку формата
  • Передачу во все микросервисы
// Spring Cloud Gateway + Spring Cloud Sleuth
@Component
public class TraceIdFilter extends AbstractGatewayFilterFactory<Config> {
    private final TraceIdGenerator traceIdGenerator;
    
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String traceId = exchange.getRequest().getHeaders()
                .getFirst("X-Trace-Id");
            
            // Если нет — создаём
            if (traceId == null) {
                traceId = UUID.randomUUID().toString();
            }
            
            // Пробрасываем во все микросервисы
            ServerWebExchange mutated = exchange.mutate()
                .request(exchange.getRequest().mutate()
                    .header("X-Trace-Id", traceId)
                    .header("X-Span-Id", UUID.randomUUID().toString())
                    .build())
                .build();
            
            return chain.filter(mutated);
        };
    }
}

Почему Gateway:

  • Один entry point — одно место для логики
  • Видит все запросы
  • Может добавить metadata (IP, User-Agent и т.д.)

2. Клиент (если есть)

Если клиент явно генерирует traceId — это тоже OK, но менее надёжно:

// Frontend
const traceId = crypto.randomUUID();
fetch('/api/orders', {
    headers: {
        'X-Trace-Id': traceId,
        'X-Request-Id': traceId
    }
});

Но лучше это делать на Gateway, потому что:

  • Клиент может не отправить (JS отключен, мобильное приложение)
  • Клиент может отправить невалидный ID
  • Gateway точно включит все запросы

Распространение traceId в микросервисах

Spring Cloud Sleuth (автоматически)

Eсли используешь Spring Cloud Sleuth — всё работает на автопилоте:

// Зависимость
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

// Автоматически создаёт traceId и добавляет в MDC
@RestController
public class OrderController {
    private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
    
    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest req) {
        // Sleuth уже добавил traceId в MDC
        logger.info("Creating order"); // логе будет [traceId=abc123]
        return orderService.create(req);
    }
}

// Логи видны как:
// 2024-03-22 10:15:30 [abc123] Creating order
// 2024-03-22 10:15:31 [abc123] Saving to database
// 2024-03-22 10:15:32 [abc123] Sending email

MDC (Mapped Diagnostic Context) — это механизм SLF4J для добавления context в каждый лог.

Ручное распространение (если нет Sleuth)

@RestController
public class OrderController {
    private final OrderService orderService;
    
    @PostMapping("/orders")
    public Order createOrder(
            @RequestBody OrderRequest req,
            @RequestHeader("X-Trace-Id") String traceId) {
        
        // Добавляем в MDC
        MDC.put("traceId", traceId);
        
        try {
            return orderService.create(req, traceId);
        } finally {
            MDC.remove("traceId");
        }
    }
}

@Service
public class OrderService {
    private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
    
    public Order create(OrderRequest req, String traceId) {
        logger.info("Creating order"); // будет [traceId=abc123]
        
        // Передаём traceId при вызове другого сервиса
        billingClient.createInvoice(req, traceId); // важно!
        return repository.save(new Order());
    }
}

// В HTTP клиенте
@Component
public class BillingClient {
    private final RestTemplate restTemplate;
    
    public void createInvoice(OrderRequest req, String traceId) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("X-Trace-Id", traceId); // передаём дальше
        
        HttpEntity<InvoiceRequest> entity = new HttpEntity<>(req, headers);
        restTemplate.postForObject("/billing/invoices", entity, void.class);
    }
}

Формат traceId

Стандартный (W3C Trace Context)

# W3C Trace Context (стандарт)
X-Trace-Id: 4bf92f3577b34da6a3ce929d0e0e4736

# Или с дополнительным context
X-Trace-Id: 4bf92f3577b34da6a3ce929d0e0e4736
X-Span-Id: 00f067aa0ba902b7
X-Parent-Span-Id: 00f067aa0ba902b8
X-Trace-Flags: 01

Микросервисы должны сохранять структуру

public class TraceContext {
    private String traceId;        // один для всей цепочки
    private String spanId;         // уникален для каждого шага
    private String parentSpanId;   // где он создан
    
    // Пример трассировки запроса
    // Gateway:     traceId=abc123, spanId=span1
    //   -> OrderService: traceId=abc123, spanId=span2, parentSpan=span1
    //     -> BillingService: traceId=abc123, spanId=span3, parentSpan=span2
    //       -> Database: traceId=abc123, spanId=span4, parentSpan=span3
}

Практический пример: Spring + Sleuth + Zipkin

// pom.xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

// application.yml
spring:
  application:
    name: order-service
  zipkin:
    base-url: http://zipkin:9411/  # адрес Zipkin сервера
    sender:
      type: web
  sleuth:
    sampler:
      probability: 1.0  # трассировать все запросы (в продакшене < 1.0)

// Контроллер
@RestController
public class OrderController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest req) {
        // Sleuth автоматически добавит в логи traceId
        logger.info("Order requested: {}", req.getId());
        
        return new Order();
    }
}

// Результат в Zipkin UI:
// Видны все traces с их duration
// Можно щёлкнуть на trace и увидеть timeline всех операций

Где используется traceId

МестоИспользование
Логикаждый лог содержит traceId
Метрикиприкреплены к trace (Prometheus)
APM (New Relic, DataDog)группируют по trace
Алерты"все запросы с traceId=abc имеют latency > 5s"
Debugging"найти все действия пользователя по его ID"

Правильный ответ на собеседовании

"traceId должен создавать API Gateway — это single entry point.

Процесс:

  1. Gateway проверяет заголовок X-Trace-Id
  2. Если его нет — создаёт (UUID)
  3. Пробрасывает во все микросервисы
  4. Каждый микросервис логирует с этим ID
  5. Логирует сервис (ELK, Splunk) может показать полный journey запроса

Инструменты:

  • Spring Cloud Sleuth (автоматически)
  • Spring Cloud Zipkin (визуализация)
  • W3C Trace Context (стандарт)

Ключ: traceId = одинаковый для всей цепочки, spanId = уникален для каждого шага."

Кто формирует traceId для трассировки запроса | PrepBro