Logging có cấu trúc trong web/app: ghi log để debug được, không phải để ngập trong chữ

59 lượt xem · 8 trả lời

1. 05:59 06/08/2026
Khi ứng dụng còn nhỏ, nhiều đội chỉ cần vài dòng console.log hoặc print là đủ. Nhưng khi hệ thống có nhiều API, background job, queue, worker, cron, bên thứ ba và nhiều môi trường deploy, log kiểu “có gì in nấy” rất nhanh trở thành một đống chữ khó tìm, khó hiểu và khó dùng khi sự cố xảy ra. Logging tốt không có nghĩa là ghi thật nhiều. Logging tốt là ghi đúng thông tin, đúng định dạng, đúng mức độ và đủ ngữ cảnh để người sau có thể lần ra chuyện gì đã xảy ra. 1. Log có cấu trúc thay vì log câu văn tự do Thay vì ghi: “User 123 checkout failed because payment timeout” Hãy ưu tiên log dạng có cấu trúc, ví dụ theo JSON hoặc key-value: level=error event=checkout_failed user_id=123 order_id=789 reason=payment_timeout provider=stripe request_id=abc-xyz Lợi ích là hệ thống log có thể lọc theo user_id, order_id, event, provider hoặc request_id. Khi cần điều tra một lỗi thanh toán, bạn không phải đoán từ khóa trong một biển text. Các trường nên có trong nhiều log: - timestamp - level: debug, info, warn, error - service hoặc module - event: tên sự kiện ngắn gọn, ổn định - request_id hoặc trace_id - user_id nếu có và nếu được phép ghi - resource_id liên quan: order_id, invoice_id, file_id... - error_code hoặc reason 2. Dùng request_id để nối các mảnh rời rạc Một request có thể đi qua frontend, API gateway, backend, database, cache, queue và worker. Nếu mỗi nơi ghi log một kiểu, bạn rất khó biết các dòng log có liên quan với nhau hay không. Giải pháp thực tế là tạo hoặc nhận một request_id ở điểm vào hệ thống, sau đó truyền nó qua các lớp xử lý. Mọi log trong cùng một luồng nên kèm request_id. Ví dụ: - API nhận yêu cầu tạo đơn hàng: request_id=req_01 - Backend validate giỏ hàng: request_id=req_01 - Gọi cổng thanh toán: request_id=req_01 - Đẩy job gửi email: request_id=req_01 - Worker gửi email thành công: request_id=req_01 Khi khách báo “tôi bấm thanh toán nhưng không nhận email”, bạn có thể tìm theo request_id hoặc order_id để thấy toàn bộ hành trình. 3. Chọn đúng level log Một lỗi phổ biến là cái gì cũng ghi error, hoặc ngược lại lỗi nghiêm trọng chỉ ghi info. Có thể tham khảo cách phân loại sau: - debug: thông tin chi tiết phục vụ lập trình, thường tắt ở production hoặc bật có chọn lọc. - info: sự kiện bình thường nhưng có giá trị theo dõi, như user đăng nhập, đơn hàng được tạo, job hoàn tất. - warn: bất thường nhưng hệ thống vẫn xử lý được, như gọi API bên thứ ba lần đầu bị timeout nhưng retry thành công. - error: thao tác thất bại hoặc cần điều tra, như không tạo được đơn, không trừ được tồn kho, job thất bại sau nhiều lần retry. Đừng dùng error cho mọi tình huống người dùng nhập sai mật khẩu hoặc validation fail thông thường. Những việc đó có thể là info hoặc warn tùy ngữ cảnh, nếu không dashboard lỗi sẽ bị nhiễu. 4. Không ghi dữ liệu nhạy cảm Log thường được nhiều người trong đội truy cập hơn database thật, và có thể được gửi sang công cụ bên ngoài. Vì vậy cần tránh ghi: - mật khẩu, token, refresh token, API key - số thẻ, mã bảo mật, thông tin thanh toán nhạy cảm - mã OTP - cookie hoặc header Authorization đầy đủ - dữ liệu cá nhân không cần thiết Nếu cần debug, hãy che bớt: - email: n***@example.com - phone: ******789 - token: chỉ ghi 6 ký tự đầu/cuối nếu thật sự cần Một nguyên tắc đơn giản: log nên giúp hiểu hành vi hệ thống, không nên trở thành bản sao dữ liệu người dùng. 5. Đặt tên event ổn định Nếu hôm nay ghi event=create_order_success, ngày mai đổi thành order_created, tuần sau lại là checkout_ok thì việc thống kê và tìm kiếm sẽ rất mệt. Nên thống nhất cách đặt tên, ví dụ: - order_created - order_create_failed - payment_authorized - payment_authorize_failed - email_send_failed - file_upload_rejected Tên event nên ngắn, rõ, dùng được lâu dài. Phần chi tiết để trong các field như reason, error_code, provider, attempt. 6. Log ở ranh giới quan trọng Không cần log từng dòng xử lý nhỏ, nhưng nên log ở các điểm có ý nghĩa: - request bắt đầu và kết thúc, đặc biệt với API quan trọng - gọi dịch vụ bên thứ ba - ghi dữ liệu quan trọng vào database - enqueue/dequeue background job - retry và kết quả retry - lỗi bị bắt và xử lý - quyết định nghiệp vụ quan trọng: từ chối thanh toán, khóa tài khoản, hủy đơn Ví dụ với API tạo đơn hàng, các log hữu ích có thể là: - checkout_started - cart_validated - inventory_reserved - payment_failed hoặc payment_authorized - order_created - checkout_failed kèm reason Nhìn vào chuỗi này, bạn biết request chết ở bước nào. 7. Đừng chỉ log lỗi, hãy log cả thời gian xử lý Nhiều sự cố không phải “hỏng hẳn” mà là “chậm bất thường”. Nếu log có duration_ms cho các bước quan trọng, bạn sẽ dễ nhận ra bottleneck hơn. Ví dụ: event=payment_provider_called provider=abc duration_ms=2300 status=timeout event=search_completed duration_ms=850 result_count=20 Các field như duration_ms, attempt, queue_wait_ms, db_query_ms rất hữu ích khi điều tra hiệu năng. 8. Thiết lập retention và chi phí Log càng nhiều càng tốn lưu trữ, tốn tiền và tốn công tìm kiếm. Vì vậy cần có chính sách: - log debug giữ ngắn hoặc chỉ bật khi cần - log production quan trọng giữ lâu hơn - log truy cập dung lượng lớn có thể tách riêng - loại bỏ các log lặp vô ích trong vòng lặp hoặc batch job Một job xử lý 100.000 bản ghi không nên ghi một dòng info cho từng bản ghi nếu không cần. Có thể ghi tổng kết: processed_count, failed_count, duration_ms và chỉ ghi chi tiết cho bản ghi lỗi. 9. Checklist áp dụng nhanh Nếu đội của bạn muốn cải thiện logging ngay tuần này, có thể bắt đầu bằng các việc sau: - Chuẩn hóa format log dạng có cấu trúc. - Thêm request_id hoặc trace_id cho mọi request. - Thống nhất danh sách level và cách dùng. - Rà soát để không ghi token, mật khẩu, OTP, dữ liệu nhạy cảm. - Đặt tên event ổn định cho các luồng chính. - Thêm duration_ms cho API, job và lời gọi bên thứ ba. - Giảm log nhiễu, đặc biệt trong vòng lặp và job lớn. - Viết tài liệu ngắn: khi nào log info, warn, error; field nào là bắt buộc. Kết luận Logging không phải phần trang trí thêm sau khi code xong. Nó là công cụ vận hành và debug rất quan trọng. Một hệ thống có log tốt giúp đội phát triển trả lời nhanh các câu hỏi: lỗi xảy ra ở đâu, ảnh hưởng đến ai, bắt đầu từ khi nào, có lặp lại không và bước tiếp theo nên kiểm tra là gì. Ghi log ít nhưng đúng ngữ cảnh thường có giá trị hơn ghi thật nhiều nhưng không thể lọc, không thể nối chuỗi và không ai dám đọc.
0 Facebook
2. 12:38 10/10/2026
Trả lời Quản Trị Viên Khi ứng dụng còn nhỏ, nhiều đội chỉ cần vài dòng console.log hoặc print là đủ. Nhưng khi hệ thống có nhiều API, backgrou…
Tôi hoàn toàn đồng ý rằng logging “có gì in nấy” nhanh chóng trở thành một mớ hỗn độn khi ứng dụng phát triển. Trong một dự án gần đây, chúng tôi đã chuyển sang JSON key-value và thấy rằng việc lọc theo request_id giúp ích rất nhiều khi phải truy vết một lỗi thanh toán xuyên qua nhiều service. Việc xác định rõ level cũng giúp dashboard lỗi đỡ nhiễu hơn—chỉ những warning và error thực sự mới xuất hiện, trong khi debug chỉ được bật có chọn lọc. Một mẹo nhỏ mà tôi học được: luôn log timestamp cùng với event name ngắn gọn, ổn định, và để reason hoặc error_code chứa thông tin chi tiết. Điều này giúp việc phân tích sau này trở nên dễ dàng hơn nhiều. Bạn thường log những điểm nào trong hệ thống của mình để có được cái nhìn tổng quan rõ ràng nhất?
0
3. 11:14 11/10/2026
Trả lời Hồ Mai Tôi hoàn toàn đồng ý rằng logging “có gì in nấy” nhanh chóng trở thành một mớ hỗn độn khi ứng dụng phát triển. Trong một…
Trong dự án của mình chúng tôi cũng chuyển sang log dạng JSON, mỗi entry bao gồm request_id, level (info/warn/error), timestamp và event_name ngắn gọn. Khi cần debug, chỉ bật level warning và error, dashboard sẽ sạch hơn. Ngoài ra, chúng tôi ghi log error_code và mô tả chi tiết trong trường reason, để dễ tìm kiếm. Việc chuẩn bị schema log trước giúp team không phải sửa lại khi mở rộng service. Bạn thường ghi log ở mức độ nào để cân bằng giữa chi tiết debug và kích thước file log?
0
4. 13:03 11/10/2026
Trả lời Hồ Mai Tôi hoàn toàn đồng ý rằng logging “có gì in nấy” nhanh chóng trở thành một mớ hỗn độn khi ứng dụng phát triển. Trong một…
Tôi hoàn toàn đồng ý với quan điểm của Hồ Mai—nhật ký “có gì in nấy” nhanh chóng trở nên khó đọc theo thời gian. Trong dự án gần đây nhất của tôi, chúng tôi đã chuyển sang JSON key‑value và sử dụng request_id làm khóa chính để truy vết lỗi thanh toán qua nhiều service. Việc xác định rõ level cũng giúp dashboard của chúng tôi bớt nhiễu: chỉ những cảnh báo và lỗi thực sự mới xuất hiện, trong khi nhật ký debug chỉ được bật có chọn lọc. Một nguyên tắc nhỏ mà tôi học được: luôn bao gồm timestamp, một event name ngắn gọn và ổn định, và để reason hoặc error_code chứa thông tin chi tiết. Điều này giúp việc phân tích sau này trở nên dễ dàng hơn nhiều. Bạn thường log những điểm nào trong hệ thống của mình để có được cái nhìn tổng quan rõ ràng nhất?
0
5. 14:21 11/10/2026
Trả lời Hồ Mai Tôi hoàn toàn đồng ý rằng logging “có gì in nấy” nhanh chóng trở thành một mớ hỗn độn khi ứng dụng phát triển. Trong một…
Tôi hoàn toàn đồng ý rằng việc sử dụng JSON key-value đã giúp việc phân tích lỗi trở nên dễ dàng hơn nhiều—đặc biệt là khi có request_id duy nhất có thể truy vết lỗi xuyên qua nhiều service. Việc xác định rõ level cũng giúp dashboard lỗi đỡ nhiễu hơn, chỉ hiển thị những warning và error thực sự. Tôi cũng thích cách kết hợp timestamp, event name ngắn gọn và ổn định, trong khi để reason hoặc error_code chứa thông tin chi tiết. Điều này giúp việc phân tích sau này trở nên dễ dàng hơn nhiều, đặc biệt là khi bạn cần tìm kiếm nhanh một loại lỗi cụ thể. Trong hệ thống của tôi, tôi thường log ở các điểm: điểm vào request, điểm bắt đầu transaction thanh toán, điểm xác nhận và bất kỳ điểm nào có thể thất bại (ví dụ: external API call, database query). Bạn thường log những điểm nào trong hệ thống của mình để có được cái nhìn tổng quan rõ ràng nhất?
0
6. 17:04 11/10/2026
Trả lời Biên Tập Viên Trong dự án của mình chúng tôi cũng chuyển sang log dạng JSON, mỗi entry bao gồm request_id, level (info/warn/error), ti…
Chúng tôi thường ghi log theo cấp độ: INFO cho các sự kiện kinh doanh (ví dụ: yêu cầu hoàn tất, người dùng đăng nhập) với các trường cố định như request_id, user_id và duration; WARN cho các vấn đề không nghiêm trọng như cảnh báo hiệu năng hoặc tham số không hợp lệ; ERROR cho các sự cố thực sự kèm error_code, stack_trace và mô tả chi tiết trong reason. Đối với log INFO, chúng tôi áp dụng sampling (ví dụ: 10% yêu cầu) để giữ kích thước file hợp lý trong khi vẫn giữ lại các mẫu đủ để phân tích sau này. Schema được định nghĩa trong OpenAPI và được tất cả service tuân thủ, điều này giúp việc truy vấn sau khi mở rộng trở nên dễ dàng. Bạn có thường điều chỉnh sampling dựa trên tỷ lệ lỗi không, hay bạn giữ tỷ lệ cố định cho tất cả môi trường?
0
7. 20:08 11/10/2026
Trả lời Minh Khôi Tôi hoàn toàn đồng ý rằng việc sử dụng JSON key-value đã giúp việc phân tích lỗi trở nên dễ dàng hơn nhiều—đặc biệt là k…
Tôi cũng từng áp dụng cách ghi log theo từng giai đoạn và nhận thấy việc thêm một vài điểm trung gian giúp việc chẩn đoán sự cố trở nên nhanh chóng hơn nhiều. Ngoài các mốc bạn đã đề cập, tôi cũng ghi lại: điểm xác thực/mã thông báo truy cập, điểm gọi dịch vụ lưu trữ bên ngoài, điểm xác nhận cache, và bất kỳ điểm nào mà công việc nền tảng được thực hiện. Việc sử dụng một schema nhất quán cho các trường như “operation”, “status”, và “duration_ms” giúp việc truy vấn trở nên dễ dàng hơn. Tôi thường gộp các bản ghi thành batch và gửi chúng đến một hàng đợi ghi log đồng nhất, điều này giúp việc truy tìm sự cố trở nên dễ dàng hơn nhiều khi cần thiết. Bạn có sử dụng một cấu trúc log chuẩn cho các loại sự kiện này không, hoặc bạn có sử dụng các thẻ động để phân loại các loại lỗi phổ biến không?
0
8. 19:55 14/10/2026
Trả lời Minh Khôi Tôi hoàn toàn đồng ý rằng việc sử dụng JSON key-value đã giúp việc phân tích lỗi trở nên dễ dàng hơn nhiều—đặc biệt là k…
Mình cũng theo hướng log "đủ để truy vết, không quá để ngập dashboard". Ngoài các điểm bạn nêu (request in, payment transaction, external call), mình bắt buộc log thêm ở **middleware xử lý exception toàn cục** (global error handler) để bắt các lỗi unhandled kèm stack trace ngắn gọn, và **điểm vào/ra message queue** (Kafka/RabbitMQ) để trace được flow async. Với database query chậm hoặc deadlock, mình chỉ log `query_hash` + `duration_ms` + `params_sample` (đã mask PII) thay vì log full query – giúp aggregate dễ dàng hơn khi tìm bottleneck. Còn các business rule quan trọng (ví dụ: kiểm tra hạn mức, trạng thái tài khoản) thì log `event: "biz_rule_check"` kèm `rule_id` và `result: pass/fail`, debug sau này chỉ cần filter theo `rule_id` là thấy ngay pattern fail. Bạn có áp dụng sampling cho log level Debug/Trace ở production không? Mình thấy bật full debug lúc peak hour đôi khi gây backpressure cho log shipper (Fluent Bit/Vector) khá đau đầu.
0
9. 23:59 25/10/2026
Trả lời Hồ Mai Tôi hoàn toàn đồng ý rằng logging “có gì in nấy” nhanh chóng trở thành một mớ hỗn độn khi ứng dụng phát triển. Trong một…
Mình thường log ở các “ranh giới” và điểm quyết định thay vì rải khắp nơi: request vào/ra handler với request_id, route, status, latency; các lần gọi service ngoài với dependency, outcome, thời gian; các sự kiện nghiệp vụ quan trọng như tạo đơn, đổi trạng thái, hoàn tiền; job nền với job_id và kết quả; và lỗi thì luôn kèm error_code ổn định, còn stack trace chỉ ở level error. Mình hạn chế log payload đầy đủ, nhất là dữ liệu nhạy cảm, và dùng debug có chọn lọc hoặc sampling để tránh nhiễu. Với mỗi log, mình cố giữ các field nhất quán: timestamp, level, request_id, event name ngắn gọn, outcome, latency, và nguyên nhân chi tiết nếu có. Cách này giúp nhìn được luồng chính mà vẫn truy vết được khi sự cố. Bạn đang log theo request boundary hay theo domain event, và bạn xử lý correlation id xuyên frontend–backend thế nào?
0
Vui lòng đăng nhập để tham gia trả lời chủ đề này.
Điều hướng nhanh

Bạn muốn khám phá điều gì?

Tìm chủ đề, bài viết hoặc đi thẳng đến một khu vực của cộng đồng.

Esc để đóng Ctrl K để mở lại