Hệ thống ghi log trong Spring Boot và cách cấu hình

1. Giới thiệu về log trong ứng dụng

Trong quá trình phát triển phần mềm, việc ghi log đóng vai trò then chốt. Nếu không có log, khi ứng dụng gặp lỗi, chúng ta sẽ rất khó xác định nguyên nhân và vị trí lỗi để khắc phục. Nhờ có log, chúng ta có thể phát hiện lỗi, định vị chính xác vị trí và xử lý kịp thời.

Trong giai đoạn học lập trình cơ bản, chúng ta thường dùng System.out.println để in thông tin ra console khi thỏa mãn điều kiện nào đó.

System.out.println("Phương thức A đã được gọi");

Trong Spring Boot, chúng ta có hệ thống log nâng cao hơn. Khi chạy dự án, console sẽ hiển thị các log mặc định bao gồm: thời gian thực thi, mức độ log, tên thread, và nhiều thông tin khác.

Nhờ vậy, khi ứng dụng gặp sự cố, bạn có thể thông qua log để xác định chính xác lỗi và sửa chữa. Ngoài việc phát hiện và định vị lỗi, log còn giúp khôi phục dữ liệu khi cần thiết, ví dụ như bổ sung thủ công tài khoản người dùng khi hệ thống đăng ký gặp sự cố.

2. Cách sử dụng log

2.1 Các mức độ log

  • TRACE: Mức độ thấp nhất, xuất thông tin debug chi tiết nhất. Thường dùng trong giai đoạn phát triển, nên tắt ở môi trường production để tránh dư thừa log.
  • DEBUG: Xuất thông tin debug của chương trình, thường dùng trong quá trình phát triển. Nên tắt ở môi trường production.
  • INFO: Xuất thông tin quan trọng khi chương trình hoạt động bình thường, như log khởi động, log chạy. Thường bật ở môi trường production.
  • WARN: Xuất cảnh báo, cho biết chương trình có thể gặp vấn đề. Ghi lại các ngoại lệ không nghiêm trọng để phát hiện và xử lý kịp thời.
  • ERROR: Xuất thông tin lỗi khi chương trình chạy, thường biểu thị lỗi không dự đoán. Ghi lại các ngoại lệ nghiêm trọng để phát hiện và xử lý.
  • FATAL: Lỗi nghiêm trọng, thường khiến ứng dụng crash hoặc không thể tiếp tục chạy. Mức độ cao hơn ERROR.

2.2 Sử dụng log trong code

Để sử dụng log, ta dùng LoggerFactory.getLogger() để lấy đối tượng Logger từ package org.slf4j.

@RestController
public class AppLogController {

    private static final Logger logger = LoggerFactory.getLogger(AppLogController.class);

    @GetMapping("/test-log")
    public void printLog() {
        String content = "log test";
        logger.trace("TRACE: " + content);
        logger.debug("DEBUG: " + content);
        logger.info("INFO: " + content);
        logger.warn("WARN: " + content);
        logger.error("ERROR: " + content);
    }
}

Khi truy cập localhost:8080/test-log, trình duyệt không hiển thị gì nhưng console sẽ xuất log tương ứng. Quan sát thấy chỉ có INFO, WARN, ERROR được in ra, vì Spring Boot mặc định chỉ in log có mức độ bằng hoặc cao hơn INFO. TRACEDEBUG sẽ không hiển thị.

2.3 Tùy chỉnh mức độ log

Nếu giữ mức mặc định INFO, mỗi ngày sẽ có rất nhiều log được ghi lại. Ta có thể dùng logging.level để thay đổi. Nếu muốn thay đổi cho toàn bộ dự án, dùng logging.level.root.

Ví dụ trong application.properties:

logging.level.root=warn

Khi đó, chỉ log mức WARNERROR mới được in ra.

Ngoài ra, ta có thể thiết lập riêng cho từng package. Ví dụ đặt mức DEBUG cho package com.example.demo:

logging.level.com.example.demo=debug

Vì sao Spring Boot có thể in log và thiết lập mức log?

Spring Boot tích hợp sẵn hai framework: SLF4JLogback. Trong đó, Logback là framework triển khai log ở tầng thấp, còn SLF4J là API mà lập trình viên sử dụng trực tiếp. Có thể ví dụ Logback như nhân viên phục vụ, còn SLF4J như ông chủ — khi dịch vụ không đạt yêu cầu, ta có thể yêu cầu đổi nhân viên khác thông qua ông chủ.

3. Lưu trữ log持久化

3.1 Ghi log vào ổ đĩa

Các log ở trên chỉ in ra console, khi đóng IDE thì log sẽ mất. Để lưu trữ log lâu dài, ta có thể ghi vào ổ đĩa bằng cấu hình:

logging.file.path=D:/logs

3.2 Đặt tên file log

Ta cũng có thể chỉ định tên file log cụ thể:

logging.file.name=D:/logs/app-logger.log

4. Sử dụng Lombok để đơn giản hóa log

4.1 Cài đặt Lombok trong IntelliJ IDEA

Vào File → Settings → Plugins, tìm kiếm Lombok và cài đặt.

Sau đó thêm dependency vào pom.xml:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

4.2 Cách sử dụng

Thêm annotation @Slf4j lên lớp, ta có thể sử dụng biến log trực tiếp mà không cần khởi tạo Logger thủ công:

@RestController
@Slf4j
public class AppLogController {

    @GetMapping("/test-log")
    public void printLog() {
        String content = "log test";
        log.trace("TRACE: " + content);
        log.debug("DEBUG: " + content);
        log.info("INFO: " + content);
        log.warn("WARN: " + content);
        log.error("ERROR: " + content);
    }
}

4.3 Nguyên lý hoạt động của Lombok

Lombok hoạt động dựa trên annotation processor và xử lý ở giai đoạn biên dịch. Nó thao tác với mã nguồn để tự động sinh ra các đoạn code cần thiết, giúp giảm thiểu code boilerplate.

Quy trình xử lý của Lombok:

  1. Khởi tạo annotation processor: Lombok khởi tạo bộ xử lý annotation trong quá trình biên dịch.
  2. Phân tích cú pháp: Compiler phân tích từ vựng và cú pháp, xây dựng Abstract Syntax Tree (AST) và điền bảng ký hiệu.
  3. Xử lý annotation: Lombok dựa trên thông tin annotation để thao tác với AST, thêm hoặc sửa đổi các node.
  4. Sinh bytecode: Kiểm tra tĩnh, phân tích luồng dữ liệu và luồng điều khiển, cuối cùng sinh ra bytecode.

Lombok cung cấp các annotation như @Data, @Builder, @Getter, @Setter — tất cả đều được xử lý lúc biên dịch để sinh code tương ứng. Ví dụ @Data sẽ tự động tạo getter, setter, equals, hashCode, và toString.

Khi thêm @Slf4j, trong thư mục target sẽ tự động sinh dòng code khởi tạo LoggerFactory. Bạn cũng có thể thử các annotation khác như @Getter, @Setter, @Data.

Thẻ: Spring Boot SLF4j Logback Lombok logging

Đăng vào ngày 23 tháng 9 lúc 09:58