VieTopik
Tiếng HànTiếng AnhIT
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
Spring Boot Web APICấu trúc ứng dụng
Bài 11/19
5 phút

Logging

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Khách báo không đặt được hàng lúc 2 giờ sáng. Không ai ngồi xem server lúc đó. Thứ duy nhất còn lại để biết chuyện gì đã xảy ra là log. Bài này hướng dẫn ghi log đúng cách trong Spring Boot.

Khái niệm

📝 Logging: ghi lại những gì ứng dụng đang làm, để tra cứu khi có sự cố.

📶 Log level: mức độ quan trọng của một dòng log, từ thấp tới cao là TRACE, DEBUG, INFO, WARN, ERROR.

Level Khi nào dùng
DEBUG chi tiết để dò lỗi lúc dev
INFO sự kiện bình thường: tạo đơn, đăng nhập
WARN bất thường nhưng vẫn chạy được
ERROR lỗi làm hỏng một thao tác

Ví dụ

// ProductsController.java
package com.shop.api;

import org.slf4j.*;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/products")
class ProductsController {
    private static final Logger log =
        LoggerFactory.getLogger(
            ProductsController.class);

    @GetMapping("/{id}")
    public ResponseEntity<Integer> getById(
            @PathVariable int id) {
        log.debug("Bắt đầu tìm sản phẩm {}", id);

        if (id > 100) {
            log.warn("Không tìm thấy sản phẩm {}", id);
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(id);
    }
}
  • Logger và LoggerFactory đến từ SLF4J, thư viện log có sẵn trong Spring Boot. Mỗi class tạo một logger static final cho riêng mình.
  • Tên class truyền vào getLogger được ghi kèm mỗi dòng log để biết log đến từ đâu.
  • {} là placeholder: giá trị của id được điền vào đúng vị trí đó.
  • application.properties quy định level thấp nhất được ghi. Mặc định là INFO, nên dòng log.debug không hiện. Thêm logging.level.com.shop.api=DEBUG thì nó hiện.

Thử ngay

Giữ các file cũ, thay ProductsController.java theo phần Ví dụ, rồi chạy lại server và gọi:

curl -i http://localhost:5000/api/products/999

Đoán trước khi chạy: getById gọi cả log.debug và log.warn. Cửa sổ đang chạy server hiện mấy dòng log mới của ProductsController?

Xem kết quả
 WARN ... com.shop.api.ProductsController
    : Không tìm thấy sản phẩm 999

Chỉ một dòng WARN của getById. Phần đầu dòng (thời gian, số tiến trình, tên thread) đã được lược bớt. log.debug thấp hơn mức INFO mặc định nên bị bỏ qua. Lần gọi đầu có kèm ba dòng INFO báo khởi tạo DispatcherServlet, bỏ qua được.

Lỗi hay gặp

Ghép chuỗi vào log. Dòng log vẫn đọc được, nhưng chuỗi luôn bị ghép, kể cả khi level đó đang tắt. Với {}, SLF4J chỉ điền giá trị khi dòng log thật sự được ghi.

// SAI — chuỗi được ghép dù DEBUG đang tắt
log.debug("Bắt đầu tìm sản phẩm " + id);

Cách đúng là truyền giá trị qua placeholder như ở phần Ví dụ: log.debug("Bắt đầu tìm sản phẩm {}", id).

Ghi dữ liệu nhạy cảm. Mật khẩu, số thẻ, token không bao giờ được ghi vào log, vì nhiều người và nhiều hệ thống đọc được log.

Tóm tắt

  • Tạo logger bằng LoggerFactory.getLogger(TênClass.class) để ghi log.
  • Chọn level theo mức độ: INFO cho sự kiện thường, WARN cho bất thường, ERROR cho lỗi.
  • Dùng placeholder {} thay vì ghép chuỗi.
  • Level thấp nhất được ghi đặt bằng logging.level.* trong application.properties.

Tự kiểm tra

0/3 câu
Câu 1

Thanh toán thất bại vì cổng thanh toán trả lỗi, đơn hàng không tạo được. Nên ghi log level nào?

Câu 2

Mức log thấp nhất đặt là WARN. Dòng nào sẽ được ghi?

Câu 3

Cách viết log nào tốt nhất?

Cấu hình với application.propertiesFilter và interceptor

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt