0

[Java Backend Zero to Hello] BÀI 3.6: LOGGING

Java Backend Zero to Hello

📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 3: Công Cụ & Build Tools | Bài 34/86


BÀI 3.6: LOGGING

Mục tiêu

  • Hiểu tầm quan trọng của logging
  • Sử dụng SLF4J với Logback
  • Cấu hình logging
  • Best practices

1. TẠI SAO CẦN LOGGING?

  • Debug ứng dụng
  • Theo dõi lỗi trong production
  • Phân tích hành vi người dùng
  • Audit, compliance

So sánh System.out vs Logger

System.out Logger
Không có level Có level (DEBUG, INFO, WARN, ERROR)
Không tắt được Có thể tắt theo level
Không ghi file Ghi file, database, console
Không có timestamp Có timestamp, thread, class
Không format Format được

2. SLF4J

SLF4J (Simple Logging Facade for Java) là facade cho nhiều logging framework.

2.1 Dependency

<!-- SLF4J API -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>2.0.9</version>
</dependency>

<!-- Logback (implementation) -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.14</version>
</dependency>

2.2 Sử dụng

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

public class UserService {
    private static final Logger log = LoggerFactory.getLogger(UserService.class);

    public User findById(Long id) {
        log.debug("Finding user by id: {}", id);
        log.info("User lookup performed");
        log.warn("User not found, returning default");
        log.error("Database error", new RuntimeException("Connection failed"));

        // ...
    }
}

3. LOG LEVEL

Level Mô tả Khi nào dùng
TRACE Chi tiết nhất Debug sâu
DEBUG Thông tin debug Phát triển
INFO Thông tin chung Production
WARN Cảnh báo Có vấn đề nhưng chưa lỗi
ERROR Lỗi Cần xử lý
FATAL Lỗi nghiêm trọng Crashed

Thứ tự

TRACE < DEBUG < INFO < WARN < ERROR < FATAL

4. LOGBACK CONFIGURATION

4.1 File logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Console appender -->
    <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>

    <!-- File appender -->
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>logs/app.log</file>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Rolling file appender -->
    <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Logger level -->
    <logger name="com.example" level="DEBUG"/>
    <logger name="org.springframework" level="INFO"/>

    <!-- Root logger -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="ROLLING"/>
    </root>
</configuration>

4.2 Pattern phổ biến

Pattern Mô tả
%d Timestamp
%thread Thread name
%-5level Log level (căn trái 5 ký tự)
%logger{36} Tên class (rút gọn 36 ký tự)
%msg Message
%n New line
%X{userId} MDC value
%exception Stack trace

5. MDC (Mapped Diagnostic Context)

Thêm context cho log (ví dụ: request ID, user ID).

import org.slf4j.MDC;

MDC.put("requestId", "12345");
MDC.put("userId", "user-001");

log.info("Processing request");  // Sẽ có requestId, userId

MDC.clear();

Pattern: %X{requestId}


6. LOG TRONG SPRING BOOT

Spring Boot mặc định dùng Logback.

6.1 application.yml

logging:
  level:
    root: INFO
    com.example: DEBUG
    org.springframework: INFO
  file:
    name: logs/app.log
  pattern:
    console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

6.2 Lombok @Slf4j

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class UserService {
    public void createUser(User user) {
        log.info("Creating user: {}", user.getName());
    }
}

7. BEST PRACTICES

7.1 ✅ NÊN

// Dùng placeholder thay string concatenation
log.debug("User {} created with id {}", name, id);

// Log exception với stack trace
log.error("Failed to process", exception);

// Log có ý nghĩa
log.info("Order {} placed successfully", orderId);

7.2 ❌ KHÔNG NÊN

// String concatenation (chậm)
log.debug("User " + name + " created");  // ❌

// Log thông tin nhạy cảm
log.info("Password: " + password);  // ❌

// Log quá nhiều trong vòng lặp
for (int i = 0; i < 1000000; i++) {
    log.info("Processing " + i);  // ❌
}

7.3 Performance

// Check level trước khi tính toán phức tạp
if (log.isDebugEnabled()) {
    log.debug("Complex data: {}", expensiveOperation());
}

8. LOG4J2 (LỰA CHỌN KHÁC)

Dependency

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j2-impl</artifactId>
    <version>2.22.0</version>
</dependency>

Sử dụng (giống SLF4J)

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

private static final Logger log = LoggerFactory.getLogger(MyClass.class);

9. BÀI TẬP THỰC HÀNH

Bài 1: Cấu hình Logback

Tạo logback.xml với:

  • Console + File appender
  • Rolling theo ngày
  • Level DEBUG cho package của bạn

Bài 2: Logging trong Service

@Service
@Slf4j
public class OrderService {
    public Order createOrder(OrderRequest request) {
        log.info("Creating order for customer: {}", request.getCustomerId());
        try {
            Order order = processOrder(request);
            log.info("Order {} created successfully", order.getId());
            return order;
        } catch (Exception e) {
            log.error("Failed to create order for customer: {}",
                request.getCustomerId(), e);
            throw e;
        }
    }
}

Bài 3: MDC cho request tracking

@Component
public class RequestLoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        String requestId = UUID.randomUUID().toString();
        MDC.put("requestId", requestId);
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.clear();
        }
    }
}

10. TÓM TẮT

Khái niệm Mô tả
SLF4J Facade cho logging
Logback Implementation phổ biến
Log level TRACE, DEBUG, INFO, WARN, ERROR
Appender Nơi ghi log (console, file)
Rolling Tự động tạo file mới
MDC Context cho log
Placeholder {} thay vì concatenation

Hoàn thành Phase 3! Tiếp tục với Phase 4: Database & SQL


🧭 Điều Hướng Series

⬅️ Bài trước: BÀI 3.5: GRADLE (TÙY CHỌN)

📋 Lộ trình tổng quan: Xem Toàn Bộ Series

➡️ Bài tiếp theo: PHASE 4: DATABASE & SQL - Tổng Quan & Mục Tiêu


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí