Thủ Thuật Log .NET Hằng Ngày: Theo Dõi Lỗi Hiệu Quả

Chúng tôi từng ngồi debug một API ASP.NET Core chạy trên production. Lỗi 500 lúc có lúc không, còn log chỉ toàn dòng “An error occurred”. Không có request nào được ghi lại, không có người dùng nào để đối chiếu. Cả nhóm mất gần nguyên một buổi chiều chỉ để khoanh vùng đúng endpoint bị lỗi.

Chuyện này khá phổ biến. Khi nói đến thủ thuật .NET hằng ngày, chúng tôi thường bắt đầu từ việc ghi log. Nhiều dự án khởi đầu bằng Console.WriteLine hoặc log mặc định mà chưa phân loại mức độ. Hệ thống lớn dần, file log phình ra, nhưng khi cần truy một lỗi cụ thể thì bạn vẫn lúng túng. Phải lật hàng nghìn dòng mà không biết dòng nào thuộc request nào. Thời gian tìm lỗi thường dài hơn hẳn thời gian sửa.

Ghi log có cấu trúc ngay từ đầu dự án

Theo kinh nghiệm của chúng tôi, nguyên tắc quan trọng nhất là phân loại log theo mức độ. Với Serilog hoặc hệ thống logging tích hợp sẵn của .NET, bạn có năm mức chính: Debug, Information, Warning, Error và Critical. Mỗi mức cần mang một ý nghĩa riêng biệt. Nếu dùng lẫn lộn, lúc có sự cố bạn sẽ không biết nên tin dòng nào. Một dòng Information không nên nằm trong cùng mức với một dòng báo mất kết nối cơ sở dữ liệu.

Trên production, chúng tôi thường chỉ giữ lại từ mức Warning trở lên. Nhờ vậy log không bị nhiễu bởi những thông tin chỉ hữu ích khi phát triển. Khi có sự cố, lọc theo Error đã thu hẹp phạm vi rà soát rất nhiều. Bạn không còn phải đọc lại toàn bộ file từ đầu đến cuối.

Chúng tôi cũng hay cấu hình mức log theo namespace. Ví dụ, phần truy cập dữ liệu có thể đặt ở mức Debug trên staging, còn production chỉ giữ Information cho các service nghiệp vụ. Bạn bật chi tiết đúng chỗ khi cần, không phải đổi cấu hình cho cả hệ thống. Điều này đặc biệt có ích khi một module đang bị nghi ngờ mà các phần khác vẫn chạy ổn định.

Gắn CorrelationId để lần theo một request

Đây là kỹ thuật chúng tôi thấy hữu ích nhất khi ứng dụng đi qua nhiều tầng API hoặc nhiều service. Ý tưởng khá đơn giản. Tạo một CorrelationId ngay trong middleware đầu tiên khi request đi vào, rồi truyền ID đó qua các lớp xử lý và ghi kèm vào mọi dòng log liên quan.

Trong ASP.NET Core, bạn có thể đưa giá trị này vào LogContext của Serilog bằng PushProperty. Từ đó mọi log trong phạm vi request tự động mang theo ID, không cần truyền tham số thủ công vào từng hàm. Khi điều tra, chỉ cần lọc theo một CorrelationId là bạn có toàn bộ hành trình của request đó, từ lúc vào cổng cho tới khi trả về kết quả.

Nếu hệ thống có nhiều service gọi lẫn nhau, hãy truyền header X-Correlation-Id trong các HttpClient. Như vậy ID không bị đứt khi request chuyển sang service khác. Nếu bạn đang thiết kế lại tầng API, có thể tham khảo thêm cách thiết kế API layer để dữ liệu chạy mượt để gắn cơ chế theo dõi request vào kiến trúc ngay từ đầu.

Một lưu ý nhỏ: đừng ghi nguyên văn mật khẩu hay số thẻ vào log. CorrelationId chỉ là chuỗi định danh ngẫu nhiên, không cần kèm thông tin cá nhân nào.

Mức log Khi nào nên dùng
Debug Thông tin chi tiết, chỉ cần khi đang phát triển hoặc điều tra sâu
Information Sự kiện bình thường, ví dụ một đơn hàng vừa được tạo thành công
Error / Critical Sự cố cần xử lý ngay, nên là thứ đầu tiên bạn lọc khi có báo lỗi

Công cụ xem log tập trung thay cho mở file thủ công

Mở từng file log trên từng server rồi bấm Ctrl+F là cách chúng tôi từng dùng, và đã bỏ sau vài dự án. Khi hệ thống có ba, bốn server, việc gom log rải rác gần như không thể làm nhanh được.

Chúng tôi khuyên bạn thiết lập một công cụ log tập trung ngay từ đầu, dù dự án còn nhỏ. Seq là lựa chọn quen thuộc trong hệ sinh thái .NET. Bạn có thể truy vấn theo CorrelationId, theo mức nghiêm trọng hay theo khoảng thời gian chỉ bằng vài thao tác. Thói quen này thường rút ngắn thời gian điều tra đi rõ rệt.

Làm việc nhóm cũng dễ thở hơn. Mọi người cùng nhìn vào một nguồn dữ liệu, không phải xin quyền truy cập server hay chờ ai gửi file qua lại. Nếu nhóm đã có quy trình review code chặt chẽ, hãy thêm tiêu chí “cách ghi log có đúng chuẩn không” vào mỗi pull request. Bạn có thể tham khảo best practices cho backend developer khi review code để bổ sung tiêu chí này.

Đừng quên cấu hình retention cho log. Giữ mọi thứ mãi mãi chỉ làm chi phí lưu trữ tăng mà không mang lại giá trị. Theo kinh nghiệm của chúng tôi, log mức Error nên giữ lâu hơn, còn log Debug có thể xoá sớm hơn nhiều.

Để tham khảo thêm cách hệ sinh thái .NET hỗ trợ logging và diagnostics ở mức tài liệu chính thức, bạn có thể xem tài liệu logging của Microsoft.

Tập thói quen ghi log từ những dự án nhỏ

Nhiều bạn mới vào nghề xem việc ghi log là chi tiết nhỏ, có thể làm sau khi dự án đã ổn định. Thực tế, cách nghĩ này thường khiến việc bổ sung log về sau rất rời rạc. Các dòng log được thêm vội vàng, định dạng không thống nhất, và có những đoạn quan trọng lại chẳng có gì để theo dõi.

Vì vậy, hãy bắt đầu với dự án cá nhân hoặc những dự án nhỏ đầu tiên. Ghi log có cấu trúc, gắn CorrelationId và phân mức độ ngay từ đầu. Khi bước vào một hệ thống lớn với nhiều service, bạn sẽ không phải học lại từ con số không giữa lúc dự án đang gấp rút. Đó là điều chúng tôi ước đã làm sớm hơn, và có lẽ bạn cũng sẽ thấy vậy.