Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 34 — Logging — CloudWatch Logs, Cloud Logging, ELK

Đã đọc 0/13 mục · còn khoảng 12 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng lúc 2 giờ sáng, hệ thống thanh toán của bạn báo lỗi hàng loạt. Khách hàng không đặt được đơn, tổng đài kêu cứu, sếp gọi điện. Bạn mở dashboard lên: CPU bình thường, RAM bình thường, network bình thường. Metric nói "mọi thứ ổn" nhưng thực tế hệ thống đang chết. Lúc này thứ duy nhất cứu được bạn là log — dòng ghi chép chi tiết từng sự kiện mà ứng dụng đã in ra. Nếu log của bạn tốt, bạn sẽ thấy ngay: "connection to payment-gateway timeout after 30s". Nếu log của bạn tệ, bạn sẽ ngồi đoán mò đến sáng.

Ở bài trước chúng ta đã học về Monitoring (CloudWatch, Stackdriver, Azure Monitor) — thứ trả lời câu hỏi "hệ thống có khỏe không?" thông qua các con số tổng hợp. Logging trả lời một câu hỏi khác và sâu hơn: "chính xác chuyện gì đã xảy ra, ở đâu, lúc nào, với request nào?". Metric cho bạn biết có vấn đề, còn log cho bạn biết vấn đề là gì. Đây là lý do mọi kỹ sư DevOps chuyên nghiệp đều xem logging là kỹ năng nền tảng.

Trong bài này, tôi sẽ giảng cho bạn ba trụ cột: cách phân tầng log cho đúng (log levels), cách viết log có cấu trúc để máy đọc được, và ba nền tảng thu thập log phổ biến nhất — CloudWatch Logs (AWS), Cloud Logging (GCP), và ELK Stack (mã nguồn mở, tự vận hành). Học xong bài này, bạn sẽ không còn print() bừa bãi nữa, mà biết thiết kế một hệ thống log dùng được thật.

Khái niệm cốt lõi

Log levels — phân tầng mức độ quan trọng

Không phải mọi dòng log đều quan trọng như nhau. Nếu bạn in mọi thứ ra cùng một mức, bạn sẽ chết chìm trong nhiễu. Vì vậy chúng ta gán cho mỗi log một level — mức độ nghiêm trọng:

  • TRACE: chi tiết nhất, rất "ồn ào". Ghi lại từng bước nhỏ bên trong hàm. Chỉ dùng khi debug sâu ở môi trường dev, gần như không bao giờ bật ở production vì sẽ sinh ra hàng GB log mỗi giờ.
  • DEBUG: thông tin phục vụ gỡ lỗi ở dev/staging. Ví dụ giá trị biến, câu SQL sinh ra, payload trả về từ một API nội bộ.
  • INFO: sự kiện nghiệp vụ trong luồng chạy bình thường. "User 12345 đã đăng nhập", "Đơn hàng #A891 được tạo". Đây là mức mặc định thường bật ở production.
  • WARN: chuyện bất thường nhưng hệ thống vẫn xử lý được. "Retry lần 2 kết nối database", "Cache miss cao bất thường", "Response chậm 3.2s". WARN là tín hiệu cảnh báo sớm — bỏ qua nó là bạn đang tích lũy rủi ro.
  • ERROR: một thao tác đã thất bại và ảnh hưởng đến người dùng. "Không tạo được đơn hàng vì mất kết nối payment gateway". Cần có người xem.
  • FATAL/CRITICAL: hệ thống không thể tiếp tục, phải khởi động lại hoặc dừng hẳn. "Không kết nối được database chính, service shutdown".
Nguyên tắc vàng: ở production thường chỉ bật từ INFO trở lên, còn dev/staging bật DEBUG. Level phải là biến cấu hình, không hard-code, để bạn có thể tăng độ chi tiết khi cần điều tra sự cố mà không phải deploy lại.

Structured logging — log cho máy đọc, không chỉ cho người

Đây là ranh giới phân biệt người mới và người có nghề. Log kiểu cũ trông thế này:

[2026-06-27 02:14:33] User dkhang failed to pay order A891 with card ****1234

Con người đọc được, nhưng máy thì rất khó truy vấn. Structured logging biến mỗi dòng thành một object JSON:

