Chương 50. Logging
Ở chương trước, ta nói observability có ba tín hiệu lớn:
Logs.
Metrics.
Traces.
Chương này đi sâu vào logs.
Logging nhìn qua có vẻ rất đơn giản:
In ra vài dòng để biết code chạy đến đâu.
Nhưng trong production, log không phải để ta cảm thấy yên tâm rằng code có chạy.
Log tốt phải giúp trả lời:
Chuyện gì đã xảy ra?
Xảy ra với request/job nào?
Xảy ra lúc nào?
Xảy ra ở service nào?
Ai hoặc tenant nào bị ảnh hưởng?
Kết quả là thành công hay lỗi?
Nếu lỗi, lỗi ở bước nào?
Thông điệp chính của chương:
> Log tốt không phải log nhiều. Log tốt là log đủ ngữ cảnh để điều tra sự việc thật trong production mà không làm lộ dữ liệu nhạy cảm và không tạo quá nhiều tiếng ồn.
---
50.1. Một tình huống thật hơn: học sinh hỏi "sao bài em chưa có điểm?"
Hãy tưởng tượng AI Judge đang chạy production.
Một học sinh nhắn:
Em nộp bài rồi nhưng chưa thấy điểm.
Nếu hệ thống không có log tốt, ta bắt đầu đoán:
Có thể upload lỗi.
Có thể job chưa vào queue.
Có thể worker chết.
Có thể AI provider timeout.
Có thể lưu result lỗi.
Có thể frontend chưa refresh.
Tất cả đều có thể đúng.
Nhưng đoán không giúp user.
Ta cần mở log và lần theo câu chuyện:
Submission sub_123 được tạo lúc 10:01.
File file_456 upload xong lúc 10:02.
GradingJob job_789 được enqueue lúc 10:02.
Worker worker-3 nhận job lúc 10:03.
Worker gọi Gemini lúc 10:03.
Gemini timeout lúc 10:04:30.
Job retry lần 1 lúc 10:05.
Worker lưu kết quả thành công lúc 10:07.
Notification gửi lỗi lúc 10:07.
Bây giờ ta hiểu:
Bài đã được chấm.
Điểm đã lưu.
Vấn đề nằm ở notification hoặc frontend hiển thị.
Log tốt biến một đống nghi ngờ thành một chuỗi sự kiện có thể kiểm chứng.
---
50.2. Log là nhật ký điều tra, không phải tiếng ồn
Khi mới code, ta hay viết:
print("here")
print("done")
print(data)
Ở local, cách này đôi khi đủ.
Nhưng production cần nhiều hơn.
Một dòng log kiểu:
done
không giúp nhiều.
Done cái gì?
Done cho request nào?
Done với user nào?
Done thành công hay chỉ đi qua một bước?
Done trong bao lâu?
Một dòng log tốt hơn:
grading_job_completed job_id=job_789 submission_id=sub_123 tenant_id=school_a duration_ms=92000 attempt=2
Dòng này chưa cần dài dòng, nhưng nó có ngữ cảnh.
Khi incident xảy ra, ngữ cảnh là vàng.
---
50.3. Log tốt kể được một câu chuyện
Một flow nộp bài có thể có các mốc:
submission_created
file_upload_completed
grading_job_enqueued
grading_job_started
ai_request_started
ai_request_failed
grading_job_retried
grading_result_saved
notification_sent
Đây không phải là log mọi dòng code.
Đây là log những sự kiện quan trọng trong vòng đời nghiệp vụ.
Nếu một bài bị kẹt, ta nhìn các mốc này để biết nó kẹt ở đâu.
Ví dụ:
Có submission_created nhưng không có grading_job_enqueued.
Vấn đề nằm giữa tạo submission và enqueue job.
Hoặc:
Có grading_job_started nhưng không có grading_result_saved.
Vấn đề nằm trong worker xử lý job.
Log tốt không cần kể từng hơi thở.
Nó cần kể đủ những đoạn quan trọng để điều tra.
---
50.4. Log xấu thường thiếu ngữ cảnh
Ví dụ log xấu:
ERROR timeout
Timeout cái gì?
Request nào?
Job nào?
Gọi service nào?
Timeout sau bao lâu?
Có retry không?
User nào bị ảnh hưởng?
Một log tốt hơn:
{
"level": "error",
"event": "ai_request_timeout",
"job_id": "job_789",
"submission_id": "sub_123",
"tenant_id": "school_a",
"provider": "gemini",
"model": "gemini-...",
"timeout_ms": 90000,
"attempt": 1,
"request_id": "req_abc"
}
Nhìn vào log này, ta có thể tìm job, submission, tenant, provider, attempt.
Ta có thể nối nó với metrics:
Có phải Gemini timeout tăng trên toàn hệ thống không?
Hay chỉ tenant school_a?
Hay chỉ model này?
Log tốt mở đường cho câu hỏi tiếp theo.
---
50.5. Structured log là gì?
Structured log là log có cấu trúc, thường là key-value hoặc JSON.
Thay vì:
Job job_789 failed for submission sub_123 because timeout
ta ghi:
{
"level": "error",
"event": "grading_job_failed",
"job_id": "job_789",
"submission_id": "sub_123",
"reason": "ai_timeout",
"attempt": 2
}
Con người đọc được.
Máy cũng query được.
Ví dụ ta có thể tìm:
event = grading_job_failed
reason = ai_timeout
attempt >= 2
Nếu log chỉ là text tự do, tìm kiếm vẫn được, nhưng khó lọc chính xác hơn.
Structured log đặc biệt hữu ích khi hệ thống lớn, nhiều service, nhiều worker.
---
50.6. Vì sao structured log quan trọng trong production?
Ở local, bạn nhìn terminal bằng mắt.
Production thì khác.
Log có thể đến từ:
- 5 API instances.
- 20 workers.
- Scheduler.
- Webhook handler.
- File processor.
- Notification worker.
Tất cả đẩy vào hệ thống log tập trung.
Nếu log có cấu trúc, bạn có thể hỏi:
Cho tôi tất cả log của job_789.
Cho tôi tất cả lỗi ai_timeout trong 30 phút qua.
Cho tôi các request của tenant school_a.
Cho tôi log có request_id=req_abc.
Nếu log chỉ là câu văn tự do, vẫn tìm được phần nào, nhưng dễ nhiễu.
Structured log giúp production bớt mù.
---
50.7. Log level là gì?
Log level là mức độ quan trọng của log.
Các mức phổ biến:
DEBUG
INFO
WARN
ERROR
FATAL/CRITICAL
Nhưng đừng học thuộc theo kiểu máy móc.
Hãy hiểu bằng câu hỏi:
Dòng này có cần ai chú ý không?
Nó là thông tin bình thường, dấu hiệu bất thường, hay lỗi thật?
Ví dụ:
INFO:
grading_job_started
grading_job_completed
WARN:
ai_request_retrying
slow_database_query
ERROR:
grading_job_failed
result_save_failed
DEBUG:
chi tiết chỉ cần khi điều tra sâu, thường tắt ở production hoặc bật có kiểm soát.
Log level giúp ta kiểm soát tiếng ồn.
Nếu mọi thứ đều ERROR, thì ERROR không còn ý nghĩa.
---
50.8. INFO không phải cứ càng nhiều càng tốt
INFO nên ghi các sự kiện bình thường nhưng có giá trị vận hành.
Ví dụ:
Job bắt đầu.
Job hoàn tất.
Webhook được xử lý.
File scan xong.
User đổi password.
API key được tạo.
Nhưng nếu mỗi request tạo 50 dòng INFO, production log sẽ rất ồn và tốn tiền.
Hãy hỏi:
Dòng INFO này sau này có giúp điều tra không?
Hay chỉ để người viết code thấy vui?
Nếu không giúp điều tra, có thể không nên log.
Hoặc chuyển sang DEBUG.
---
50.9. WARN là dấu hiệu cần chú ý, chưa chắc đã lỗi
WARN hợp với tình huống:
Hệ thống vẫn xử lý được, nhưng có dấu hiệu không bình thường.
Ví dụ:
AI request timeout lần đầu, chuẩn bị retry.
Database query chậm hơn 2 giây.
Webhook signature sai.
File upload gần chạm size limit.
Queue wait cao nhưng chưa fail.
WARN tốt giúp ta thấy vấn đề đang hình thành.
Nhưng nếu WARN quá nhiều, team sẽ bỏ qua.
WARN nên đáng chú ý.
Không nên dùng WARN cho mọi chuyện bình thường.
---
50.10. ERROR là lỗi thật cần điều tra
ERROR nên dành cho việc thất bại có ảnh hưởng rõ.
Ví dụ:
Không lưu được grading result.
Job fail sau khi hết retry.
Payment webhook xử lý thất bại.
Không cấp được signed URL vì permission service lỗi.
Database connection timeout.
ERROR không nhất thiết luôn cần người thức dậy lúc nửa đêm.
Nhưng ERROR nên có giá trị điều tra.
Nếu một lỗi đã được xử lý an toàn và không ảnh hưởng user, có thể WARN là đủ.
Nếu lỗi ảnh hưởng user hoặc dữ liệu, ERROR là hợp lý.
---
50.11. DEBUG dùng thế nào?
DEBUG dùng cho chi tiết sâu khi đang điều tra.
Ví dụ:
Prompt length.
Parsed rubric sections.
Intermediate scoring features.
Retry decision details.
Nhưng DEBUG ở production cần cẩn thận:
- Có thể rất nhiều.
- Tốn tiền log.
- Làm khó tìm log quan trọng.
- Dễ lộ dữ liệu nhạy cảm.
- Có thể ảnh hưởng performance.
Một cách tốt là:
Bình thường tắt DEBUG.
Cho phép bật DEBUG có thời hạn cho một service, tenant, request hoặc job cụ thể.
Không nên bật DEBUG toàn hệ thống rồi quên.
---
50.12. Correlation ID là gì?
Correlation ID là một id dùng để nối các log liên quan đến cùng một request hoặc flow.
Ví dụ:
request_id=req_abc
Khi API nhận request nộp bài, mọi log trong request đó có request_id=req_abc.
Nếu request tạo job, job nên được log cùng:
request_id=req_abc
job_id=job_789
Sau đó worker xử lý job log:
job_id=job_789
submission_id=sub_123
Nếu có thể, worker cũng giữ trace_id hoặc origin_request_id.
Nhờ vậy ta nối được:
Request tạo submission
-> job được enqueue
-> worker xử lý
-> result được lưu
Không có correlation ID, log nhiều service sẽ rời rạc như các trang sách bị xé ra.
---
50.13. Request ID và Job ID khác nhau
Request ID gắn với một HTTP request.
Job ID gắn với một job nền.
Một request có thể tạo một job.
Job có thể chạy sau đó rất lâu, ở process khác.
Ví dụ:
request_id=req_abc
submission_id=sub_123
job_id=job_789
Khi user báo lỗi, nếu có submission id hoặc job id, ta vẫn điều tra được dù request ban đầu đã kết thúc.
Với hệ thống có queue/worker, chỉ request id là chưa đủ.
Bạn cần cả job id và domain id như submission id.
---
50.14. Nên log những id nào?
Trong AI Judge, các id thường hữu ích:
request_id
job_id
submission_id
assignment_id
course_id
tenant_id
user_id nếu cần và an toàn
worker_id
provider_request_id nếu AI provider trả
Nhưng không phải log mọi thứ mọi lúc.
Nguyên tắc:
Log id giúp điều tra.
Không log dữ liệu nhạy cảm nếu chỉ cần id.
Ví dụ:
Log submission_id tốt hơn log toàn bộ nội dung bài làm.
Khi cần xem nội dung chi tiết, hệ thống có thể kiểm tra quyền rồi truy cập source of truth.
Log không nên trở thành bản sao dữ liệu private.
---
50.15. Log dữ liệu nhạy cảm là lỗi rất nặng
Log thường được gửi đến hệ thống tập trung.
Nhiều người hoặc tool có thể đọc log hơn đọc database production.
Vì vậy log không được chứa:
password
access token
refresh token
API key
JWT đầy đủ
reset token
private key
database URL có password
secret header
Với AI Judge, cũng cẩn thận với:
nội dung bài làm riêng tư
feedback private
prompt chứa dữ liệu học sinh
file content
thông tin cá nhân học sinh
Một khi secret vào log, hãy coi như secret đã lộ.
Bạn phải rotate.
Vì vậy tốt nhất là đừng log từ đầu.
---
50.16. Redaction là gì?
Redaction là che hoặc xóa phần nhạy cảm trước khi log.
Ví dụ:
Authorization: Bearer abcdef...
không nên log nguyên.
Có thể log:
Authorization: [REDACTED]
Hoặc với API key:
key_prefix=sk_live_1234
để nhận diện key nào, nhưng không lộ toàn bộ key.
Redaction nên nằm ở logger/middleware/hệ thống chung nếu có thể.
Không nên phụ thuộc từng developer nhớ tự che.
---
50.17. Log exception thế nào?
Khi có exception, log cần đủ để điều tra:
- Error type.
- Message an toàn.
- Stack trace.
- Request/job context.
- Service.
- Version.
Ví dụ:
{
"level": "error",
"event": "grading_result_save_failed",
"job_id": "job_789",
"submission_id": "sub_123",
"error_type": "DatabaseTimeout",
"error_message": "connection timeout",
"attempt": 2
}
Stack trace rất hữu ích cho developer.
Nhưng đừng trả stack trace cho user.
Và đừng để stack trace chứa secret do message lỗi lộ config.
Log nội bộ có thể chi tiết hơn response public.
---
50.18. Log ở boundary quan trọng
Boundary là nơi hệ thống nói chuyện với bên ngoài hoặc với thành phần khác.
Đây là nơi rất đáng log.
Ví dụ:
HTTP request vào API.
Webhook nhận từ payment/LMS.
Job được enqueue.
Worker bắt đầu job.
External AI API call.
Object storage upload/download.
Database transaction quan trọng.
Permission deny cho action nhạy cảm.
Vì lỗi thường xảy ra ở ranh giới:
- Request thiếu dữ liệu.
- Webhook signature sai.
- Queue publish lỗi.
- External API timeout.
- Permission sai.
- File không tồn tại.
Log ở boundary giúp thấy hệ thống nối với nhau thế nào.
---
50.19. Không cần log mọi function
Một phản xạ hay gặp:
Log vào đầu và cuối mọi function.
Cách này nhanh chóng tạo tiếng ồn.
Thay vì log mọi function, hãy log các sự kiện có nghĩa:
submission_created
grading_job_enqueued
ai_request_timeout
grading_result_saved
permission_denied
Function là chi tiết code.
Event là chuyện hệ thống đã làm.
Production thường cần biết chuyện hệ thống, không phải từng bước code nhỏ.
Trace có thể giúp xem bước chi tiết hơn khi cần.
---
50.20. Log quá nhiều cũng là vấn đề
Log quá nhiều gây:
- Tốn tiền.
- Tốn storage.
- Query chậm.
- Khó tìm dòng quan trọng.
- Dễ lộ dữ liệu.
- Có thể ảnh hưởng performance.
Một hệ thống log tốt không phải vì nó ghi mọi thứ.
Nó tốt vì khi có câu hỏi, tìm được câu trả lời.
Ví dụ câu hỏi:
Job này đang kẹt ở đâu?
Tại sao webhook này bị reject?
Tenant nào đang tạo nhiều lỗi?
AI provider timeout tăng từ khi nào?
Nếu log nhiều nhưng không trả lời được câu hỏi này, log đó chưa tốt.
---
50.21. Log quá ít cũng là vấn đề
Ngược lại, log quá ít khiến production mù.
Ví dụ chỉ log:
Server started.
Error occurred.
Khi user báo lỗi, ta không biết bắt đầu từ đâu.
Log tối thiểu nên có ở các flow chính:
- Request vào/ra ở mức tổng hợp.
- Lỗi có context.
- Job lifecycle.
- External API call result.
- Permission deny quan trọng.
- Action nhạy cảm/audit.
Điểm khó là cân bằng:
Đủ để điều tra.
Ít enough để không chìm trong tiếng ồn.
---
50.22. Access log và application log
Access log thường do proxy/app server ghi.
Nó nói:
method
path
status
duration
client_ip
user_agent
request_size
response_size
Ví dụ:
POST /submissions 201 240ms
Application log nói chuyện bên trong app:
submission_created
grading_job_enqueued
permission_denied
ai_request_timeout
Hai loại log bổ sung nhau.
Access log cho biết request tổng thể.
Application log cho biết logic bên trong request.
Khi debug, thường cần cả hai.
---
50.23. Audit log khác application log
Audit log ghi lại hành động quan trọng cần truy vết trách nhiệm.
Ví dụ:
teacher_score_overridden
student_data_exported
permission_granted
api_key_created
admin_accessed_submission
Audit log không chỉ để debug kỹ thuật.
Nó dùng để trả lời:
Ai đã làm hành động nhạy cảm này?
Lúc nào?
Với resource nào?
Lý do gì?
Application log có thể có retention ngắn hơn.
Audit log thường cần retention và bảo vệ nghiêm túc hơn.
Đừng trộn audit log nhạy cảm vào log debug hỗn loạn rồi sau này không tìm được.
---
50.24. Log retention
Retention là giữ log bao lâu.
Không phải log nào cũng cần giữ mãi.
Ví dụ:
Debug/application logs:
7-30 ngày tùy nhu cầu.
Security/audit logs:
lâu hơn, tùy compliance.
Metrics aggregate:
có thể giữ dài hơn raw logs.
Giữ quá ngắn thì khi incident phát hiện muộn, không còn dữ liệu.
Giữ quá dài thì tốn tiền và tăng rủi ro dữ liệu.
Retention nên dựa vào:
- Nhu cầu điều tra.
- Chi phí.
- Dữ liệu nhạy cảm.
- Yêu cầu khách hàng/pháp lý.
---
50.25. Log sampling
Với traffic lớn, có thể không cần log đầy đủ mọi request thành công.
Có thể:
Log 100% error.
Log 100% action nhạy cảm.
Log sample request thành công.
Log request chậm.
Ví dụ:
GET /health
được gọi liên tục, không nên tạo quá nhiều log vô ích.
Nhưng:
score_override
phải log đầy đủ.
Sampling phải theo giá trị điều tra.
Không sample bừa các sự kiện quan trọng.
---
50.26. Log và metrics không thay thế nhau
Nếu muốn biết:
Trong 5 phút qua có bao nhiêu job fail?
Metrics tốt hơn logs.
Nếu muốn biết:
Job job_789 fail vì sao?
Log tốt hơn metrics.
Đừng dùng log để tính mọi dashboard nếu metrics phù hợp hơn.
Đừng chỉ dùng metrics rồi không có log để điều tra case cụ thể.
Hai thứ có vai trò khác nhau.
---
50.27. Log và trace không thay thế nhau
Trace cho biết đường đi và thời gian từng bước.
Log cho biết sự kiện có ý nghĩa trong bước đó.
Ví dụ trace nói:
AI call mất 90 giây.
Log nói:
AI call timeout, attempt=1, retry_scheduled_in=30s.
Khi hệ thống đơn giản, log có thể đủ.
Khi nhiều service, trace giúp rất nhiều.
Nhưng trace không thay log nghiệp vụ quan trọng.
---
50.28. Centralized logging
Nếu production có nhiều instance, log nằm rải rác trên từng máy/container sẽ rất khó dùng.
Cần đưa log về một nơi tập trung.
Ví dụ:
- Cloud logging.
- ELK/OpenSearch.
- Loki.
- Datadog.
- Better Stack.
- Grafana Cloud.
Mục tiêu:
Tìm toàn bộ log của request/job trên mọi service ở một nơi.
Nếu phải SSH từng server để grep log, hệ thống sẽ rất mệt khi có sự cố.
Container/pod còn có thể bị xóa, log local mất.
Centralized logging là nền tảng production cơ bản.
---
50.29. Log trong container nên ra stdout/stderr
Với container, pattern phổ biến:
App ghi log ra stdout/stderr.
Platform/container runtime thu log.
Log collector gửi về hệ thống tập trung.
Không nên chỉ ghi vào file trong container rồi không collect.
Vì:
- Container có thể chết.
- File mất.
- Scale nhiều instance khó gom.
- Disk có thể đầy.
Nếu cần file log vì lý do đặc biệt, vẫn phải có log rotation và collection.
Nhưng mặc định, stdout/stderr là đường tốt cho containerized app.
---
50.30. Log trong AI Judge nên trông thế nào?
Một flow tốt có thể tạo log như sau.
Khi student nộp bài:
{
"level": "info",
"event": "submission_created",
"request_id": "req_abc",
"submission_id": "sub_123",
"assignment_id": "assign_12",
"tenant_id": "school_a",
"user_id": "user_9"
}
Khi job được tạo:
{
"level": "info",
"event": "grading_job_enqueued",
"request_id": "req_abc",
"job_id": "job_789",
"submission_id": "sub_123"
}
Khi worker gặp timeout:
{
"level": "warn",
"event": "ai_request_retrying",
"job_id": "job_789",
"submission_id": "sub_123",
"provider": "gemini",
"attempt": 1,
"reason": "timeout",
"retry_in_seconds": 30
}
Khi job hoàn tất:
{
"level": "info",
"event": "grading_job_completed",
"job_id": "job_789",
"submission_id": "sub_123",
"duration_ms": 123000,
"attempt": 2
}
Nhìn chuỗi này, ta hiểu chuyện.
Không cần log toàn bộ bài làm.
Không cần log toàn bộ prompt.
Chỉ cần log các mốc quan trọng và id để tra cứu.
---
50.31. Khi nào nên log permission denied?
Không phải mọi 403 đều cần ERROR.
Student thử vào submission không thuộc mình có thể là:
User bấm nhầm link.
Frontend bug.
Ai đó đang dò id.
Log có ích, nhưng level nên cân nhắc.
Ví dụ:
permission_denied action=submission.view user_id=user_9 submission_id=sub_999
Với action nhạy cảm như admin export, score override, API key management, nên log kỹ hơn và có thể đưa vào audit.
Nếu permission denied tăng đột biến, metrics/alert nên báo.
Một dòng deny riêng lẻ chưa chắc là lỗi hệ thống.
Nhưng nhiều deny bất thường có thể là dấu hiệu tấn công hoặc bug.
---
50.32. Khi nào không nên log body?
Request/response body thường chứa dữ liệu nhạy cảm hoặc rất lớn.
Ví dụ:
- Password.
- Token.
- File content.
- Bài làm.
- Prompt.
- Feedback.
- Thông tin học sinh.
Không nên log body mặc định.
Nếu cần debug một trường hợp khó, có thể:
- Log có kiểm soát.
- Chỉ staging.
- Redact field nhạy cảm.
- Bật trong thời gian ngắn.
- Chỉ cho request/job cụ thể.
Production body logging là con dao sắc.
Rất dễ tự cắt vào dữ liệu của mình.
---
50.33. Log message nên ổn định
Nếu log event name thay đổi liên tục:
job failed
grading failed
grade error
ai failed
query và dashboard sẽ khó.
Nên có event name ổn định:
grading_job_failed
ai_request_timeout
permission_denied
file_upload_completed
Message con người đọc có thể thay đổi.
Nhưng event nên ổn định để máy query.
Đây là lý do structured log có field event rất hữu ích.
---
50.34. Log phải đi cùng deploy version
Khi lỗi xuất hiện sau deploy, ta cần biết log đến từ version nào.
Nên log hoặc gắn metadata:
service_name
service_version
git_sha
environment
Ví dụ:
{
"service": "grading-worker",
"version": "git-a1b2c3",
"env": "production",
"event": "grading_job_failed"
}
Khi canary/rolling deploy, có thể hai version cùng chạy.
Nếu version mới lỗi nhiều hơn, metadata này giúp phát hiện.
---
50.35. Log thiết kế tốt từ đầu sẽ tiết kiệm rất nhiều
Logging thường bị làm sau cùng.
Nhưng khi production lỗi, log là thứ đầu tiên ta ước mình đã làm tốt.
Không cần hoàn hảo từ ngày đầu.
Nhưng các flow sống còn nên có log tốt ngay:
login
submission
file upload
grading job
AI provider call
result save
payment/webhook nếu có
permission/action nhạy cảm
Nếu sản phẩm cốt lõi là AI Judge, flow chấm bài phải có log tốt.
Đó là mạch máu của hệ thống.
---
50.36. Những lỗi logging phổ biến
Một số lỗi rất hay gặp:
Log thiếu request_id/job_id.
Log toàn text tự do, khó query.
Log quá nhiều DEBUG ở production.
Log password/token/body nhạy cảm.
Mọi lỗi đều ghi "Something went wrong".
Không phân biệt INFO/WARN/ERROR.
Không có centralized logging.
Không log worker, chỉ log web API.
Không gắn version deploy vào log.
Log quá nhiều nhưng không trả lời được câu hỏi incident.
Hầu hết không phải lỗi công cụ.
Là lỗi không nghĩ logging như một phần thiết kế hệ thống.
---
50.37. Checklist logging
Trước khi coi logging là ổn, hãy hỏi:
- Khi user báo lỗi, ta tìm request/job bằng gì?
- Log có structured không?
- Có request_id/job_id/submission_id không?
- Lỗi có error type và context không?
- Có log worker/job lifecycle không?
- Có log external API timeout/rate limit không?
- Có log action nhạy cảm/audit không?
- Có centralized logging không?
- Có log version deploy không?
- Có đang log secret/token/body nhạy cảm không?
- Có quá nhiều log vô ích không?
- Có retention phù hợp không?
Checklist này không cần biến thành nghi thức.
Nó là cách tự hỏi:
Khi production cháy, log này có giúp mình không?
---
50.38. Bảng chọn nhanh
| Tình huống | Cách logging thường hợp | |---|---| | Request bắt đầu/kết thúc | Access log hoặc request log tổng hợp | | Flow nghiệp vụ quan trọng | Log event có structured fields | | Job nền | Log job_id, status, attempt, duration | | External API lỗi | Log provider, error type, latency, attempt | | Permission deny | Log action/resource/user/tenant ở level phù hợp | | Action nhạy cảm | Audit log riêng hoặc event audit rõ | | Debug sâu | DEBUG có kiểm soát, không bật tràn lan | | Dữ liệu nhạy cảm | Redact hoặc không log | | Nhiều container/service | Centralized logging | | Incident sau deploy | Log service version/git sha |
---
50.39. Tóm tắt bằng AI Judge
Với AI Judge, logging tốt phải giúp trả lời:
Bài nộp đã tạo chưa?
File đã upload chưa?
Job chấm đã vào queue chưa?
Worker nào nhận job?
AI provider có timeout không?
Job retry mấy lần?
Kết quả đã lưu chưa?
User/tenant nào bị ảnh hưởng?
Deploy version nào đang chạy?
Log nên có:
request_id
job_id
submission_id
tenant_id
event
level
duration
attempt
error_type nếu lỗi
service_version
Log không nên chứa:
password
token
API key
nội dung bài làm private
toàn bộ prompt/feedback nhạy cảm
file content
Log tốt là dây nối các sự kiện rời rạc thành một câu chuyện có thể điều tra.
---
50.40. Kết luận của chương
Logging không phải là in thật nhiều dòng ra console.
Logging là thiết kế nhật ký vận hành để khi production có chuyện, ta biết chuyện gì đã xảy ra, với request/job/resource nào, ở bước nào, và bị ảnh hưởng ra sao.
Thông điệp cần nhớ:
> Log tốt là log có ngữ cảnh, có cấu trúc, có correlation ID, có level đúng, không lộ dữ liệu nhạy cảm, và đủ ít để tìm được thứ quan trọng.
Ở chương tiếp theo, ta sẽ nói về metrics: counter, gauge, histogram là gì, p50/p95/p99 giúp hiểu latency ra sao, và vì sao metrics là cách nhìn xu hướng hệ thống tốt hơn log trong rất nhiều trường hợp.