← Назад к вопросам
Что нужно сделать, чтобы появился traceId в логах?
2.3 Middle🔥 121 комментариев
#REST API и микросервисы#SOLID и паттерны проектирования
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Добавление traceId в логи (Distributed Tracing)
traceId — это уникальный идентификатор, который связывает все логи одного запроса, пока тот проходит через разные компоненты приложения и микросервисы. Это критично для debugging в production.
1. Использование Spring Cloud Sleuth (РЕКОМЕНДУЕТСЯ)
Это самый простой способ для Spring приложений.
Шаг 1: Добавляем зависимость
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>3.1.10</version>
</dependency>
Шаг 2: Настраиваем Logback
<!-- src/main/resources/logback-spring.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- %X{traceId} и %X{spanId} автоматически добавляются Sleuth -->
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId},%X{spanId}] %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
Результат логов
2024-03-22 10:15:23.456 [main] INFO com.example.UserService - [abc123def456,xyz789] User created: John
2024-03-22 10:15:24.123 [main] INFO com.example.EmailService - [abc123def456,xyz789] Email sent
2. Spring Cloud Sleuth + Zipkin (для микросервисов)
Для полного distributed tracing с визуализацией.
Шаг 1: Зависимости
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
Шаг 2: Конфигурация (application.yml)
# application.yml
spring:
application:
name: user-service
sleuth:
sampler:
probability: 1.0 # Собирать 100% трейсов (для dev)
zipkin:
base-url: http://localhost:9411 # URL Zipkin сервера
enabled: true
Шаг 3: Запуск Zipkin
# Docker
docker run -d -p 9411:9411 openzipkin/zipkin
# Или Docker Compose
version: '3.8'
services:
zipkin:
image: openzipkin/zipkin
ports:
- "9411:9411"
3. Ручная реализация с MDC (Mapped Diagnostic Context)
Для более контроля или если не используешь Spring Sleuth.
Шаг 1: Создаём фильтр для генерации traceId
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.UUID;
@Component
public class TraceIdFilter extends OncePerRequestFilter {
private static final String TRACE_ID = "traceId";
private static final String TRACE_ID_HEADER = "X-Trace-Id";
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain)
throws ServletException, IOException {
// 1. Проверяем, есть ли уже traceId в заголовке (из upstream сервиса)
String traceId = request.getHeader(TRACE_ID_HEADER);
if (traceId == null || traceId.isEmpty()) {
// Генерируем новый traceId
traceId = UUID.randomUUID().toString();
}
// 2. Добавляем traceId в MDC (доступен для всех логов)
MDC.put(TRACE_ID, traceId);
try {
// 3. Добавляем traceId в response header для downstream сервисов
response.addHeader(TRACE_ID_HEADER, traceId);
// 4. Продолжаем цепочку фильтров
filterChain.doFilter(request, response);
} finally {
// 5. Очищаем MDC в конце запроса
MDC.remove(TRACE_ID);
}
}
}
Шаг 2: Логирование с traceId
<!-- logback-spring.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- %X{traceId} извлекает значение из MDC -->
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId}] %msg%n
</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/application.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
</rollingPolicy>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - [%X{traceId}] %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
Шаг 3: Использование в коде
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
@Service
public class UserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public User createUser(CreateUserRequest request) {
// traceId автоматически добавляется к каждому логу
logger.info("Creating user: {}", request.getEmail());
User user = new User(request.getEmail(), request.getName());
User saved = userRepository.save(user);
logger.info("User created successfully with ID: {}", saved.getId());
return saved;
}
public void sendWelcomeEmail(User user) {
logger.info("Sending welcome email to: {}", user.getEmail());
// traceId проходит через все методы одного запроса
emailService.send(user.getEmail(), "Welcome!");
logger.info("Email sent successfully");
}
}
@Service
public class EmailService {
private static final Logger logger = LoggerFactory.getLogger(EmailService.class);
public void send(String email, String subject) {
// Тот же traceId будет в этом логе
logger.info("Sending email to: {} with subject: {}", email, subject);
// Отправка...
logger.debug("Email sent to SMTP");
}
}
4. Передача traceId между микросервисами
RestTemplate с traceId
import org.slf4j.MDC;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
private static final String TRACE_ID_HEADER = "X-Trace-Id";
private final RestTemplate restTemplate;
public OrderService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public void createOrder(Order order) {
logger.info("Creating order: {}", order.getId());
// Передаём traceId в downstream сервис
String traceId = MDC.get("traceId");
HttpHeaders headers = new HttpHeaders();
headers.set(TRACE_ID_HEADER, traceId);
HttpEntity<Order> request = new HttpEntity<>(order, headers);
// Вызов другого сервиса
restTemplate.postForObject(
"http://payment-service/api/v1/payments",
request,
PaymentResponse.class
);
logger.info("Order created successfully");
}
}
WebClient с traceId (для реактивных приложений)
import org.slf4j.MDC;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.util.context.Context;
@Service
public class ReactiveOrderService {
private final WebClient webClient;
public ReactiveOrderService(WebClient.Builder builder) {
this.webClient = builder.build();
}
public Mono<PaymentResponse> createOrder(Order order) {
String traceId = MDC.get("traceId");
return webClient.post()
.uri("http://payment-service/api/v1/payments")
.header("X-Trace-Id", traceId)
.bodyValue(order)
.retrieve()
.bodyToMono(PaymentResponse.class)
// Пропагируем traceId в реактивную цепочку
.contextWrite(Context.of("traceId", traceId));
}
}
5. Интеграция с логирующей системой (ELK Stack)
Конфигурация Logstash
{
"version": "1",
"fields": {
"traceId": "abc123def456",
"spanId": "xyz789",
"service": "user-service",
"level": "INFO",
"timestamp": "2024-03-22T10:15:23.456Z"
},
"@timestamp": "2024-03-22T10:15:23.456Z",
"message": "User created successfully with ID: 123"
}
Поиск в Kibana
# Все логи одного запроса
traceId: "abc123def456"
# Логи конкретного сервиса
service: "user-service" AND traceId: "abc123def456"
# Ошибки в трейсе
level: "ERROR" AND traceId: "abc123def456"
6. Асинхронные операции (Async Tasks)
ThreadPoolTaskExecutor с traceId
import org.slf4j.MDC;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@Service
public class AsyncService {
@Async
public void sendEmailAsync(String email) {
// ❌ Проблема: MDC не пропагируется в новый поток
String traceId = MDC.get("traceId");
if (traceId == null) {
traceId = "no-trace-id";
}
// ✅ Решение: явно установи traceId в новом потоке
MDC.put("traceId", traceId);
try {
// Отправка письма
emailService.send(email);
} finally {
MDC.remove("traceId");
}
}
}
// Или используй TaskDecorator
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
// Пропагируем MDC в новые потоки
executor.setTaskDecorator(runnable -> {
Map<String, String> context = MDC.getCopyOfContextMap();
return () -> {
if (context != null) {
MDC.setContextMap(context);
}
try {
runnable.run();
} finally {
MDC.clear();
}
};
});
executor.initialize();
return executor;
}
}
7. Сравнение подходов
| Подход | Сложность | Производительность | Когда использовать |
|---|---|---|---|
| Spring Cloud Sleuth | Низкая | Хорошая | Все Spring приложения |
| Sleuth + Zipkin | Средняя | Хорошая | Микросервисная архитектура |
| Ручная реализация | Средняя | Отличная | Максимальный контроль |
| MDC + Logback | Низкая | Отличная | Монолиты и простые приложения |
8. Best Practices
// ✅ Включай traceId в ВСЕ логи
logger.info("Operation result: {}", result);
// Логируется как: [abc123] Operation result: success
// ✅ Пропагируй traceId между сервисами
headers.set("X-Trace-Id", MDC.get("traceId"));
// ✅ Очищай MDC в finally блоке
try {
// логика
} finally {
MDC.remove("traceId");
}
// ✅ Для асинхронных операций используй TaskDecorator
// или явно пропагируй MDC
// ❌ Не логируй traceId явно (он добавится автоматически)
logger.info("Trace: {} - Message", traceId); // Лишнее
// ❌ Не забывай о чистке MDC
MDC.put("traceId", value);
// ... код ...
// MDC.remove("traceId"); // Должно быть в finally
Выводы
- Spring Cloud Sleuth — самый простой способ для Spring приложений
- MDC + logback pattern — стандартный инструмент для работы с traceId
- Передавай traceId между микросервисами через HTTP заголовки
- Для асинхронных операций явно пропагируй traceId в новые потоки
- Используй Zipkin для визуализации трейсов в микросервисах
- Интегрируй с ELK/Kibana для централизованного логирования
- Тестируй что traceId правильно появляется во всех логах одного запроса