Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 48 — Logging Spec & Audit Trail

Technical BA Masterclass Bài 48/60

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng 3 giờ sáng, hệ thống thanh toán của một ví điện tử bỗng từ chối hàng loạt giao dịch. Đội vận hành dựng nhau dậy, mở dashboard lên — nhưng tất cả những gì họ thấy chỉ là một dòng log cụt lủn: Error: payment failed. Không có mã giao dịch, không có user nào, không biết gọi sang đối tác nào, không biết bắt đầu từ tầng nào. Họ mất 4 tiếng chỉ để tìm ra chuyện gì đang xảy ra, trong khi mỗi phút trôi qua là hàng nghìn giao dịch lỗi và khách hàng nổi giận trên Facebook.

Sự khác biệt giữa một sự cố được khắc phục trong 10 phút và một sự cố kéo dài 4 tiếng thường không nằm ở code chạy nhanh hay chậm, mà nằm ở chỗ: hệ thống có ghi log đúng cách hay không. Và đây chính là nơi Technical BA tạo ra giá trị mà ít người để ý. Khi bạn viết spec cho một tính năng, nếu bạn không nói rõ "ghi log gì, ở mức nào, kèm thông tin gì", thì dev sẽ ghi log theo bản năng — mỗi người một kiểu, và cuối cùng không ai dùng được.

Trong bài này, chúng ta tách bạch hai khái niệm hay bị nhập làm một: logging (phục vụ kỹ thuật — gỡ lỗi, giám sát) và audit trail (phục vụ nghiệp vụ và pháp lý — chứng minh "ai làm gì, lúc nào"). Là Technical BA, bạn phải viết spec cho cả hai, vì cả hai đều là yêu cầu, chỉ khác mục đích người đọc.

Khái niệm cốt lõi

Log levels — ngôn ngữ chung của vận hành

Mọi hệ thống logging nghiêm túc đều phân cấp mức độ (log level). Đây là quy ước chung mà bạn cần đưa vào spec để dev không tùy tiện:

  • DEBUG — Thông tin chi tiết phục vụ lập trình viên: giá trị biến, từng bước trong vòng lặp, payload đầy đủ. Cực kỳ "ồn ào", chỉ bật khi đang điều tra. Bắt buộc tắt trên production vì nó vừa làm phình log vừa dễ lộ dữ liệu nhạy cảm.
  • INFO — Luồng chạy bình thường: "Người dùng đăng nhập thành công", "Đơn hàng #12345 được tạo", "Gửi email xác nhận". Đây là nhịp tim của hệ thống, cho bạn biết mọi thứ đang chảy đúng hướng.
  • WARN — Bất thường nhưng hệ thống vẫn chạy được: gọi API đối tác bị chậm phải retry, cache miss bất thường, một config dùng giá trị mặc định vì thiếu cấu hình. WARN là "đèn vàng" — chưa hỏng, nhưng đáng để theo dõi xu hướng.
  • ERROR — Một thao tác thất bại và cần con người để ý: ghi database lỗi, gọi cổng thanh toán trả về 500, không gửi được tin nhắn. Mỗi ERROR nên ứng với một thứ gì đó thực sự sai và đáng để cảnh báo.
  • FATAL/CRITICAL — Hệ thống không thể tiếp tục: mất kết nối database hoàn toàn, hết bộ nhớ, service sập. Mức này thường gắn với cảnh báo gọi điện cho người trực.
Mẹo viết spec: hãy gắn mỗi nghiệp vụ quan trọng với một level kỳ vọng. Ví dụ trong spec đăng nhập: "Đăng nhập thành công → INFO; sai mật khẩu → WARN; tài khoản bị khóa do quá số lần thử → WARN kèm cảnh báo bảo mật; lỗi kết nối tới Identity Provider → ERROR".

Structured logging — log để máy đọc, không chỉ người đọc

Log dạng văn bản thuần (User abc logged in at 10:05) đẹp với mắt người nhưng vô dụng khi bạn có hàng triệu dòng và cần lọc, đếm, vẽ biểu đồ. Thay vào đó, hệ thống hiện đại dùng structured logging — mỗi dòng là một JSON có các trường rõ ràng:

{
  "timestamp": "2026-06-27T03:12:45.123Z",
  "level": "ERROR",
  "service": "payment-service",
  "trace_id": "a1b2c3d4",
  "user_id": "U_88213",
  "event": "payment_failed",
  "order_id": "ORD-99812",
  "amount": 1250000,
  "gateway": "VNPAY",
  "error_code": "GW_TIMEOUT",
  "message": "Payment gateway timed out after 30s"
}