{
  "timestamp": "2026-06-27T02:14:33Z",
  "level": "ERROR",
  "message": "payment_failed",
  "user_id": "dkhang",
  "order_id": "A891",
  "gateway": "vnpay",
  "error_code": "TIMEOUT",
  "latency_ms": 30012,
  "trace_id": "abc-123-xyz"
}

Bây giờ bạn có thể truy vấn: "cho tôi mọi log ERROR có gateway=vnpay trong 1 giờ qua, nhóm theo error_code". Máy làm việc này trong mili giây. Đó là sức mạnh thật sự của log hiện đại — và cả ba nền tảng dưới đây đều được sinh ra để xử lý log dạng cấu trúc này.

Chú ý trường trace_id: nó là sợi chỉ xuyên suốt cho phép bạn nối tất cả log của cùng một request lại với nhau, kể cả khi request đó đi qua 5 microservice khác nhau. Đây là cầu nối giữa logging và distributed tracing mà chúng ta sẽ học ở bài sau.

Ba nền tảng thu thập log

CloudWatch Logs (AWS): Log được tổ chức theo Log Group (thường một group cho một ứng dụng/service) và bên trong là các Log Stream (thường một stream cho một instance/container). Bạn dùng CloudWatch Logs Insights để truy vấn bằng ngôn ngữ riêng, và Metric Filter để biến một pattern trong log thành một metric có thể đặt cảnh báo (ví dụ đếm số dòng "ERROR" rồi báo động khi vượt ngưỡng). Tính phí theo GB thu thập, GB lưu trữ và GB quét khi truy vấn.

Cloud Logging (GCP), tên cũ Stackdriver Logging: log tự động gom về, truy vấn bằng Logging Query Language (LQL). Điểm mạnh là Log-based Metrics và Log Router (Sink) — cho phép định tuyến log tới BigQuery để phân tích lâu dài, tới Cloud Storage để lưu rẻ, hoặc tới Pub/Sub để xử lý real-time. Với ứng dụng chạy trên GKE hay Cloud Run, log gần như tự động xuất hiện, gần như không cần cấu hình.

ELK Stack (nay gọi là Elastic Stack): bộ ba tự vận hành gồm Elasticsearch (lưu trữ và tìm kiếm), Logstash (thu thập, biến đổi, làm giàu log — ngày nay thường thay bằng Beats/Filebeat nhẹ hơn ở khâu thu thập), và Kibana (giao diện xem và dashboard). Ưu điểm: không khóa nhà cung cấp (vendor lock-in), chạy được ở on-premise hay bất kỳ cloud nào, kiểm soát toàn quyền dữ liệu — rất quan trọng với ngân hàng và cơ quan nhà nước ở Việt Nam có yêu cầu data residency. Nhược điểm: bạn phải tự lo cluster, tự lo scaling, tự lo backup. Loki của Grafana là một lựa chọn nhẹ hơn cùng triết lý.

Tình huống thực tế

Ví dụ 1 — Sàn thương mại điện tử tại TP.HCM cứu Black Friday nhờ structured log

Một sàn TMĐT giả định tên "ChợViệt" chạy trên AWS, dùng CloudWatch Logs. Trong đợt sale 12/12, tỷ lệ thanh toán thất bại tăng vọt từ 2% lên 18%. Đội kỹ thuật ban đầu bối rối vì CPU và RAM đều ổn.

May mắn là họ đã log dạng cấu trúc. Một kỹ sư mở CloudWatch Logs Insights và chạy: fields gateway, error_code | filter level="ERROR" | stats count() by gateway, error_code. Kết quả hiện ra ngay: 95% lỗi đến từ gateway="momo" với error_code="RATE_LIMITED". Hóa ra MoMo giới hạn 500 request/giây mà lượng đơn đang vượt 1.200/giây.

Họ lập tức thêm hàng đợi đệm (queue) trước cổng MoMo và bật thêm gateway dự phòng. Toàn bộ quá trình chẩn đoán mất 12 phút thay vì cả buổi tối. Bài học: nếu họ log kiểu văn bản thô, việc bóc tách "lỗi nào, cổng nào, bao nhiêu" sẽ vô cùng chậm. Structured logging + query engine là thứ biến 3 tiếng khủng hoảng thành 12 phút.

Ví dụ 2 — Fintech phải log đúng chuẩn để không rò rỉ dữ liệu nhạy cảm

