CodeGym /Các khóa học /JAVA 25 SELF /Những điều cơ bản về ghi log: java.util.logging, Log4j, S...

Những điều cơ bản về ghi log: java.util.logging, Log4j, SLF4J

JAVA 25 SELF
Mức độ , Bài học
Có sẵn

1. Vì sao cần ghi log

Ghi log ≠ in ra console

Khi bạn mới bắt đầu lập trình, có vẻ như để tìm vấn đề chỉ cần chèn khắp nơi System.out.println("Tôi đã ở đây!"). Điều đó hoạt động khi chương trình nhỏ và chạy trên máy của bạn. Nhưng hãy tưởng tượng một dự án lớn, một máy chủ chạy 24/7, hoặc một ứng dụng mà hàng nghìn người đang dùng — bạn đâu thể ngồi sau lưng từng người dùng để nhìn vào console của họ?

Ghi log không chỉ là in thông điệp. Đó là việc ghi chép có hệ thống thông tin về hoạt động của ứng dụng: lỗi, cảnh báo, sự kiện nghiệp vụ, chi tiết kỹ thuật. Log được lưu vào tệp, cơ sở dữ liệu, có thể gửi qua mạng — và cho phép phân tích hoạt động của chương trình sau khi chạy hoặc ngay trong quá trình chạy.

Log để làm gì

  • Chẩn đoán sự cố: nếu có gì trục trặc, log là trợ thủ đắc lực. Dựa vào đó có thể hiểu lỗi phát sinh ở đâu và vì sao.
  • Audit và bảo mật: log ghi lại các hành động quan trọng của người dùng, giúp điều tra sự cố.
  • Gỡ lỗi: đôi khi bug chỉ xuất hiện trong những điều kiện nhất định, và không có log thì khó bắt được.
  • Giám sát: qua log có thể theo dõi trạng thái ứng dụng, hiệu năng và “sức khỏe” của nó.

Ví dụ thực tế

Nếu máy bay không có “hộp đen” (nhật ký chuyến bay), việc điều tra nguyên nhân tai nạn sẽ gần như bất khả thi. Trong lập trình, log chính là “hộp đen” của bạn.

2. Các mức ghi log cơ bản

Trong ghi log, người ta sử dụng các mức (levels) cho thông điệp. Nó giống đèn giao thông: đỏ — nguy hiểm, vàng — thận trọng, xanh — mọi thứ ổn.

Các mức chính (từ “to” nhất đến “nhỏ” nhất):

Mức Mô tả
ERROR
Lỗi nghiêm trọng, ứng dụng không thể tiếp tục chạy
WARN
Cảnh báo: có gì đó không ổn, nhưng chương trình vẫn chạy
INFO
Thông điệp thông tin về các sự kiện bình thường
DEBUG
Thông tin chi tiết phục vụ gỡ lỗi (chỉ hiển thị cho nhà phát triển)
TRACE
Thông tin chi tiết nhất — phục vụ chẩn đoán chuyên sâu

Ví dụ:

  • Người dùng không đăng nhập được do mật khẩu sai — đây là WARN.
  • Cơ sở dữ liệu hỏng — đây là ERROR.
  • Ứng dụng khởi động — đây là INFO.
  • In giá trị biến trong vòng lặp — đây là DEBUG hoặc TRACE.

Cách chọn mức

Đừng ghi mọi thứ ở mức ERROR — nếu không bạn sẽ chìm trong các lỗi giả. Hãy dùng các mức một cách có chủ ý: chỉ những sự cố thực sự nghiêm trọng mới nên là lỗi.

3. Ghi log tiêu chuẩn trong Java (java.util.logging)

Java đi kèm với hệ thống ghi log tích hợp — gói java.util.logging (viết tắt JUL). Đây là công cụ “gốc” luôn có sẵn, ngay cả khi bạn không thêm bất kỳ thư viện nào.

Các lớp chính:

  • Logger — lớp chính để ghi log.
  • Level — enum các mức ghi log (SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST).
  • Handler — bộ xử lý quy định ghi log đi đâu (tệp, console, v.v.).
  • Formatter — chịu trách nhiệm định dạng thông điệp.

Ví dụ sử dụng JUL

import java.util.logging.Logger;
import java.util.logging.Level;

public class LoggingExample {
    // Lấy logger theo tên lớp
    private static final Logger logger = Logger.getLogger(LoggingExample.class.getName());