Khác biệt là một trời một vực: với JSON này, bạn có thể truy vấn trong Kibana/Grafana kiểu "tất cả payment_failed của VNPAY trong 1 giờ qua" chỉ trong vài giây. Là BA, nhiệm vụ của bạn là chỉ định các trường bắt buộc (mandatory fields) trong spec: thường gồm timestamp, level, service, trace_id (hoặc correlation_id), và các định danh nghiệp vụ (order_id, transaction_id...).

Correlation ID / Trace ID — sợi chỉ xuyên suốt

Trong kiến trúc nhiều dịch vụ, một yêu cầu của khách đi qua API Gateway → service đơn hàng → service thanh toán → service thông báo. Nếu mỗi service ghi log riêng, bạn không cách nào nối chúng lại. Correlation ID là một mã duy nhất sinh ra ở cửa ngõ và được truyền theo suốt hành trình, gắn vào mọi dòng log. Khi điều tra, bạn chỉ cần lọc theo mã đó là thấy toàn bộ "đường đi" của một giao dịch. Hãy luôn đưa yêu cầu này vào spec tích hợp.

Audit trail — chứng cứ nghiệp vụ, không phải log gỡ lỗi

Đây là phần dân kỹ thuật hay xem nhẹ nhưng dân BA và kiểm toán thì cực kỳ coi trọng. Audit trail trả lời câu hỏi: Ai đã làm , với đối tượng nào, lúc nào, từ đâu, giá trị cũ và mới là gì. Khác biệt cốt lõi so với log kỹ thuật:

Tiêu chíApplication LogAudit Trail
Mục đíchGỡ lỗi, giám sátTruy vết trách nhiệm, tuân thủ
Người đọcDev, SREKiểm toán, pháp lý, bảo mật
Tính bất biếnCó thể xoay vòng, xóaKhông được sửa/xóa (append-only)
Thời gian lưuVài tuần đến vài thángThường nhiều năm theo luật
Nội dungKỹ thuậtNghiệp vụ: ai, gì, trước/sau
Một bản ghi audit chuẩn thường gồm: actor (người/hệ thống thực hiện), action (CREATE/UPDATE/DELETE/APPROVE...), entityentity_id (đối tượng tác động), before_value / after_value, timestamp, ip_address, và reason (nếu là thao tác nhạy cảm như miễn giảm phí). Điểm mấu chốt: audit trail phải append-only — chỉ thêm, không bao giờ sửa hay xóa, để chính nó không thể bị làm giả.

Đừng ghi cái không được phép ghi

Quy tắc vàng: không bao giờ ghi mật khẩu, số thẻ đầy đủ, CVV, OTP, token, hay thông tin định danh nhạy cảm vào log. Đây không phải lời khuyên — đó là yêu cầu tuân thủ (PCI-DSS, Nghị định 13/2023 về bảo vệ dữ liệu cá nhân tại Việt Nam). Khi cần ghi để truy vết, hãy che (mask): 4111**1111, hoặc dùng token thay thế.

Tình huống thực tế

Tình huống 1 — Ví điện tử và "dòng log vô dụng"

Một ví điện tử tại TP.HCM xử lý khoảng 2 triệu giao dịch/ngày. Đội kỹ thuật ghi log nhưng theo kiểu tự do: chỗ thì payment error, chỗ thì txn fail, không có trace_id, không có order_id. Khi cổng thanh toán đối tác gặp sự cố, đội vận hành mất gần 4 tiếng chỉ để xác định bao nhiêu giao dịch bị ảnh hưởng và là những giao dịch nào — vì không thể truy vấn được.

Sau sự cố, một Technical BA được giao viết lại Logging Spec. Cô ấy quy định: mọi event thanh toán phải là structured JSON, bắt buộc có trace_id, transaction_id, gateway, error_code chuẩn hóa (một bảng mã lỗi cố định thay vì message tự do), và level theo quy ước. Ba tháng sau, một sự cố tương tự xảy ra — lần này đội vận hành lọc error_code = GW_TIMEOUTgateway = VNPAY, xác định chính xác 12.430 giao dịch bị ảnh hưởng trong 8 phút, rồi chủ động hoàn tiền trước khi khách kịp khiếu nại.

Bài học: Giá trị của log không nằm ở việc log, mà ở việc log có cấu trúc và định danh để truy vấn được. Đó là việc của BA, không phải để dev tự quyết.

Tình huống 2 — Ngân hàng và câu hỏi của kiểm toán: "Ai đã duyệt?"

Một ngân hàng số tại Việt Nam triển khai tính năng nhân viên có thể miễn phí chuyển khoản cho khách VIP. Sáu tháng sau, kiểm toán nội bộ phát hiện một loạt giao dịch được miễn phí bất thường, tổng cộng vài trăm triệu đồng. Câu hỏi đặt ra: ai đã duyệt những lần miễn phí đó, dựa trên lý do gì?

Vấn đề: hệ thống chỉ có application log (đã bị xoay vòng và xóa sau 30 ngày), không có audit trail riêng. Không ai trả lời được. Đây là một phát hiện nghiêm trọng trong báo cáo kiểm toán.

Bài học được rút ra khi xây dựng lại module: BA viết spec audit trail bắt buộc cho mọi thao tác miễn/giảm phí, ghi actor (mã nhân viên), action = FEE_WAIVER, customer_id, amount_waived, reason (bắt buộc nhập, không cho để trống), approver_id, timestamp, lưu vào bảng append-only giữ tối thiểu 7 năm theo quy định lưu trữ chứng từ. Lần kiểm toán sau, mọi miễn giảm đều có dấu vết đầy đủ.

Bài học: Audit trail không phải tính năng "có thì tốt" — với ngành tài chính, nó là yêu cầu pháp lý. Nếu BA không viết nó vào spec, không ai làm, và rủi ro dồn về phía doanh nghiệp khi bị kiểm toán.

Tình huống 3 — Sàn TMĐT Đông Nam Á và "cơn lũ ERROR giả"

Một sàn thương mại điện tử khu vực Đông Nam Á gặp tình trạng đội SRE bị "ngợp": mỗi đêm có hàng chục nghìn cảnh báo ERROR, đến mức không ai buồn đọc nữa — hiện tượng alert fatigue (mệt mỏi vì cảnh báo). Khi điều tra, hóa ra dev đã đặt mọi thứ ở mức ERROR, kể cả những việc bình thường như "khách bỏ giỏ hàng" hay "tìm kiếm không ra kết qu" — những thứ lẽ ra chỉ là INFO.

BA cùng đội kỹ thuật rà soát lại bảng phân loại level: chuyển phần lớn về INFO/WARN, chỉ giữ ERROR cho những thất bại thật sự cần con người can thiệp. Số cảnh báo ERROR giảm từ ~40.000 xuống còn ~300 mỗi đêm. Quan trọng hơn, khi một ERROR thật xuất hiện, nó không còn bị chôn vùi trong đống nhiễu.

Bài học: Level đặt sai cũng nguy hiểm như không có log. Cảnh báo "kêu" quá nhiều thì cũng như không có cảnh báo. Đặt đúng level là một quyết định nghiệp vụ mà BA nên tham gia định nghĩa.

Hướng dẫn từng bước

Khi bạn cần viết một Logging & Audit Spec cho một tính năng, hãy đi theo các bước sau:

  • Liệt kê các sự kiện đáng ghi. Đi qua từng luồng nghiệp vụ và liệt kê các điểm cần log: bắt đầu thao tác, thành công, thất bại, các nhánh ngoại lệ. Đừng ghi mọi thứ — ghi cái có ích cho gỡ lỗi và truy vết.
  • Gán level cho từng sự kiện. Lập bảng: Sự kiện | Level | Khi nào kích hoạt. Đây là tài liệu để dev triển khai nhất quán và để SRE cấu hình cảnh báo.
  • Định nghĩa schema log chuẩn. Liệt kê các trường bắt buộc (timestamp, level, service, trace_id) và các trường nghiệp vụ theo tính năng. Quy định định dạng (JSON), múi giờ (luôn UTC để tránh nhầm lẫn), và bảng mã lỗi chuẩn hóa.
  • Tách riêng yêu cầu Audit Trail. Với các thao tác nhạy cảm (tạo/sửa/xóa dữ liệu quan trọng, phê duyệt, thay đổi quyền, giao dịch tiền), định nghĩa bản ghi audit: actor, action, entity, before/after, reason, lưu append-only, thời gian lưu trữ theo quy định.
  • Chỉ định dữ liệu cần che/loại bỏ. Ghi rõ trường nào không được log, trường nào phải mask. Đây là rào chắn tuân thủ — đừng để nó là chuyện "dev tự nhớ".
  • Định nghĩa thời gian lưu và xoay vòng (retention & rotation). Log kỹ thuật giữ bao lâu, audit giữ bao lâu, lưu ở đâu (hot storage để truy vấn nhanh, cold storage/archive cho dài hạn). Cân nhắc chi phí lưu trữ.
  • Liên kết với cảnh báo. Chỉ ra ERROR/FATAL nào cần tạo cảnh báo, gửi đi đâu (Slack, Telegram, PagerDuty), ngưỡng nào thì báo. Đây là cầu nối giữa logging và observability.

Lỗi thường gặp & mẹo

  • Log quá nhiều hoặc quá ít. Quá nhiều thì tốn tiền lưu trữ và che mất tín hiệu quan trọng; quá ít thì không gỡ được lỗi. Mẹo: tự hỏi "nếu sự cố này xảy ra lúc 3 giờ sáng, dòng log này có giúp người trực hiểu chuyện gì không?".
  • Nhầm log với audit. Application log có thể bị xoay vòng/xóa, nên đừng dựa vào nó cho mục đích pháp lý. Nếu nghiệp vụ cần chứng minh trách nhiệm, hãy viết audit trail riêng, append-only.
  • Để ERROR thành "thùng rác". Đừng dồn mọi thứ vào ERROR. Hãy giữ ERROR cho những gì thật sự cần con người. Đây là cách phòng chống alert fatigue.
  • Ghi nhầm dữ liệu nhạy cảm. Mật khẩu, token, số thẻ, OTP lọt vào log là sự cố bảo mật nghiêm trọng. Luôn có mục "dữ liệu cấm ghi" trong spec.
  • Message tự do, không chuẩn hóa. Để dev viết message tùy ý khiến không thể truy vấn theo nhóm lỗi. Mẹo: định nghĩa một bảng error_code cố định, message tự do chỉ là phần bổ sung cho người đọc.
  • Quên múi giờ và correlation ID. Hai thiếu sót kinh điển khiến điều tra sự cố biến thành ác mộng. Luôn UTC, luôn có trace_id xuyên suốt.

Bài tập thực hành

Bạn đang làm Technical BA cho tính năng "Đặt lại mật khẩu qua email" của một ứng dụng ngân hàng số.

  • Liệt kê tối thiểu 6 sự kiện cần ghi log trong luồng này (từ lúc yêu cầu đặt lại đến lúc đổi mật khẩu thành công, bao gồm các nhánh lỗi), và gán level phù hợp cho mỗi sự kiện. Giải thích vì sao "yêu cầu đặt lại cho email không tồn tại" nên là WARN chứ không phải ERROR.
  • Thiết kế schema structured log (JSON) cho sự kiện "đổi mật khẩu thành công", liệt kê các trường bắt buộc. Chỉ rõ trường nào tuyệt đối không được xuất hiện trong log.
  • Tính năng này có cần audit trail riêng không? Nếu có, hãy thiết kế bản ghi audit cho thao tác đổi mật khẩu (actor, action, entity, các trường cần thiết) và đề xuất thời gian lưu trữ, kèm lý do.
  • Đề xuất một cảnh báo (alert): điều kiện nào trên log nên tự động báo cho đội bảo mật? (Gợi ý: nghĩ về dấu hiệu tấn công dò mật khẩu.)

Tóm tắt

Logging và audit trail là hai mặt của cùng một câu hỏi: "Khi có chuyện xảy ra, chúng ta có biết được không?". Logging phục vụ kỹ thuật — gỡ lỗi và giám sát — và sống nhờ ba thứ: phân cấp level đúng (DEBUG/INFO/WARN/ERROR/FATAL), structured logging để máy truy vấn được, và correlation ID để nối các mảnh lại. Audit trail phục vụ nghiệp vụ và pháp lý — nó append-only, bất biến, lưu lâu dài, và trả lời "ai làm gì, lúc nào, trước/sau ra sao".

Là Technical BA, bạn không viết code ghi log, nhưng bạn quyết định ghi gì, ở mức nào, kèm thông tin gì, lưu bao lâu, và cái gì tuyệt đối không được ghi. Một logging spec tốt biến một sự cố 4 tiếng thành 8 phút, và một audit spec tốt giúp doanh nghiệp đứng vững trước kiểm toán. Đừng để những quyết định này phó mặc cho bản năng của từng dev — đó chính là chỗ bạn tạo ra giá trị.