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

Какие знаешь способы реализации логирования?

2.0 Middle🔥 261 комментариев
#Spring Boot и Spring Data#Основы Java

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

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

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

Способы реализации логирования в Java

Логирование — неотъемлемая часть каждого приложения. От выбора подходящего фреймворка и уровня логирования зависит эффективность отладки и мониторинга.

1. Основные уровни логирования

Стандартные уровни от наиболее до наименее серьёзных:

logger.error("Критическая ошибка");      // Ошибка, требующая внимания
logger.warn("Предупреждение");           // Потенциальная проблема
logger.info("Информационное сообщение"); // Важные события
logger.debug("Отладочная информация");   // Для разработки
logger.trace("Трассировка");             // Самая детальная информация

2. SLF4J с Logback (рекомендуемый подход)

SLF4J — фасад, который позволяет менять реализацию без изменения кода:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
public class UserService {
    private static final Logger logger = LoggerFactory.getLogger(UserService.class);
    
    public User createUser(CreateUserRequest request) {
        logger.info("Creating user: {}", request.getName());
        
        try {
            User user = repository.save(mapToUser(request));
            logger.info("User created successfully with id: {}", user.getId());
            return user;
        } catch (Exception e) {
            logger.error("Failed to create user", e);
            throw e;
        }
    }
}

logback.xml конфигурация

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Роль консоли -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <!-- Роль файла -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/application.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/application-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>30</maxHistory>
            <totalSizeCap>10GB</totalSizeCap>
        </rollingPolicy>
    </appender>
    
    <!-- Уровни логирования по пакетам -->
    <logger name="com.myapp" level="DEBUG" />
    <logger name="org.springframework" level="INFO" />
    <logger name="org.hibernate" level="WARN" />
    
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

3. Параметризованные сообщения

Никогда не используй конкатенацию строк:

// ❌ Плохо: конкатенация происходит всегда
logger.debug("User " + user.getName() + " logged in");

// ✅ Хорошо: параметризация (конкатенация только если DEBUG включен)
logger.debug("User {} logged in", user.getName());

// ✅ Множественные параметры
logger.info("User {} with email {} created at {}", 
    user.getId(), user.getEmail(), user.getCreatedAt());

// ✅ С исключением
logger.error("Failed to process order {}", orderId, exception);

4. Structured Logging (логирование структурированных данных)

Использование MDC (Mapped Diagnostic Context)

import org.slf4j.MDC;

@Component
public class RequestLoggingFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) throws Exception {
        String requestId = UUID.randomUUID().toString();
        MDC.put("requestId", requestId);
        MDC.put("userId", getUserId(request));
        