    public static void main(String[] args) {
        logger.info("Ứng dụng đã khởi động");
        logger.warning("Đây là cảnh báo!");
        logger.severe("Và đây là lỗi rồi!");

        int x = 42;
        logger.fine("Biến debug x=" + x); // Mặc định không hiển thị
    }
}

Điểm quan trọng: mặc định JUL chỉ hiển thị các mức INFO trở lên. Để thấy fine và các thông điệp chi tiết khác, cần cấu hình mức ghi log.

Cấu hình qua tệp

Có thể cấu hình JUL qua tệp logging.properties (thường nằm trong thư mục JRE). Trong đó có thể chỉ định:

  • Mức tối thiểu cho logger
  • Ghi log đi đâu (tệp, console)
  • Định dạng thông điệp

Ví dụ một dòng trong logging.properties:

.level=INFO

4. Thư viện ghi log bên ngoài

Log4j (Apache)

Log4j là một trong những thư viện ghi log nổi tiếng nhất cho Java. Thư viện linh hoạt, mạnh mẽ, hỗ trợ nhiều định dạng, ghi bất đồng bộ, xoay vòng tệp và nhiều tính năng khác.

Ví dụ cấu hình đơn giản nhất cho Log4j 2 (XML):

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

Ví dụ dùng Log4j 2 trong code:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jExample {
    private static final Logger logger = LogManager.getLogger(Log4jExample.class);

    public static void main(String[] args) {
        logger.info("Hello from Log4j!");
        logger.error("Đây là lỗi thông qua Log4j");
    }
}

Để chạy được, bạn cần thêm các phụ thuộc Log4j vào dự án của mình (ví dụ qua Maven hoặc Gradle).

SLF4J: lớp facade ghi log

SLF4J (Simple Logging Facade for Java) không phải là một hệ thống ghi log riêng, mà là “lớp trung gian” giữa code của bạn và các triển khai cụ thể (Log4j, Logback, JUL, v.v.).

Facade giúp bạn viết code một lần và khi cần có thể thay đổi hệ thống ghi log thực tế mà không phải viết lại code, ngay cả khi các thư viện khác nhau dùng các logger khác nhau.

Ví dụ sử dụng SLF4J:

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

public class Slf4jExample {
    private static final Logger logger = LoggerFactory.getLogger(Slf4jExample.class);

    public static void main(String[] args) {
        logger.info("Xin chào, ghi log qua SLF4J!");
        logger.warn("Chú ý: có thể có vấn đề");
        logger.error("Lỗi qua SLF4J");
    }
}

Nó hoạt động thế nào?

  • Bạn viết code thông qua SLF4J.
  • Trong classpath thêm triển khai cần dùng (ví dụ Logback hoặc Log4j).
  • SLF4J tự chuyển các lời gọi tới triển khai đã chọn.

Tương tác của SLF4J với các logger khác: SLF4J có thể hoạt động trên các hệ như JUL, Log4j, Logback, v.v., giúp dễ dàng di trú mà không phải viết lại code.

5. Thực hành: so sánh System.out.println và ghi log

Ví dụ 1: System.out.println

public class PrintlnExample {
    public static void main(String[] args) {
        System.out.println("Ứng dụng đã khởi động");
        System.out.println("Đã xảy ra lỗi: có gì đó không ổn");
    }
}

Có gì chưa ổn:

  • Không có mức thông điệp (lỗi, thông tin, cảnh báo — tất cả như nhau).
  • Không có thời gian, tên lớp, luồng.
  • Tất cả thông điệp lẫn lộn vào một chỗ.
  • Không thể cấu hình linh hoạt việc xuất (ví dụ chỉ ghi lỗi).

Ví dụ 2: Ghi log qua SLF4J + Logback

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

public class LoggingVsPrintln {
    private static final Logger logger = LoggerFactory.getLogger(LoggingVsPrintln.class);

    public static void main(String[] args) {
        logger.info("Ứng dụng đã khởi động");
        logger.error("Đã xảy ra lỗi: có gì đó không ổn");
    }
}

Ưu điểm:

  • Mỗi thông điệp đi kèm thời gian, mức, tên lớp, luồng.
  • Có thể lọc thông điệp theo mức.
  • Có thể ghi log vào tệp, gửi qua mạng, định dạng, lưu trữ.
  • Ghi log an toàn với luồng (hữu ích cho ứng dụng đa luồng).

