Logging hiệu quả: ghi log sao cho dễ tìm lỗi khi sự cố xảy ra

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Khi ứng dụng chạy thật và người dùng báo lỗi, bạn không thể gắn debugger vào. Thứ duy nhất bạn có là log. Log tốt giúp bạn xác định sự cố trong vài phút, log tệ thì hoặc không có gì để đọc, hoặc ngập trong hàng triệu dòng vô nghĩa. Dưới đây là những nguyên tắc ghi log hiệu quả.

1. Hiểu các mức log (log level)

Hầu hết thư viện đều có các mức từ nhẹ đến nặng. Dùng đúng mức giúp lọc nhanh khi cần.

MứcDùng khiVí dụ
DEBUGChi tiết phục vụ lập trình viênGiá trị biến, truy vấn đã chạy
INFOSự kiện bình thường quan trọngNgười dùng đăng nhập, job bắt đầu
WARNBất thường nhưng chưa hỏngDùng cấu hình mặc định vì thiếu giá trị
ERRORThao tác thất bại cần chú ýKhông lưu được đơn hàng
FATAL / CRITICALHệ thống không thể tiếp tụcMất kết nối database chính

Trên môi trường production thường chỉ bật từ INFO trở lên; khi điều tra mới tạm bật DEBUG.

2. Ghi log cái gì?

  • Các điểm ranh giới: request vào/ra, gọi dịch vụ ngoài, truy vấn lớn, job nền bắt đầu và kết thúc.
  • Quyết định quan trọng: vì sao đi nhánh này (ví dụ "áp dụng mã giảm giá X").
  • Mọi lỗi: kèm đủ ngữ cảnh để tái hiện.
  • Thay đổi trạng thái quan trọng: đơn hàng chuyển từ "chờ thanh toán" sang "đã thanh toán".

Đừng ghi log mọi dòng code, bạn sẽ chìm trong nhiễu. Hãy tự hỏi: "Lúc 3 giờ sáng có sự cố, thông tin nào giúp mình nhất?"

3. Mỗi dòng log cần ngữ cảnh

So sánh hai dòng log:

Kém:  Payment failedTốt:  Payment failed orderId=1042 userId=77 provider=momo error="timeout after 10s"

Dòng thứ hai cho bạn biết đơn nào, người nào, nhà cung cấp nào, lỗi gì. Hãy luôn kèm: thời gian, mức log, mã yêu cầu (request id/correlation id), các định danh liên quan (user, order), và thông điệp rõ ràng.

4. Dùng log có cấu trúc

Thay vì chuỗi văn bản tự do, nhiều hệ thống ghi log dạng JSON để máy dễ lọc và tìm:

{"time": "2025-03-14T09:21:07Z","level": "error","message": "payment failed","orderId": 1042,"userId": 77,"provider": "momo","requestId": "a1b2c3"}

Với công cụ tập trung log, bạn có thể tìm "mọi dòng có orderId=1042" chỉ bằng một truy vấn. Trong JavaScript có thể dùng thư viện như Winston hoặc Pino, Python có module logging tích hợp, PHP có Monolog, Java có SLF4J với Logback.

5. Dùng request id để lần theo một yêu cầu

Trong hệ thống có nhiều dịch vụ, một yêu cầu đi qua nhiều nơi. Hãy sinh một mã duy nhất ở điểm vào, truyền theo mọi lời gọi và ghi vào mọi dòng log. Khi có khiếu nại, bạn chỉ cần tìm mã đó để thấy toàn bộ hành trình.

6. Không ghi dữ liệu nhạy cảm

  • Mật khẩu, token, khóa API, số thẻ, mã OTP: tuyệt đối không ghi.
  • Thông tin cá nhân (email, số điện thoại, địa chỉ): hạn chế, hoặc che bớt (ví dụ a***@gmail.com).
  • Tránh in nguyên cả đối tượng request hoặc user mà không lọc trường.

Log thường được nhiều người đọc và lưu lâu, nên rò rỉ qua log là lỗi bảo mật khá phổ biến.

7. Ghi log lỗi đúng cách

Khi bắt exception, hãy ghi cả stack trace và ngữ cảnh, và chỉ ghi một lần. Nếu cùng một lỗi được log ở mỗi tầng khi nó đi lên, bạn sẽ có năm dòng cho một sự cố và khó đếm đúng số lần xảy ra.

try:charge(order)except PaymentError:logger.exception("Thanh toán thất bại order_id=%s", order.id)raise

Hàm logger.exception trong Python tự đính kèm stack trace. Lưu ý dùng tham số của logger (dấu %s) thay vì tự nối chuỗi, để thư viện chỉ định dạng khi mức log thực sự được bật.

8. Quản lý dung lượng và vòng đời log

  • Xoay vòng (rotation): chia file log theo ngày hoặc kích thước để không đầy ổ đĩa.
  • Thời gian lưu giữ: xóa hoặc nén log cũ theo chính sách.
  • Tập trung hóa: với nhiều máy chủ, gom log về một nơi để tìm kiếm.
  • Cảnh báo: thiết lập thông báo khi số lỗi tăng đột biến.
  • Hiệu năng: ghi log ở vòng lặp nóng hoặc với khối lượng lớn có thể làm chậm hệ thống.

9. Đọc log hiệu quả

  1. Xác định khoảng thời gian xảy ra sự cố.
  2. Lọc theo mức ERROR trước, rồi mở rộng sang WARN, INFO xung quanh thời điểm đó.
  3. Theo request id hoặc user id để dựng lại câu chuyện.
  4. Tìm dòng lỗi đầu tiên theo thời gian, vì phần sau thường là hệ quả.

Trên Linux, các lệnh như tail -f app.log để theo dõi trực tiếp và grep "orderId=1042" app.log để lọc nhanh rất hữu ích.

Kết luận

Log tốt là log được thiết kế cho người sẽ đọc nó trong lúc sự cố: có mức hợp lý, ngữ cảnh đủ, cấu trúc rõ ràng và không lộ dữ liệu nhạy cảm. Hãy thử rà lại các thông điệp log trong dự án của bạn, xem chúng có trả lời được câu hỏi "chuyện gì đã xảy ra, với ai, ở đâu" hay chưa.

Nếu bạn muốn học cách triển khai và vận hành ứng dụng bài bản, hãy xem thêm trên codebem.com.

author_photo
Nguyễn Đình Cường

0 comment