Chương 52. Tracing

Chương 50 nói về logs.

Chương 51 nói về metrics.

Chương này nói về tracing.

Nếu log trả lời:

Chuyện gì đã xảy ra?

Metrics trả lời:

Toàn hệ thống đang khỏe hay chậm theo thời gian?

Thì trace trả lời:

Một request hoặc một job đã đi qua những bước nào, mất bao lâu ở mỗi bước, và chậm/lỗi ở đâu?

Thông điệp chính của chương:

> Tracing giúp nhìn đường đi của một request/job qua nhiều bước và nhiều service. Nó đặc biệt hữu ích khi hệ thống có microservices, queue, worker, external API, hoặc những flow dài khó hiểu chỉ bằng log rời rạc.

---

52.1. Một tình huống: request "nộp bài" nhanh, nhưng điểm vẫn lâu

Student bấm nộp bài.

Frontend gọi:

POST /submissions

API trả rất nhanh:

202 Accepted
job_id=job_789

Nhìn metric API, mọi thứ đẹp:

POST /submissions p95 = 220ms
5xx gần như 0

Nhưng user vẫn nói:

Em chờ 15 phút chưa có điểm.

Log có thể cho thấy vài mốc.

Metrics có thể cho thấy queue backlog tăng.

Nhưng trace giúp nhìn một flow cụ thể:

Submit request
  -> auth check: 12ms
  -> permission check: 18ms
  -> save submission: 45ms
  -> create grading job: 20ms
  -> enqueue job: 8ms

Grading job
  -> wait in queue: 12m 30s
  -> download file: 400ms
  -> extract files: 1.2s
  -> call Gemini: 91s
  -> parse response: 100ms
  -> save result: 60ms

Bây giờ ta thấy rõ:

API không chậm.
AI call có lâu, nhưng không phải vấn đề lớn nhất.
Vấn đề lớn nhất là job chờ trong queue 12 phút 30 giây.

Trace biến cảm giác "chậm" thành một đường đi có thời gian từng đoạn.

---

52.2. Trace là gì?

Trace là bản ghi đường đi của một operation.

Operation có thể là:

  • Một HTTP request.
  • Một background job.
  • Một message được xử lý.
  • Một workflow.

Ví dụ trace của request:

GET /teacher/dashboard
  -> authenticate
  -> query courses
  -> query submissions summary
  -> query grading stats
  -> call billing service
  -> render response

Trace giúp ta thấy:

Tổng thời gian là bao lâu?
Bước nào tốn nhiều thời gian nhất?
Bước nào lỗi?
Request đi qua service nào?

Trace không thay log.

Trace là bản đồ đường đi.

Log là các ghi chú trên đường.

---

52.3. Span là gì?

Span là một đoạn trong trace.

Nếu trace là cả chuyến đi, span là từng chặng.

Ví dụ:

Trace: POST /submissions

Span 1: auth_check
Span 2: permission_check
Span 3: db_insert_submission
Span 4: enqueue_grading_job

Mỗi span thường có:

  • Tên.
  • Thời gian bắt đầu.
  • Thời gian kết thúc.
  • Duration.
  • Status: success/error.
  • Attributes/tags.
  • Parent span.

Ví dụ:

db_insert_submission duration=45ms status=ok

Span giúp ta không chỉ biết request chậm, mà biết chậm ở bước nào.

---

52.4. Parent và child span

Trace có cấu trúc cây.

Một span có thể chứa span con.

Ví dụ:

POST /submissions
  auth_check
  permission_check
  create_submission
    db_insert_submission
    db_insert_outbox_event
  enqueue_job

POST /submissions là span cha.

Các bước bên trong là span con.

Cấu trúc này giúp ta thấy:

Tổng request mất 220ms.
Trong đó create_submission mất 70ms.
Trong create_submission, db_insert_submission mất 45ms.

Không có trace, ta có thể chỉ thấy:

Request 220ms.

Trace cho ta kính hiển vi thời gian.

---

52.5. Trace ID là gì?

Trace ID là id chung cho toàn bộ trace.

Ví dụ:

trace_id=tr_abc

Mọi span trong cùng trace mang cùng trace id.

Logs cũng nên gắn trace id nếu có thể.

Ví dụ log:

{
  "event": "grading_job_enqueued",
  "trace_id": "tr_abc",
  "request_id": "req_123",
  "job_id": "job_789"
}

Nhờ vậy từ log ta có thể nhảy sang trace.

Từ trace ta có thể nhảy sang log.

Observability tốt là các tín hiệu nối được với nhau.

---

52.6. Trace context là gì?

Trace context là thông tin giúp truyền trace qua các service.

Ví dụ API Service gọi Submission Service.

Nếu không truyền trace context, Submission Service tạo trace mới.

Bạn sẽ thấy hai trace rời rạc.

Nếu có truyền trace context:

API Service span
  -> Submission Service span

cùng nằm trong một trace.

Qua HTTP, trace context thường được truyền bằng header.

Ví dụ theo chuẩn W3C:

traceparent: ...

Bạn không cần nhớ cú pháp.

Chỉ cần hiểu:

> Muốn trace đi qua nhiều service, ta phải truyền ngữ cảnh trace qua boundary.

---

52.7. Vì sao tracing quan trọng khi có nhiều service?

Trong monolith, request thường nằm trong một process.

Log và profiler có thể đủ cho nhiều vấn đề.

Trong microservices, một request có thể đi:

API Gateway
-> Auth Service
-> Course Service
-> Submission Service
-> Grading Service
-> Billing Service

Nếu user nói:

Dashboard chậm.

Bạn cần biết:

Chậm ở gateway?
Auth?
Course service?
Submission query?
Billing call?
Network giữa service?

Trace cho thấy đường đi qua các service.

Không có trace, mỗi service chỉ thấy một mảnh.

Team dễ đổ lỗi qua lại:

API nói database.
Database nói query bình thường.
Billing nói không phải tôi.
Gateway nói upstream timeout.

Trace giúp đưa mọi mảnh lên cùng một bản đồ.

---

52.8. Trace trong monolith có cần không?

Có thể vẫn cần.

Monolith lớn cũng có nhiều bước:

  • Middleware.
  • Auth.
  • Permission.
  • Service layer.
  • Database queries.
  • Cache.
  • External API.
  • File storage.

Ví dụ:

GET /teacher/dashboard
  auth: 10ms
  permission: 20ms
  query courses: 50ms
  query stats: 2.5s
  render: 30ms

Trace giúp thấy query stats là điểm chậm.

Không cần chờ đến microservices mới dùng tracing.

Nhưng tracing càng có giá trị khi boundary càng nhiều.

---

52.9. Trace qua HTTP tương đối dễ

HTTP request có header.

Khi Service A gọi Service B, A có thể gửi trace context qua header.

Service B nhận header và tạo span con.

Luồng:

Client
-> API Gateway
-> API Service
-> Submission Service

Trace context đi theo request.

Nhiều framework/library có instrumentation tự động cho HTTP client/server.

Vì vậy tracing qua HTTP thường dễ hơn tracing qua queue.

---

52.10. Trace qua queue khó hơn

Queue làm flow bị tách thời gian.

Ví dụ:

HTTP request tạo job lúc 10:00.
Worker xử lý job lúc 10:10.

Không còn một call stack liền mạch.

Nếu không làm gì, trace request và trace worker sẽ tách rời.

Muốn nối, khi enqueue job cần lưu trace context hoặc ít nhất lưu:

trace_id
origin_request_id
job_id
submission_id

Worker đọc job và tiếp tục trace hoặc tạo trace mới có link tới trace cũ.

Đây là điểm nhiều hệ thống bỏ sót.

Kết quả là:

API trace kết thúc ở enqueue.
Worker trace bắt đầu ở job processing.
Không biết chúng liên quan với nhau.

Với AI Judge, trace qua queue rất quan trọng.

---

52.11. Trace link là gì?

Đôi khi worker job không phải là span con trực tiếp của HTTP request.

Vì nó chạy sau, có thể retry, có thể được xử lý bởi worker khác.

Ta có thể dùng khái niệm link:

Worker trace link tới request trace ban đầu.

Nói dễ hiểu:

Hai trace không nằm cùng một cây, nhưng có quan hệ.

Ví dụ:

Trace A:
  POST /submissions -> enqueue job_789

Trace B:
  process job_789 -> call AI -> save result

Trace B links to Trace A.

Không cần dùng thuật ngữ quá sâu ngay.

Điểm chính:

> Queue làm trace không còn thẳng như HTTP. Ta phải chủ động nối bằng job id, trace context hoặc trace link.

---

52.12. Trace giúp thấy thời gian chờ trong queue

Một lỗi observability phổ biến:

Chỉ đo thời gian worker xử lý job.

Ví dụ:

worker_processing_time = 95s

Nghe có vẻ ổn nếu AI call mất 90s.

Nhưng user có thể chờ 15 phút.

Vì còn:

queue_wait_time = 13m 30s

Trace hoặc metrics nên tách:

time_in_queue
time_processing
end_to_end_time

Với AI Judge:

Student care about end_to_end_time.
Engineer care about queue_wait and processing breakdown.

Trace giúp thấy cả hai.

---

52.13. Trace và database query

Trace có thể instrument database queries.

Ví dụ:

GET /teacher/dashboard
  db query courses: 30ms
  db query assignments: 80ms
  db query grading_stats: 2.8s

Ta không cần đoán query nào chậm.

Trace chỉ ra.

Nhưng cẩn thận:

  • Không ghi full query chứa dữ liệu nhạy cảm nếu không cần.
  • Không tạo quá nhiều span cho từng query nhỏ nếu chi phí cao.
  • Nên normalize query hoặc dùng query name.

Ví dụ tốt:

db.operation=select
db.statement_name=teacher_dashboard_stats
duration=2.8s

Không nhất thiết log full SQL với tham số nhạy cảm.

---

52.14. Trace và external API

External API là nơi trace rất hữu ích.

Ví dụ worker:

download_submission_file: 300ms
call_gemini: 91s
save_result: 60ms

Nếu job chậm, trace cho thấy AI call chiếm gần hết thời gian.

Nếu AI call retry:

call_gemini attempt=1 timeout 90s
backoff 30s
call_gemini attempt=2 success 80s

Trace giúp nhìn retry có làm job dài hơn thế nào.

Với AI product, trace external API call rất đáng giá.

Nhưng không nên ghi full prompt/response nhạy cảm vào trace.

Chỉ ghi metadata an toàn:

provider
model
attempt
status
duration
token_count
cost estimate nếu cần

---

52.15. Trace và cache

Trace cũng giúp thấy cache có hoạt động không.

Ví dụ:

get_course_config
  redis_get: 5ms cache_hit=true

Hoặc:

get_teacher_dashboard
  cache_lookup: 4ms cache_hit=false
  db_query_stats: 2.3s
  cache_write: 8ms

Nếu latency cao vì cache miss nhiều, trace giúp thấy.

Metrics cho biết cache hit rate toàn hệ thống.

Trace cho biết request cụ thể miss ở đâu.

---

52.16. Trace và permission

Permission check có thể chậm hoặc sai.

Ví dụ:

permission_check: 1.8s

Nếu request detail submission chậm, ta có thể nghĩ database chính chậm.

Nhưng trace cho thấy:

permission check gọi nhiều query membership.

Khi authorization phức tạp, trace giúp hiểu permission layer có đang thành bottleneck không.

Nhưng đừng ghi dữ liệu permission nhạy cảm quá chi tiết vào trace.

Ghi đủ:

action=submission.view
resource_type=submission
result=allow/deny
duration

---

52.17. Sampling trong tracing

Trace có thể tốn chi phí nếu lưu mọi request.

Nhiều hệ thống dùng sampling:

Trace một phần request bình thường.
Trace toàn bộ request lỗi.
Trace toàn bộ request chậm.
Trace flow quan trọng.

Ví dụ:

Sample 10% GET bình thường.
Sample 100% POST /submissions.
Sample 100% failed grading jobs.

Sampling nên thông minh.

Nếu sample quá ít, bạn không thấy lỗi hiếm.

Nếu sample tất cả, chi phí có thể cao.

Với flow quan trọng như chấm bài, nên trace nhiều hơn ít nhất trong giai đoạn đầu.

---

52.18. Trace không nên chứa dữ liệu nhạy cảm

Trace attributes dễ bị lạm dụng.

Không nên đưa vào trace:

  • Password.
  • Token.
  • API key.
  • Full prompt có dữ liệu học sinh.
  • Nội dung bài làm.
  • Feedback private.
  • File content.
  • Email nếu không cần.

Nên đưa metadata an toàn:

submission_id
job_id
tenant_id
provider
model
duration
status
attempt
error_type

Trace giúp điều tra đường đi.

Nó không nên trở thành bản sao dữ liệu private.

---

52.19. Trace UI nhìn như thế nào?

Một trace viewer thường hiển thị waterfall.

Nghĩa là các span được xếp theo thời gian.

Ví dụ:

POST /teacher/dashboard  3.2s
|-- auth_check           10ms
|-- permission_check     40ms
|-- query_courses        80ms
|-- query_stats          2.8s
|-- format_response      30ms

Nhìn vào là thấy:

query_stats chiếm gần hết thời gian.

Waterfall rất trực quan.

Nó giúp người mới hiểu performance hơn rất nhiều so với chỉ đọc log.

---

52.20. Trace và N+1 query

Trace rất giỏi phát hiện N+1 query.

Ví dụ dashboard load một danh sách 100 submissions.

Trace hiện:

query submissions: 80ms
query user for submission 1: 5ms
query user for submission 2: 5ms
...
query user for submission 100: 5ms

Mỗi query nhỏ.

Nhưng tổng cộng nhiều.

Nếu chỉ nhìn slow query, không query nào quá chậm.

Nhưng trace cho thấy pattern lặp.

Giải pháp có thể là:

  • Eager load.
  • Batch query.
  • Join hợp lý.
  • Data loader.

Trace giúp thấy cấu trúc chậm, không chỉ một query chậm.

---

52.21. Trace và retry

Retry làm flow phức tạp hơn.

Trace có thể cho thấy:

call_ai attempt 1: timeout 90s
backoff: 30s
call_ai attempt 2: timeout 90s
backoff: 60s
call_ai attempt 3: success 80s

Tổng job mất:

90 + 30 + 90 + 60 + 80 = 350s

Nếu user hỏi sao job lâu, trace trả lời rất rõ.

Nếu retry storm xảy ra, metrics cho thấy toàn hệ thống retry tăng.

Trace cho thấy một job cụ thể retry như thế nào.

---

52.22. Trace và timeout

Timeout ở proxy/API/worker/external API có thể khó debug.

Trace giúp thấy:

Gateway timeout at 30s.
Backend span kept running to 45s.
Database query took 40s.

Hoặc:

Worker timeout at 120s.
AI call took 118s.
Save result never happened.

Nếu không trace, ta có thể chỉ thấy user nhận 504.

Trace cho thấy request chết ở biên nào.

---

52.23. OpenTelemetry là gì ở mức khái niệm?

OpenTelemetry là một bộ chuẩn và công cụ mở để thu thập telemetry:

  • Traces.
  • Metrics.
  • Logs.

Với tracing, OpenTelemetry giúp app tạo spans và truyền trace context theo cách chuẩn.

Ý tưởng:

Instrument app bằng OpenTelemetry.
Gửi traces đến backend như Jaeger, Tempo, Honeycomb, Datadog, New Relic, Grafana Cloud...

Điểm hay là giảm phụ thuộc vào một vendor ngay từ code.

Bạn không cần hiểu toàn bộ OpenTelemetry ngay.

Chỉ cần hiểu:

> Nó là cách phổ biến để chuẩn hóa việc tạo, truyền và xuất trace/metric/log trong hệ thống hiện đại.

---

52.24. Auto instrumentation và manual instrumentation

Auto instrumentation là thư viện tự tạo span cho thứ phổ biến:

  • HTTP server.
  • HTTP client.
  • Database client.
  • Redis.
  • Message queue.

Manual instrumentation là bạn tự thêm span cho logic nghiệp vụ.

Ví dụ:

grade_submission
build_prompt
call_ai_provider
parse_ai_feedback
save_grading_result

Auto instrumentation cho bạn nền.

Manual instrumentation cho bạn ý nghĩa domain.

Nếu chỉ có auto, trace có thể nói:

HTTP call, DB query.

Nhưng không nói:

Đây là bước build rubric prompt.
Đây là bước parse feedback.

Với hệ thống nghiệp vụ quan trọng, cần cả hai.

---

52.25. Trace tốt bắt đầu từ naming tốt

Span name nên rõ.

Không tốt:

process
handle
do_work
call

Tốt hơn:

create_submission
enqueue_grading_job
download_submission_file
call_ai_provider
save_grading_result

Tên span nên mô tả bước có ý nghĩa.

Khi nhìn waterfall, người đọc phải hiểu hệ thống đang làm gì.

Trace không chỉ cho máy.

Trace là công cụ để con người đọc đường đi.

---

52.26. Trace attributes nên có gì?

Attributes là metadata của span.

Ví dụ cho AI call:

provider=gemini
model=...
attempt=1
status=timeout
input_tokens=...
output_tokens=...

Ví dụ cho job:

job_id=job_789
job_type=grading
submission_id=sub_123
tenant_id=school_a
attempt=2

Chọn attributes như chọn log fields:

Đủ để điều tra.
Không lộ dữ liệu nhạy cảm.
Không tạo cardinality quá nguy hiểm nếu backend trace tính phí/đánh index theo field.

---

52.27. Trace không thay thiết kế tốt

Trace giúp thấy vấn đề.

Nó không tự sửa vấn đề.

Nếu trace cho thấy:

queue wait = 20 phút

Bạn vẫn cần giải quyết bằng:

  • Tăng worker.
  • Tối ưu job.
  • Tách queue ưu tiên.
  • Kiểm soát retry.
  • Tính lại quota AI.

Nếu trace cho thấy:

permission_check = 2s

Bạn vẫn cần tối ưu query/cache/rule.

Observability là đèn pin.

Không phải cái búa tự sửa nhà.

---

52.28. Khi nào nên ưu tiên tracing?

Tracing đặc biệt đáng ưu tiên khi:

  • Có nhiều service.
  • Có queue/worker.
  • Request đi qua nhiều dependency.
  • Latency khó giải thích.
  • Có external API chậm.
  • Có N+1 query.
  • Có retry/timeout phức tạp.
  • User phàn nàn "chậm" nhưng metrics chưa đủ chỉ ra nguyên nhân.

Nếu app còn rất nhỏ, logs + metrics tốt có thể đủ lúc đầu.

Nhưng với AI Judge có API + queue + worker + AI provider, tracing rất đáng cân nhắc sớm cho flow chấm bài.

---

52.29. AI Judge: trace một flow chấm bài

Một trace tốt cho AI Judge có thể nhìn như:

submit_assignment_request
  authenticate_user
  check_assignment_permission
  create_submission
  create_grading_job
  enqueue_grading_job

process_grading_job
  wait_in_queue
  download_submission_file
  extract_submission
  load_rubric
  build_ai_prompt
  call_ai_provider
  parse_ai_response
  save_grading_result
  publish_grading_completed_event

Nhìn trace này, ta biết:

User chờ lâu vì queue?
Hay vì download file?
Hay vì AI provider?
Hay vì parse response?
Hay vì save result?

Không cần đoán.

---

52.30. AI Judge: trace qua queue cần gì?

Khi API tạo job, job payload nên có đủ context an toàn:

job_id
submission_id
tenant_id
origin_request_id
trace_context hoặc trace_id/link
created_at

Worker khi nhận job:

Tạo span process_grading_job.
Gắn job_id/submission_id.
Nối với trace/request ban đầu nếu có.
Đo queue wait bằng now - created_at.

Nếu làm vậy, ta nối được web request và worker processing.

Nếu không, production sẽ có hai mảnh rời:

API nói đã enqueue.
Worker nói đã xử lý job.
Nhưng nối hai chuyện rất mệt.

---

52.31. Những lỗi tracing phổ biến

Một số lỗi hay gặp:

Chỉ trace HTTP, quên worker/queue.
Không truyền trace context qua service.
Span name quá chung chung.
Trace chứa dữ liệu nhạy cảm.
Không sample hợp lý nên quá tốn.
Không trace request lỗi/chậm.
Chỉ có auto instrumentation, không có span nghiệp vụ.
Không gắn job_id/submission_id.
Không nối trace với log.
Nhìn trace nhưng không có metrics để biết vấn đề lan rộng không.

Tracing là một phần của observability.

Nó mạnh nhất khi đi cùng logs và metrics.

---

52.32. Checklist tracing

Hỏi:

  • Request quan trọng có trace không?
  • Worker/job quan trọng có trace không?
  • Có truyền trace context qua HTTP không?
  • Có nối flow qua queue không?
  • Span name có dễ hiểu không?
  • Span có đo duration của bước quan trọng không?
  • Có attributes đủ để điều tra không?
  • Có tránh dữ liệu nhạy cảm không?
  • Có sample request lỗi/chậm đầy đủ không?
  • Có nối trace với logs bằng trace_id/job_id không?
  • Trace có giúp trả lời "chậm ở đâu" không?

Nếu trace không giúp trả lời "chậm ở đâu", nó cần được thiết kế lại.

---

52.33. Bảng chọn nhanh

| Câu hỏi | Trace giúp thế nào | |---|---| | Request chậm ở đâu? | Xem span nào chiếm nhiều thời gian | | Service nào gây chậm? | Xem waterfall qua service | | Job chờ hay xử lý lâu? | Tách queue wait và processing time | | AI provider có chiếm thời gian không? | Span call_ai_provider | | Có N+1 query không? | Thấy nhiều span DB lặp | | Retry làm job dài không? | Thấy các attempt và backoff | | Timeout ở lớp nào? | Thấy span kết thúc/lỗi ở proxy/app/dependency | | Log của trace này ở đâu? | Dùng trace_id/request_id/job_id | | Queue có làm đứt trace không? | Truyền trace context/link qua job payload |

---

52.34. Tóm tắt bằng AI Judge

Với AI Judge:

Metrics nói:
  chấm bài hôm nay chậm trên toàn hệ thống.

Logs nói:
  job_789 timeout ở Gemini attempt 1.

Trace nói:
  job_789 chờ queue 12 phút, gọi AI 91 giây, lưu result 60ms.

Ba góc nhìn này bổ sung nhau.

Trace đặc biệt hữu ích cho flow:

student submit
-> API tạo submission
-> enqueue job
-> worker xử lý
-> gọi AI
-> lưu kết quả
-> thông báo user

Điểm khó nhất là queue.

Muốn trace qua queue, phải truyền hoặc liên kết context bằng job id, trace id, origin request id.

---

52.35. Kết luận của chương

Tracing giúp ta nhìn đường đi của một request hoặc job.

Trace là cả hành trình.

Span là từng chặng trong hành trình.

Trace context giúp nối hành trình qua service boundary.

Qua HTTP, việc nối trace tương đối tự nhiên.

Qua queue, ta phải chủ động truyền context hoặc tạo link.

Thông điệp cần nhớ:

> Trace trả lời câu hỏi "chậm/lỗi ở bước nào trong hành trình này?". Khi hệ thống có nhiều service, worker, queue và external API, tracing giúp biến một flow mơ hồ thành một bản đồ thời gian có thể đọc được.

Ở chương tiếp theo, ta sẽ nói về alerting và SLO: alert tốt là alert cần hành động, vì sao alert quá nhiều làm team tê liệt, SLI/SLO/SLA khác nhau thế nào, và vì sao nên báo động theo triệu chứng người dùng thay vì từng lỗi nhỏ rời rạc.