        try {
            filterChain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

// В конфиге logback
<pattern>%d [%X{requestId}] [%X{userId}] %-5level %logger - %msg%n</pattern>

// Результат лога
// 2024-03-22 10:15:30 [a1b2c3d4] [user123] INFO UserService - User created

5. JSON логирование (для машинной обработки)

Logstash Logback Encoder

<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>7.2</version>
</dependency>
<appender name="JSON_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logs/app.json</file>
    <encoder class="net.logstash.logback.encoder.LogstashEncoder">
        <customFields>{"service":"user-service","env":"prod"}</customFields>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>logs/app-%d{yyyy-MM-dd}.%i.json</fileNamePattern>
        <maxFileSize>100MB</maxFileSize>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
</appender>

Результат

{
  "@timestamp": "2024-03-22T10:15:30.123Z",
  "message": "User created successfully",
  "level": "INFO",
  "logger_name": "com.myapp.UserService",
  "thread_name": "http-nio-8080-exec-1",
  "requestId": "a1b2c3d4",
  "userId": "user123",
  "service": "user-service",
  "env": "prod"
}

6. Log Aggregation (ELK Stack)

Отправка логов в Elasticsearch

<appender name="ELASTIC" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logs/elasticsearch.log</file>
    <encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>

<!-- Filebeat подхватит файл и отправит в Elasticsearch -->

Kibana запрос

kibana_sample_data_logs | stats count() by level
kibana_sample_data_logs | where requestId == "a1b2c3d4" | sort timestamp desc

7. Асинхронное логирование

Для высоконагруженных систем:

<configuration>
    <!-- Асинхронный appender -->
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
        <queueSize>512</queueSize>
        <discardingThreshold>0</discardingThreshold>
        <appender-ref ref="FILE" />
    </appender>
    
    <root level="INFO">
        <appender-ref ref="ASYNC" />
    </root>
</configuration>

8. Логирование с Spring Boot

application.properties

logging.level.root=INFO
logging.level.com.myapp=DEBUG
logging.level.org.springframework.web=WARN
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n
logging.file.name=logs/application.log
logging.file.max-size=100MB
logging.file.max-history=30

9. Логирование исключений

@Service
public class PaymentService {
    private static final Logger logger = LoggerFactory.getLogger(PaymentService.class);
    
    public Payment processPayment(PaymentRequest request) {
        try {
            return externalPaymentApi.charge(request);
        } catch (NetworkTimeoutException e) {
            logger.warn("Payment timeout for order {}, will retry", request.getOrderId(), e);
            return retry(request);
        } catch (PaymentDeclinedException e) {
            logger.error("Payment declined for user {}, order {}, amount {}", 
                request.getUserId(), request.getOrderId(), request.getAmount(), e);
            throw new PaymentFailedException(e);
        } catch (Exception e) {
            logger.error("Unexpected error processing payment", e);
            throw e;
        }
    }
}

10. Performance Logging

@Component
public class PerformanceLoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(PerformanceLoggingAspect.class);
    
    @Around("@annotation(com.myapp.annotations.Timed)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - start;
            
            logger.info("Method {} executed in {} ms", 
                joinPoint.getSignature().getName(), duration);
            
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - start;
            logger.error("Method {} failed after {} ms", 
                joinPoint.getSignature().getName(), duration, e);
            throw e;
        }
    }
}

11. Логирование при Debug mode

public class DebugLogger {
    private static final Logger logger = LoggerFactory.getLogger(DebugLogger.class);
    
    public static void debugObject(Object obj) {
        if (logger.isDebugEnabled()) {
            ObjectMapper mapper = new ObjectMapper();
            try {
                String json = mapper.writerWithDefaultPrettyPrinter()
                    .writeValueAsString(obj);
                logger.debug("Object: {}", json);
            } catch (JsonProcessingException e) {
                logger.debug("Failed to serialize object", e);
            }
        }
    }
}

12. Кастомные аннотации для логирования

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogMe {
    String value() default "";
    boolean logArgs() default true;
    boolean logResult() default true;
}

@Component
public class LoggingAspect {
    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
    @Around("@annotation(com.myapp.annotations.LogMe)")
    public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        LogMe annotation = getAnnotation(joinPoint, LogMe.class);
        String message = annotation.value();
        
        if (annotation.logArgs()) {
            Object[] args = joinPoint.getArgs();
            logger.info("{} called with args: {}", message, Arrays.toString(args));
        }
        
        Object result = joinPoint.proceed();
        
        if (annotation.logResult()) {
            logger.info("{} returned: {}", message, result);
        }
        
        return result;
    }
}

// Использование
@Service
public class UserService {
    @LogMe("Creating new user")
    public User createUser(CreateUserRequest request) {
        // ...
    }
}

Лучшие практики логирования

  1. Используй SLF4J — универсальный фасад
  2. Параметризованные сообщения — избегай конкатенации
  3. Правильные уровни — ERROR для ошибок, DEBUG для отладки
  4. Структурированное логирование — используй JSON для машинной обработки
  5. MDC для контекста — requestId, userId, sessionId
  6. Асинхронное логирование — не замораживай основной поток
  7. Ротация файлов — не позволяй логам занимать всё место
  8. Агрегация логов — используй ELK или подобные системы
  9. Мониторинг и алерты — настрой оповещения о критичных ошибках
  10. Performance logging — отслеживай медленные операции