Minh họa sự khác biệt

System.out.println
Ghi log (SLF4J/Logback)
Ứng dụng đã khởi động 12:34:56 [main] INFO LoggingVsPrintln - Ứng dụng đã khởi động
Đã xảy ra lỗi: có gì đó không ổn 12:34:56 [main] ERROR LoggingVsPrintln - Đã xảy ra lỗi: có gì đó không ổn

6. Cách thêm ghi log vào dự án của bạn

Đối với logger tiêu chuẩn (JUL)

Mọi thứ đã có sẵn trong JDK, có thể dùng ngay:

import java.util.logging.Logger;

public class MyApp {
    private static final Logger logger = Logger.getLogger(MyApp.class.getName());

    public static void main(String[] args) {
        logger.info("Đây là thông điệp info");
    }
}

Đối với Log4j hoặc SLF4J

  1. Thêm các phụ thuộc (ví dụ qua Maven).

    Cho Log4j 2:

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.20.0</version>
    </dependency>
    

    Cho SLF4J + Logback:

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.7</version>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.4.7</version>
    </dependency>
    
  2. Tạo tệp cấu hình (ví dụ logback.xml cho Logback hoặc log4j2.xml cho Log4j).
  3. Sử dụng logger trong code của bạn (xem các ví dụ ở trên).

7. Ứng dụng mini: thêm ghi log

Giả sử chúng ta có một ứng dụng đơn giản — máy tính trên dòng lệnh. Hãy thêm ghi log cho nó.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Scanner;

public class CalculatorApp {
    private static final Logger logger = LoggerFactory.getLogger(CalculatorApp.class);

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        logger.info("Máy tính đã khởi chạy");

        try {
            System.out.print("Nhập số thứ nhất: ");
            int a = Integer.parseInt(scanner.nextLine());
            logger.debug("Đã nhập số thứ nhất: {}", a);

            System.out.print("Nhập số thứ hai: ");
            int b = Integer.parseInt(scanner.nextLine());
            logger.debug("Đã nhập số thứ hai: {}", b);

            int sum = a + b;
            logger.info("Tổng các số: {}", sum);
            System.out.println("Kết quả: " + sum);
        } catch (NumberFormatException ex) {
            logger.error("Lỗi nhập số", ex);
            System.out.println("Lỗi: hãy nhập số hợp lệ!");
        } catch (Exception ex) {
            logger.error("Lỗi không xác định", ex);
            System.out.println("Đã xảy ra lỗi!");
        }
        logger.info("Máy tính đã kết thúc");
    }
}

Ở đây diễn ra điều gì:

  • Tất cả sự kiện quan trọng đều được ghi vào log.
  • Lỗi được ghi ở mức ERROR kèm đầy đủ stack trace.
  • Toàn bộ thông tin có sẵn để phân tích sau khi chương trình chạy.

8. Những lỗi thường gặp khi làm việc với ghi log

Lỗi số 1: Chỉ dùng System.out.println để gỡ lỗi và chẩn đoán.
Tiện lúc ban đầu, nhưng hoàn toàn không phù hợp cho ứng dụng thực tế. Bạn không thể quản lý mức thông điệp, không thấy thời gian, không thể phân tích log nếu chương trình chạy trên máy chủ.

Lỗi số 2: Ghi quá nhiều thông tin ở mức ERROR.
Nếu ghi mọi thứ là lỗi, bạn sẽ không phân biệt được cái gì thực sự quan trọng và cái gì chỉ là thông tin phục vụ gỡ lỗi.

Lỗi số 3: Ghi log dữ liệu nhạy cảm (mật khẩu, token, số thẻ).
Log phải an toàn! Đừng ghi những thứ mà người khác không nên thấy.

Lỗi số 4: Không cấu hình ghi log.
Nếu không cấu hình mức, định dạng, nơi lưu log — bạn có thể “chìm” trong hàng gigabyte thông tin không cần thiết hoặc bỏ lỡ lỗi hoàn toàn.

Lỗi số 5: Không dùng facade ghi log (SLF4J) trong các dự án lớn.
Khi dự án phát triển, chuyển từ thư viện này sang thư viện khác — nếu không có facade sẽ rất đau đầu. SLF4J cho phép dễ dàng thay “engine” ghi log mà không phải viết lại code.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION