Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn là kiến trúc sư trưởng của một sàn thương mại điện tử ở TP.HCM. 2 giờ sáng ngày 12/12 — đúng đợt sale lớn nhất năm — điện thoại bạn rung liên tục. Khách hàng phàn nàn "bấm thanh toán mà quay mãi không xong". Bộ phận vận hành hỏi: "Hệ thống có sập không anh?". Bạn mở dashboard lên: CPU ổn, RAM ổn, server vẫn "xanh". Nhưng đơn hàng thì vẫn đang rớt. Câu hỏi không phải "server còn sống không" mà là "chuyện gì đang thực sự xảy ra bên trong hệ thống của tôi?".
Đó chính là khoảng cách giữa monitoring và observability. Monitoring trả lời cho những câu hỏi bạn đã biết trước ("CPU có quá 80% không?"). Observability trả lời cho những câu hỏi bạn chưa từng nghĩ tới khi sự cố xảy ra ("tại sao 3% request thanh toán từ khách dùng Momo qua mạng Viettel lại chậm hơn 8 giây?"). Trong một hệ thống phân tán hiện đại — với hàng chục service, hàng trăm instance, gọi chéo lẫn nhau — bạn không thể debug bằng cách SSH vào từng máy đọc log. Bạn cần một hệ thống giúp bạn suy ra trạng thái bên trong của hệ thống chỉ từ các tín hiệu nó phát ra bên ngoài. Đó là định nghĩa gốc của observability, mượn từ lý thuyết điều khiển học.
Với vai trò người ra quyết định kiến trúc, observability không phải là "tính năng thêm vào cuối dự án". Nó là một thuộc tính kiến trúc (architectural concern) phải được thiết kế từ đầu, ngang hàng với bảo mật hay khả năng mở rộng. Một hệ thống không quan sát được là một hệ thống bạn không thể vận hành, không thể tối ưu, và không thể tin tưởng ở quy mô lớn. Bài này giúp bạn nắm vững ba trụ cột nền tảng — Logs, Metrics, Traces — để đưa ra các quyết định thiết kế đúng đắn.
Khái niệm cốt lõi
Observability được xây dựng trên ba loại dữ liệu telemetry, thường gọi là 3 trụ cột (3 pillars). Mỗi trụ cột trả lời một loại câu hỏi khác nhau, và sức mạnh thực sự đến từ việc kết hợp cả ba.
Trụ cột 1 — Logs: bản ghi sự kiện rời rạc
Log là bản ghi văn bản của một sự kiện rời rạc xảy ra tại một thời điểm cụ thể. "User 8821 đăng nhập lúc 14:03:22", "Không kết nối được database, timeout sau 5s", "Đơn hàng #VN10293 chuyển sang trạng thái PAID". Log là dữ liệu telemetry lâu đời và trực quan nhất — lập trình viên nào cũng từng viết print() hay logger.info().
Điểm mấu chốt trong thiết kế hiện đại là structured logging (log có cấu trúc). Thay vì ghi một dòng text tự do:
[ERROR] Payment failed for order 10293 user 8821
bạn ghi dưới dạng JSON có các trường rõ ràng:
{"level":"error","event":"payment_failed","order_id":"10293","user_id":"8821","gateway":"momo","latency_ms":8210,"trace_id":"a1b2c3","timestamp":"2026-06-27T14:03:22Z"}
Sự khác biệt là một trời một vực. Với log có cấu trúc, bạn có thể truy vấn: "cho tôi tất cả payment_failed của gateway momo trong 1 giờ qua có latency_ms > 5000". Với log dạng text, bạn chỉ có grep và nước mắt.
Điểm mạnh của log: giàu ngữ cảnh, chi tiết đến từng sự kiện, tuyệt vời để điều tra một trường hợp cụ thể. Điểm yếu: tốn kém về lưu trữ và khó tổng hợp. Ở quy mô hàng tỷ dòng log mỗi ngày, chi phí lưu trữ và index trở thành gánh nặng thực sự — đây là lý do bạn phải quyết định sampling (chỉ giữ một phần) và retention (giữ bao lâu).
Trụ cột 2 — Metrics: số đo theo thời gian
Metric là giá trị số được đo lường và tổng hợp theo thời gian. Khác với log ghi lại từng sự kiện, metric nén thông tin thành các con số: "số request mỗi giây", "tỷ lệ lỗi", "độ trễ p99", "số lượng đơn hàng đang chờ xử lý", "phần trăm bộ nhớ đã dùng".
Đặc trưng quan trọng của metric là nó là time series — một chuỗi cặp (thời điểm, giá trị). Vì đã được tổng hợp trước, metric cực kỳ hiệu quả về lưu trữ và truy vấn. Bạn có thể lưu metric của cả một năm với chi phí bằng một phần nhỏ so với log tương ứng. Đây là lý do metric là nền tảng cho dashboard và alerting (cảnh báo).
Các loại metric cơ bản bạn cần biết:
- Counter: chỉ tăng, ví dụ tổng số request đã xử lý.
- Gauge: lên xuống tự do, ví dụ số kết nối đang mở.
- Histogram: phân bố giá trị, dùng để tính các percentile như p50, p95, p99 của độ trễ (bạn đã học ở Bài 11).
Trụ cột 3 — Traces: dòng chảy của một request xuyên hệ thống
Trace theo dõi hành trình của một request duy nhất khi nó đi qua nhiều service. Trong kiến trúc microservices, một lần khách bấm "Thanh toán" có thể kích hoạt: API Gateway → Order Service → Inventory Service → Payment Service → gọi ra cổng Momo → Notification Service. Nếu tổng thời gian là 8 giây, câu hỏi sống còn là: 8 giây đó dành ở đâu?
Trace trả lời câu hỏi đó. Mỗi trace gồm nhiều span — mỗi span là một đoạn công việc (một lần gọi service, một truy vấn database) với thời gian bắt đầu, thời gian kết thúc, và quan hệ cha-con với các span khác. Tất cả span của cùng một request được nối với nhau qua một trace_id chung, được truyền (propagate) qua từng lần gọi service.
Khi nhìn một trace được vẽ dạng thác nước (waterfall), bạn thấy ngay: "Order Service mất 200ms, Inventory 150ms, nhưng lần gọi ra cổng Momo mất 7.4 giây — thủ phạm là đây". Đây chính là sức mạnh mà log và metric không thể cung cấp: định vị điểm nghẽn trong một hệ thống phân tán.
Ba trụ cột bổ trợ nhau như thế nào
Đừng nghĩ ba trụ cột là ba lựa chọn thay thế. Chúng là ba góc nhìn của cùng một thực tại, và quy trình điều tra lý tưởng đi qua cả ba:
- Metric báo động: "tỷ lệ lỗi thanh toán vọt lên 12% lúc 14:00".
- Trace khoanh vùng: "các request lỗi đều tắc ở span gọi Payment Service, cụ thể là gateway Momo".
- Log giải thích tận gốc: dòng log
{"event":"payment_failed","error":"connection_pool_exhausted"}— hết kết nối trong pool.
trace_id vào cả log lẫn metric để bạn có thể nhảy từ một biểu đồ metric bất thường sang đúng trace, rồi từ trace sang đúng dòng log. Chuẩn OpenTelemetry (OTel) ra đời chính để chuẩn hóa việc thu thập và tương quan cả ba loại dữ liệu này, giúp bạn không bị khóa chặt vào một nhà cung cấp.Tình huống thực tế
Tình huống 1 — Sàn TMĐT và sự cố "âm thầm rớt đơn" đêm 12/12
Một sàn thương mại điện tử giả định tại Việt Nam, gọi là ShopViet, chạy kiến trúc microservices với khoảng 40 service. Đêm sale 12/12, đội vận hành nhận báo cáo khách không thanh toán được, nhưng mọi dashboard hạ tầng đều xanh: CPU, RAM, disk đều bình thường.
Vấn đề nằm ở chỗ ShopViet chỉ có metric hạ tầng (CPU, RAM) mà thiếu metric nghiệp vụ và trace. Server "khỏe" nhưng nghiệp vụ đang chết. Sau sự cố, họ bổ sung ba thứ: một metric nghiệp vụ orders_completed_per_minute (khi con số này rớt từ 300 xuống 40, chuông reo ngay lập tức, không cần chờ khách phàn nàn), distributed tracing xuyên toàn bộ luồng checkout, và structured log có trace_id.
Lần sale tiếp theo, khi độ trễ tăng, metric nghiệp vụ báo động sau 30 giây. Trace chỉ thẳng vào Payment Service. Log tiết lộ connection_pool_exhausted — số kết nối tới database của Payment Service bị giới hạn ở 20, không đủ cho lưu lượng sale. Họ nâng pool và thêm circuit breaker. Bài học: metric hạ tầng "xanh" không có nghĩa hệ thống khỏe. Phải đo cả tín hiệu nghiệp vụ, và phải có đủ ba trụ cột để đi từ "có gì đó sai" đến "sai chính xác chỗ nào" trong vài phút thay vì vài giờ.
Tình huống 2 — Grab và bài toán chi phí observability ở quy mô Đông Nam Á
Một siêu ứng dụng như Grab, phục vụ hàng chục triệu người dùng khắp Đông Nam Á, tạo ra khối lượng telemetry khổng lồ — có thể lên tới hàng petabyte log mỗi tháng nếu ghi tất cả. Nếu giữ 100% log ở tốc độ đó, hóa đơn cho hệ thống quan sát có thể còn đắt hơn cả chính hệ thống production.
Bài toán kiến trúc ở đây không phải "quan sát nhiều nhất có thể" mà là "quan sát đủ thông minh". Cách tiếp cận điển hình: dùng metric làm lớp phòng thủ đầu tiên vì nó rẻ (giữ đủ mọi metric quan trọng với độ phân giải cao). Với trace, áp dụng tail-based sampling — chỉ giữ lại 100% các trace có lỗi hoặc chậm bất thường, còn các trace bình thường thì lấy mẫu 1%. Với log, hạ mức log ở production (không ghi DEBUG), rút ngắn thời gian lưu trữ log chi tiết xuống 7–14 ngày, và đẩy dữ liệu cũ sang lưu trữ lạnh giá rẻ.
Bài học: ở quy mô lớn, observability là một bài toán trade-off chi phí trực tiếp. Người ra quyết định kiến trúc phải cân giữa độ chi tiết của tín hiệu và ngân sách. Nguyên tắc hữu ích: metric thì đầy đủ và giữ lâu; trace và log thì lấy mẫu thông minh, ưu tiên giữ những gì bất thường vì đó mới là thứ bạn cần khi điều tra.
Tình huống 3 — Ngân hàng số và yêu cầu truy vết cho tuân thủ
Một ngân hàng số tại Việt Nam triển khai hệ thống chuyển tiền. Ở đây, observability không chỉ để debug — nó gắn với tuân thủ (compliance) và kiểm toán (audit). Khi một giao dịch bị khiếu nại, đội vận hành phải tái dựng chính xác điều gì đã xảy ra với giao dịch đó, qua mọi service, kèm dấu thời gian chính xác.
Họ thiết kế mỗi giao dịch mang một transaction_id được truyền xuyên suốt, đóng vai trò như trace_id nghiệp vụ. Structured log ghi lại từng bước chuyển trạng thái (khởi tạo, xác thực OTP, trừ tiền, cộng tiền, hoàn tất) kèm transaction_id. Nhưng có một ràng buộc đặc thù: log không được chứa dữ liệu nhạy cảm — số thẻ đầy đủ, mật khẩu, số dư. Đội phát triển phải masking (che) các trường này trước khi ghi log, nếu không chính hệ thống observability lại trở thành lỗ hổng rò rỉ dữ liệu.
Bài học: trong các lĩnh vực có quản lý chặt (tài chính, y tế), observability đan xen với bảo mật và tuân thủ. Quyết định thiết kế phải bao gồm: log gì, không log gì, giữ bao lâu (đôi khi luật yêu cầu giữ tối thiểu nhiều năm), và ai được xem. Observability không phải chỉ là công cụ kỹ thuật, nó còn là công cụ pháp lý và niềm tin.
Hướng dẫn từng bước
Dưới đây là cách tiếp cận thực dụng để đưa observability vào một hệ thống, dành cho người ra quyết định kiến trúc:
- Xác định câu hỏi trước, công cụ sau. Đừng bắt đầu bằng "ta nên dùng Prometheus hay Datadog". Bắt đầu bằng: "Khi hệ thống có sự cố, những câu hỏi nào ta cần trả lời trong 5 phút?". Danh sách câu hỏi đó định hình tín hiệu bạn cần thu thập.
- Bắt đầu với metric và các chỉ số vàng. Với mỗi service, đo tối thiểu bốn tín hiệu vàng (Golden Signals của Google SRE): Latency (độ trễ), Traffic (lưu lượng), Errors (tỷ lệ lỗi), Saturation (mức bão hòa tài nguyên). Bổ sung ít nhất một metric nghiệp vụ cốt lõi cho mỗi luồng quan trọng (đơn hàng/phút, giao dịch thành công/phút).
- Chuẩn hóa structured logging ngay từ đầu. Định nghĩa một schema log chung cho toàn tổ chức: luôn có
timestamp,level,service,trace_id,event. Ghi log dạng JSON. Đặt mức log hợp lý (production dùngINFOtrở lên).
- Triển khai distributed tracing với chuẩn mở. Dùng OpenTelemetry để instrument code. Đảm bảo
trace_idđược truyền qua mọi ranh giới service (HTTP header, message header). Đây là phần dễ bị bỏ sót nhất và khó thêm vào sau nhất.
- Tương quan ba trụ cột. Nhúng
trace_idvào log. Cấu hình để từ một điểm bất thường trên dashboard metric, bạn có thể nhảy sang trace tương ứng, rồi sang log. Đây là thứ biến ba đống dữ liệu rời rạc thành một hệ thống observability thực sự.
- Xây alert dựa trên triệu chứng, không dựa trên nguyên nhân. Cảnh báo nên phản ánh trải nghiệm người dùng ("tỷ lệ lỗi checkout > 2% trong 5 phút") thay vì chi tiết kỹ thuật ("CPU > 90%"). CPU cao chưa chắc là vấn đề; khách không mua được hàng chắc chắn là vấn đề.
- Quyết định sampling và retention có chủ đích. Ngay từ đầu hãy đặt câu hỏi ngân sách: giữ log bao lâu, lấy mẫu trace ở tỷ lệ nào, dùng tail-based sampling để ưu tiên giữ những gì bất thường.
Lỗi thường gặp & mẹo
Lỗi 1 — Nhầm monitoring với observability. Nhiều đội cài dashboard CPU/RAM rồi tuyên bố "đã có observability". Dashboard chỉ trả lời câu hỏi bạn biết trước. Observability phải cho phép bạn điều tra những sự cố bạn chưa từng lường trước. Mẹo: kiểm tra bằng câu hỏi "nếu một sự cố hoàn toàn mới xảy ra ngay bây giờ, tôi có đủ dữ liệu để truy ra gốc rễ không?".
Lỗi 2 — Ghi log quá nhiều, quá vô tổ chức. Log DEBUG tràn lan ở production vừa tốn tiền vừa che lấp tín hiệu quan trọng trong biển nhiễu. Mẹo: log có mục đích. Mỗi dòng log nên trả lời được câu hỏi "ai sẽ đọc dòng này, khi nào, để làm gì?".
Lỗi 3 — Log dạng text tự do. grep trên hàng tỷ dòng text là con đường dẫn tới thất bại. Luôn structured logging.
Lỗi 4 — Quên truyền trace_id qua ranh giới async. Trace hoạt động tốt với gọi HTTP đồng bộ, nhưng khi request đi qua message queue (Kafka, RabbitMQ), rất nhiều đội quên truyền context. Kết quả: trace bị đứt đoạn đúng chỗ khó debug nhất. Mẹo: đưa việc truyền trace context vào thư viện chung, không để mỗi lập trình viên tự nhớ.
Lỗi 5 — Cardinality bùng nổ trong metric. Thêm nhãn có giá trị vô hạn (như user_id) vào metric sẽ làm nổ số lượng time series và giết chết hệ thống metric. Mẹo: metric chỉ nên có nhãn với tập giá trị hữu hạn nhỏ (như region, status_code). Cần chi tiết theo user? Đó là việc của log và trace.
Lỗi 6 — Alert quá nhiều gây "mù cảnh báo". Khi mọi thứ đều cảnh báo, không gì được chú ý. Đội ngũ tê liệt, bỏ qua cả alert thật. Mẹo: mỗi alert phải actionable — nếu không ai làm gì khi nó kêu, hãy xóa nó đi.
Bài tập thực hành
- Phân loại tín hiệu. Cho hệ thống đặt vé xem phim online. Liệt kê 3 metric, 3 loại sự kiện cần log, và mô tả một luồng request nên được trace. Với mỗi metric, ghi rõ nó là counter, gauge hay histogram.
- Thiết kế schema log. Viết một cấu trúc JSON cho log của sự kiện "đặt vé thất bại". Đảm bảo có đủ trường để một kỹ sư nửa đêm điều tra được, và chỉ rõ trường nào cần masking vì lý do bảo mật.
- Đi qua ba trụ cột. Giả sử metric báo "tỷ lệ đặt vé thành công rớt từ 95% xuống 60%". Viết ra từng bước bạn dùng trace rồi log để tìm nguyên nhân gốc. Câu hỏi nào ở mỗi bước?
- Quyết định trade-off chi phí. Hệ thống của bạn tạo 5 TB log/ngày, chi phí lưu trữ đang vượt ngân sách gấp đôi. Đề xuất một chiến lược sampling và retention cho cả ba trụ cột, giải thích bạn hy sinh gì và giữ lại gì.
- Thiết kế alert. Viết 3 quy tắc cảnh báo dựa trên triệu chứng (không dựa trên nguyên nhân) cho hệ thống đặt vé, kèm ngưỡng và khoảng thời gian cụ thể.
Tóm tắt
Observability là khả năng suy ra trạng thái bên trong của hệ thống từ những tín hiệu nó phát ra — khác hẳn monitoring vốn chỉ trả lời các câu hỏi định sẵn. Nó dựa trên ba trụ cột: Logs (bản ghi sự kiện rời rạc, giàu ngữ cảnh nhưng tốn kém), Metrics (số đo tổng hợp theo thời gian, rẻ và nhanh, lý tưởng cho alert và dashboard), và Traces (dòng chảy của một request xuyên nhiều service, không thể thiếu để định vị điểm nghẽn trong hệ thống phân tán).
Sức mạnh thực sự đến từ việc tương quan cả ba qua trace_id: metric báo động, trace khoanh vùng, log giải thích tận gốc. Với vai trò người ra quyết định kiến trúc, hãy nhớ ba điều: observability là thuộc tính kiến trúc phải thiết kế từ đầu chứ không thêm vào sau; đo cả tín hiệu nghiệp vụ chứ không chỉ hạ tầng; và ở quy mô lớn, đây là bài toán trade-off chi phí đòi hỏi quyết định có chủ đích về sampling, retention và cardinality. Một hệ thống không quan sát được là một hệ thống bạn không thể vận hành, tối ưu hay tin tưởng.