Một startup fintech ở Hà Nội chạy trên GCP với Cloud Logging. Trong lần review bảo mật, họ phát giác các dòng log DEBUG ở service KYC đang in nguyên số CMND/CCCD và số thẻ đầy đủ của khách hàng ra log — vì lập trình viên tiện tay log.debug("payload: " + request.body). Những log này bị route sang BigQuery và lưu 400 ngày, tạo ra rủi ro tuân thủ nghiêm trọng theo Nghị định bảo vệ dữ liệu cá nhân (PDPD).

Họ xử lý ba việc: (1) tắt DEBUG ở production, chỉ để INFO; (2) thêm một tầng lọc che (masking) ở Log Router để tự động thay số thẻ bằng *; (3) đặt cảnh báo log-based metric khi phát hiện pattern giống số CCCD. Bài học*: log là con dao hai lưỡi — thông tin càng chi tiết càng dễ debug, nhưng cũng càng dễ rò rỉ. Không bao giờ log mật khẩu, token, số thẻ, thông tin định danh cá nhân ở dạng nguyên bản.

Ví dụ 3 — Ngân hàng chọn ELK tự vận hành vì yêu cầu chủ quyền dữ liệu

Một ngân hàng tại Việt Nam có yêu cầu bắt buộc: log giao dịch phải nằm trong data center đặt tại lãnh thổ Việt Nam, không được gửi ra dịch vụ log của cloud nước ngoài. Họ dựng ELK Stack on-premise: Filebeat cài trên từng server đẩy log về Logstash, Logstash làm giàu (thêm mã chi nhánh, phân loại giao dịch) rồi ghi vào cluster Elasticsearch 6 node, Kibana cho đội vận hành xem.

Chi phí vận hành cao hơn dùng dịch vụ managed, và họ phải có một kỹ sư chuyên lo cluster Elasticsearch (tuning shard, quản lý index lifecycle để xóa log cũ tự động sau 90 ngày). Nhưng đổi lại họ kiểm soát 100% dữ liệu và thỏa mãn yêu cầu pháp lý. Bài học: lựa chọn nền tảng log không chỉ là chuyện kỹ thuật, mà còn là chuyện tuân thủ và chi phí. Managed service (CloudWatch, Cloud Logging) tiện và nhanh; ELK tự vận hành cho bạn chủ quyền nhưng đòi hỏi nhân lực.

Hướng dẫn từng bước

Đây là quy trình thiết lập logging cho một service mới:

  • Chọn thư viện log có cấu trúc. Với Python dùng structlog hoặc python-json-logger; Node.js dùng pino hoặc winston; Java dùng Logback với encoder JSON. Đừng dùng print/console.log cho production.
  • In log ra stdout/stderr, không ghi thẳng vào file. Trong thế giới container (Docker, Kubernetes), triết lý 12-factor app khuyến nghị coi log là luồng sự kiện. Ứng dụng chỉ việc in ra stdout, còn hạ tầng (CloudWatch agent, Cloud Logging agent, Filebeat) lo phần thu thập.
  • Chuẩn hóa schema log. Thống nhất trong cả team các trường bắt buộc: timestamp, level, message, service, trace_id, user_id. Có schema chung thì mọi service query được giống nhau.
  • Gắn correlation/trace ID vào mọi request. Ở tầng đầu vào (API gateway hoặc middleware), sinh một trace_id và truyền nó qua mọi lời gọi nội bộ. Đây là thứ giúp bạn ghép log toàn hệ thống.
  • Cấu hình agent thu thập. Trên AWS: cài CloudWatch Agent hoặc dùng driver awslogs của Docker. Trên GCP với GKE: log tự gom, gần như không cần làm gì. Với ELK: cài Filebeat trỏ tới Logstash/Elasticsearch.
  • Đặt retention và cảnh báo. Quyết định giữ log bao lâu (ví dụ 30 ngày ở hệ nóng, chuyển sang lưu trữ lạnh 1 năm). Tạo metric filter / log-based metric để báo động khi số lượng ERROR vượt ngưỡng.
  • Xây dashboard và tập query hay dùng. Lưu sẵn các câu truy vấn "top error trong 1h", "log của một trace_id cụ thể" để cả team dùng khi có sự cố.

Lỗi thường gặp & mẹo

  • Log ra file trong container rồi... mất luôn. Container bị xóa là file log bay theo. Luôn in ra stdout.
  • Bật DEBUG ở production. Sinh khối lượng log khổng lồ, làm phồng hóa đơn CloudWatch/Elasticsearch lên gấp nhiều lần và làm chậm truy vấn. Chỉ bật DEBUG tạm thời khi điều tra, xong tắt ngay.
  • Log thông tin nhạy cảm. Mật khẩu, token, OTP, số thẻ, CCCD — tuyệt đối không log nguyên bản. Thêm tầng masking.
  • Log không có ngữ cảnh. Dòng "Error occurred" trần trụi vô dụng. Luôn kèm trace_id, user_id, order_id và thông điệp cụ thể.
  • Quên đặt retention. Log tích tụ vô hạn = hóa đơn tăng vô hạn. Đặt vòng đời index (ILM) trong Elasticsearch, đặt retention trong CloudWatch.
  • Nhầm log level. Đừng log lỗi nghiệp vụ bình thường (như user nhập sai mật khẩu) là ERROR — nó sẽ làm bạn "quen tai" và bỏ qua ERROR thật. Đó là WARN hoặc INFO.
  • Mẹo về chi phí: cả ba nền tảng tính tiền theo lượng log. Giảm chi phí bằng cách lọc bớt log rác ngay tại nguồn (sampling log INFO tần suất cao), nén, và chuyển log cũ sang lưu trữ rẻ (S3/Cloud Storage) thay vì giữ trong hệ tìm kiếm đắt tiền.
  • Mẹo về tương quan: log, metric và trace nên chia sẻ chung trace_id. Khi metric báo động, bạn nhảy thẳng sang log của đúng khoảng thời gian và trace đó — điều tra nhanh hơn nhiều.

Bài tập thực hành

  • Thiết kế schema log: Cho một API đặt vé xem phim, hãy viết ra một dòng log JSON mẫu cho sự kiện "đặt vé thành công" và một dòng cho "hết ghế". Xác định level phù hợp cho mỗi dòng và liệt kê các trường bắt buộc.
  • Phân loại level: Với 5 tình huống sau, gán level đúng: (a) người dùng đăng nhập thành công; (b) retry lần 3 gọi API tỉ giá; (c) mất kết nối tới database chính, service dừng; (d) giá trị biến trung gian khi tính giảm giá; (e) không gửi được email xác nhận cho khách. Giải thích lựa chọn.
  • Viết query: Giả sử bạn dùng CloudWatch Logs Insights. Viết một câu truy vấn (dạng gần đúng) để tìm 10 error_code xuất hiện nhiều nhất trong các log level ERROR của service payment trong 3 giờ qua, nhóm theo error_code.
  • So sánh nền tảng: Bạn là kiến trúc sư cho một startup logistics ở Đông Nam Á, dự kiến 50 triệu log/ngày, đội ngũ 4 kỹ sư, ưu tiên vận hành nhẹ. Bạn chọn CloudWatch/Cloud Logging hay ELK tự vận hành? Viết 3 lý do bảo vệ lựa chọn.

Tóm tắt

Logging là mắt xích không thể thiếu để hiểu chuyện gì đã xảy ra bên trong hệ thống — bổ sung cho monitoring vốn chỉ cho biết có vấn đề hay không. Ba điều cốt lõi cần nhớ:

  • Log levels (TRACE → DEBUG → INFO → WARN → ERROR → FATAL) giúp phân tầng độ quan trọng; production thường bật từ INFO, level phải cấu hình được chứ không hard-code.
  • Structured logging (log dạng JSON có trường rõ ràng, kèm trace_id) biến log thành dữ liệu máy truy vấn được — đây là kỹ năng phân biệt người có nghề.
  • Ba nền tảng: CloudWatch Logs cho hệ AWS, Cloud Logging cho hệ GCP (mạnh về routing sang BigQuery), và ELK/Elastic Stack cho ai cần tự vận hành, tránh vendor lock-in và đáp ứng chủ quyền dữ liệu.
Nguyên tắc thực chiến: in log ra stdout, chuẩn hóa schema toàn team, gắn trace_id, không bao giờ log dữ liệu nhạy cảm, và luôn đặt retention để kiểm soát chi phí. Làm đúng những điều này, lần tới hệ thống sập lúc 2 giờ sáng, bạn sẽ tìm ra nguyên nhân trong vài phút thay vì cả đêm.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi