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ỏiTrace 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.