Chào mừng các bạn quay trở lại với series “.NET Roadmap”! Sau khi đã khám phá những nền tảng cơ bản như C#, Hệ Sinh Thái .NET, Git, hay cách làm việc với Cơ sở dữ liệu quan hệ và Entity Framework Core, chúng ta đang dần xây dựng nên bộ kỹ năng vững chắc của một lập trình viên .NET. Hôm nay, chúng ta sẽ đi sâu vào một khía cạnh cực kỳ quan trọng trong phát triển ứng dụng chuyên nghiệp: **Logging**.
Bạn có nhớ bài viết về Ghi Log có Cấu Trúc với Serilog chứ? Logging không chỉ đơn thuần là ghi lại những dòng chữ khi có lỗi xảy ra. Nó là “đôi mắt” giúp bạn quan sát hoạt động của ứng dụng trong môi trường thực tế, từ đó dễ dàng phát hiện lỗi, phân tích hành vi người dùng, giám sát hiệu năng và đảm bảo an ninh. Trong hệ sinh thái .NET, bên cạnh Serilog, **NLog** là một thư viện logging mạnh mẽ, linh hoạt và được sử dụng rất phổ biến. Bài viết này sẽ hướng dẫn bạn cách cấu hình NLog để quản lý log một cách nâng cao, khai thác tối đa sức mạnh của nó.
Mục lục
Tại Sao Cần Quản Lý Log Nâng Cao?
Ở mức cơ bản, việc ghi log chỉ đơn giản là ghi một thông báo vào Console hoặc một tệp tin. Tuy nhiên, khi ứng dụng của bạn phát triển lớn hơn, phức tạp hơn và chạy trên môi trường production, những yêu cầu về log cũng tăng lên:
- Cần ghi log ra nhiều nơi khác nhau cùng lúc (tệp tin, database, dịch vụ log tập trung…).
- Cần lọc log dựa trên mức độ quan trọng (chỉ ghi lỗi Fatal vào database, ghi tất cả thông tin Debug vào tệp tin…).
- Cần định dạng log một cách tùy chỉnh để dễ đọc hoặc phân tích bằng máy.
- Cần thêm thông tin ngữ cảnh vào log (ID yêu cầu, ID người dùng, thông tin môi trường…).
- Cần xử lý lỗi khi ghi log (ví dụ: tệp tin bị khóa).
- Cần đảm bảo việc ghi log không làm chậm hiệu suất ứng dụng.
NLog cung cấp các tính năng mạnh mẽ để đáp ứng tất cả những nhu cầu này và hơn thế nữa.
Giới Thiệu Về NLog
NLog là một framework logging miễn phí, linh hoạt và dễ cấu hình cho các nền tảng .NET, bao gồm cả .NET Core và ASP.NET Core. Điểm mạnh của NLog nằm ở:
- Linh hoạt: Hỗ trợ cấu hình bằng tệp tin (XML, JSON) hoặc bằng code.
- Đa dạng Target: Có sẵn nhiều loại target để ghi log ra các nguồn khác nhau (Console, File, Database, Email, Network, Event Log, ASP.NET Trace…).
- Layout mạnh mẽ: Cho phép tùy chỉnh định dạng thông báo log chi tiết với nhiều Layout Renderers.
- Lọc log hiệu quả: Cung cấp cơ chế Rule để định nghĩa luồng log dựa trên tên logger, mức độ, và điều kiện.
- Hiệu năng cao: Hỗ trợ ghi log bất đồng bộ để không chặn luồng chính của ứng dụng.
- Cộng đồng lớn: Được duy trì và phát triển tích cực.
Bắt Đầu Với NLog Trong ASP.NET Core
Giống như các thư viện khác trong hệ sinh thái .NET, bạn bắt đầu bằng cách thêm package NuGet vào dự án của mình. Với ASP.NET Core, package phổ biến nhất là `NLog.Web.AspNetCore` vì nó cung cấp tích hợp sâu với môi trường web.
dotnet add package NLog.Web.AspNetCore
Sau khi cài đặt, bạn cần cấu hình NLog cho ứng dụng. Cách phổ biến nhất là sử dụng tệp cấu hình XML (thường là `nlog.config`).
Cấu hình NLog trong ASP.NET Core
Thêm một tệp XML vào gốc dự án của bạn và đặt tên là `nlog.config`. Đảm bảo tệp này được copy vào thư mục đầu ra khi build (trong `.csproj` file, thêm `
Nội dung cơ bản của `nlog.config`:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogFile="c:\temp\nlog-internal.log"
internalLogLevel="Info" >
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="logfile" fileName="c:\temp\logs\${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId.Id}|${uppercase:${level}}|${logger}|${message} ${exception}" />
<!-- write logs to console -->
<target xsi:type="Console" name="logconsole"
layout="${longdate}|${event-properties:item=EventId.Id}|${uppercase:${level}}|${logger}|${message} ${exception}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!-- All logs >= Debug go to the console -->
<logger name="*" minlevel="Debug" writeTo="logconsole" />
<!-- All logs >= Info go to the file -->
<logger name="*" minlevel="Info" writeTo="logfile" />
</rules>
</nlog>
Tiếp theo, tích hợp NLog vào `Program.cs` của ứng dụng ASP.NET Core:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Web;
public class Program
{
public static void Main(string[] args)
{
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("Init main");
CreateHostBuilder(args).Build().Run();
}
catch (Exception exception)
{
// NLog: catch setup errors
logger.Error(exception, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders(); // Xóa các logging provider mặc định
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); // Thiết lập mức độ log tối thiểu cho .NET Core
})
.UseNLog(); // Sử dụng NLog làm logging provider
}
Với cấu hình này, ứng dụng của bạn đã sẵn sàng sử dụng NLog thông qua `ILogger` của .NET Core Dependency Injection. Nếu bạn chưa nắm vững về Vòng Đời Dịch Vụ trong DI hay cách Tiêm Phụ Thuộc, hãy xem lại các bài viết đó nhé!
Các Thành Phần Chính Của NLog
Để cấu hình NLog nâng cao, bạn cần hiểu rõ các thành phần cốt lõi của nó:
- Logger: Đối tượng mà bạn sử dụng trong code để gọi các phương thức log (`logger.Info(“…”)`, `logger.Error(“…”)`). Mỗi logger có một tên, thường là tên của class sử dụng nó.
- Log Level: Mức độ nghiêm trọng của thông báo log (Trace, Debug, Info, Warn, Error, Fatal). Thứ tự tăng dần về mức độ nghiêm trọng: Trace < Debug < Info < Warn < Error < Fatal.
- Target: Đích đến nơi log được ghi. NLog có rất nhiều target tích hợp sẵn (File, Console, Database, Mail, Network…) và bạn cũng có thể tạo target tùy chỉnh.
- Layout: Định nghĩa cách thông báo log được định dạng thành một chuỗi văn bản. Sử dụng các **Layout Renderers** để chèn các thông tin khác nhau (ngày giờ, mức độ, thông báo, tên logger, exception, thông tin ngữ cảnh…).
- Rule: Kết nối các logger (hoặc nhóm logger theo tên), mức độ log với một hoặc nhiều target. Đây là nơi bạn định nghĩa luồng xử lý log.
Dưới đây là bảng tóm tắt các thành phần chính này:
Thành Phần | Mô Tả | Ví Dụ |
---|---|---|
Logger | Đối tượng để ghi log trong code. Tên thường là tên class. | private readonly ILogger<HomeController> _logger; |
Log Level | Mức độ nghiêm trọng của thông báo. | Debug , Info , Error , Fatal |
Target | Nơi log được ghi đến. | File , Console , Database , Mail |
Layout | Định dạng chuỗi log. Sử dụng Layout Renderers. | ${longdate}|${level}|${logger}|${message} |
Rule | Kết nối Logger + Level với Target. | Ghi tất cả log Info trở lên từ bất kỳ logger nào ra fileTarget . |
Cấu Hình Nâng Cao Với NLog.config (XML)
Cấu hình bằng tệp XML là cách phổ biến và mạnh mẽ nhất để khai thác NLog cho các kịch bản phức tạp. Chúng ta sẽ đi sâu vào các phần chính trong tệp `nlog.config`:
1. Phần <targets>
Đây là nơi bạn định nghĩa tất cả các đích đến (target) mà bạn muốn ghi log ra. Mỗi target có một tên duy nhất và một kiểu (`xsi:type`).
<targets>
<!-- Target ghi vào file hàng ngày -->
<target xsi:type="File" name="fileTarget"
fileName="c:\logs\app-${shortdate}.log"
layout="${longdate} ${level} ${logger} ${message} ${exception}"
archiveFileName="c:\logs\archives\app-${shortdate}.{#####}.zip"
archiveEvery="Day"
archiveNumbering="Sequence"
maxArchiveFiles="7"
enableArchiveFileCompression="true" />
<!-- Target ghi lỗi nghiêm trọng vào database -->
<target xsi:type="Database" name="databaseTarget"
connectionString="Server=myServer;Database=myLogDb;User Id=myUser;Password=myPassword;"
commandText="INSERT INTO Logs (Timestamp, Level, Logger, Message, Exception) VALUES (@timestamp, @level, @logger, @message, @exception)">
<parameter name="@timestamp" layout="${longdate}" />
<parameter name="@level" layout="${level}" />
<parameter name="@logger" layout="${logger}" />
<parameter name="@message" layout="${message}" />
<parameter name="@exception" layout="${exception:format=tostring}" />
</target>
<!-- Target gửi email khi có lỗi Fatal -->
<target xsi:type="Mail" name="mailTarget"
smtpServer="smtp.gmail.com"
smtpPort="587"
enableSsl="true"
smtpAuthentication="Basic"
smtpUsername="your.email@gmail.com"
smtpPassword="yourpassword"
from="app.logger@yourcompany.com"
to="admin@yourcompany.com"
subject="Fatal Error Occurred in App: ${shortdate}"
body="${longdate} ${level}: ${message}${newline}${exception}" />
<!-- Target ghi log có cấu trúc ra Console (thường dùng cho ELK stack) -->
<target xsi:type="Console" name="jsonConsole"
layout='${json:message=@m:exception=@e:logger=@l:level=@l:properties=${all-event-properties:includeMdcDiagnosticsContext=true:includeNdcDiagnosticsContext=true}}' />
</targets>
Trong ví dụ trên, chúng ta định nghĩa 4 target khác nhau:
- `fileTarget`: Ghi log vào tệp tin, có cấu hình quay vòng tệp (archive) theo ngày, nén tệp cũ và chỉ giữ lại 7 tệp gần nhất.
- `databaseTarget`: Ghi log vào cơ sở dữ liệu SQL Server. Bạn định nghĩa chuỗi kết nối và câu lệnh INSERT. Các cột trong database được ánh xạ từ các Layout Renderers bằng các thẻ `
`. NLog hỗ trợ nhiều loại cơ sở dữ liệu khác nhau. Đây có thể là nơi bạn cần kiến thức về SQL hoặc thậm chí NoSQL nếu bạn dùng các target tương ứng. - `mailTarget`: Gửi email khi có log. Cấu hình SMTP server, thông tin đăng nhập, người gửi, người nhận, tiêu đề và nội dung email. Rất hữu ích cho các cảnh báo quan trọng.
- `jsonConsole`: Ghi log ra console dưới định dạng JSON. Đây là cách phổ biến để gửi log đến các hệ thống quản lý log tập trung như Elasticsearch (bạn có thể tham khảo bài Bắt Đầu Với Elasticsearch) hoặc Seq, sử dụng target `Network` hoặc các target chuyên biệt khác.
2. Phần <rules>
Phần này là “trái tim” của cấu hình NLog, định nghĩa luồng log sẽ đi như thế nào. Mỗi rule kết nối một tập hợp các logger (xác định bằng thuộc tính `name`, hỗ trợ wildcard `*`) với một hoặc nhiều target (xác định bằng thuộc tính `writeTo`, cách nhau bởi dấu phẩy) dựa trên mức độ log (`minlevel`, `maxlevel`, `level`).
<rules>
<!-- Rule 1: Tất cả log từ mức Debug trở lên đều ghi ra fileTarget -->
<logger name="*" minlevel="Debug" writeTo="fileTarget" />
<!-- Rule 2: Chỉ các log mức Error và Fatal mới ghi ra databaseTarget -->
<logger name="*" level="Error,Fatal" writeTo="databaseTarget" />
<!-- Rule 3: Chỉ các log mức Fatal mới gửi email -->
<logger name="*" level="Fatal" writeTo="mailTarget" />
<!-- Rule 4: Log từ namespace "Microsoft.*" (các log nội bộ của ASP.NET Core) chỉ ghi ra console ở mức Info trở lên -->
<logger name="Microsoft.*" minlevel="Info" writeTo="logconsole" final="true" />
<!-- Rule 5: Ghi log có cấu trúc ra jsonConsole -->
<logger name="*" minlevel="Trace" writeTo="jsonConsole" />
<!-- Ví dụ rule cho logger cụ thể -->
<logger name="MyApp.Controllers.HomeController" minlevel="Debug" writeTo="debugFileTarget" />
</rules>
Giải thích các rule:
- Rule 1: Rất phổ biến, ghi tất cả log từ Debug trở lên vào tệp tin để phục vụ việc debug tổng thể.
- Rule 2: Chỉ ghi những lỗi nghiêm trọng vào database để dễ dàng truy vấn và phân tích tập trung.
- Rule 3: Gửi email ngay lập tức khi có lỗi Fatal để team vận hành biết và xử lý kịp thời.
- Rule 4: Sử dụng `final=”true”`. Điều này có nghĩa là khi NLog tìm thấy một rule khớp (ở đây là logger bắt đầu bằng “Microsoft.” và mức độ từ Info trở lên), nó sẽ áp dụng rule này (ghi ra `logconsole`) và **dừng xử lý các rule tiếp theo** cho log message đó. Rất hữu ích để kiểm soát luồng log và tránh ghi log nội bộ của framework vào các target không cần thiết.
- Rule 5: Ghi tất cả log ở định dạng JSON, có thể dùng để gửi đến các hệ thống log tập trung.
- Bạn có thể chỉ định rule cho logger cụ thể bằng cách dùng tên đầy đủ của class.
3. Sử dụng Condition-based Filtering (<when>)
Nâng cao hơn, bạn có thể thêm điều kiện lọc (`<when>`) vào rule hoặc target để chỉ ghi log khi một biểu thức điều kiện là đúng. Biểu thức này sử dụng các Layout Renderers.
<rules>
<!-- Chỉ ghi log Error ra databaseTarget nếu exception message chứa "Database connection failed" -->
<logger name="*" level="Error" writeTo="databaseTarget">
<filters>
<when condition='${exception:format=message} contains "Database connection failed"' />
</filters>
</logger>
<!-- Gửi email chỉ khi log Fatal đến từ logger "MyApp.Services.PaymentGateway" -->
<logger name="MyApp.Services.PaymentGateway" level="Fatal" writeTo="mailTarget" />
<!-- Hoặc dùng when trong target -->
<target xsi:type="File" name="criticalErrorsFile" fileName="c:\logs\critical-errors.log"
layout="${longdate} ${level} ${logger} ${message} ${exception}" >
<filters>
<when condition='${level} == "Fatal" or (${level} == "Error" and ${logger} starts-with "MyApp.BackgroundTasks.")' />
</filters>
</target>
<!-- Sau đó thêm rule để đẩy log vào criticalErrorsFile target -->
<logger name="*" minlevel="Error" writeTo="criticalErrorsFile" />
</rules>
`
Thêm Ngữ Cảnh Với MDC và NDLC
Trong các ứng dụng web hoặc đa luồng, việc biết log message đến từ yêu cầu (request) nào hoặc luồng nào là rất quan trọng. NLog cung cấp hai cơ chế để thêm thông tin ngữ cảnh theo luồng:
- **Mapped Diagnostics Context (MDC):** Sử dụng `MappedDiagnosticsContext.Set()` để lưu trữ các giá trị key-value theo luồng hiện tại. Các giá trị này có thể được truy cập trong Layout bằng `${mdc:item=Key}`.
- **Nested Diagnostics Context (NDLC):** Sử dụng `NestedDiagnosticsContext.Push()` để đẩy các giá trị vào một stack theo luồng hiện tại. Các giá trị này có thể được truy cập bằng `${ndlc}`. Thường dùng để theo dõi các bước lồng nhau trong một tác vụ.
Ví dụ sử dụng MDC trong code ASP.NET Core Middleware (hoặc filter):
public class RequestIdMiddleware
{
private readonly RequestDelegate _next;
public RequestIdMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Lấy hoặc tạo Request ID
var requestId = context.TraceIdentifier;
// Đặt Request ID vào MDC
using (NLog.MappedDiagnosticsContext.SetScoped("RequestId", requestId))
{
// Đặt thông tin user (nếu có)
if (context.User.Identity.IsAuthenticated)
{
NLog.MappedDiagnosticsContext.SetScoped("UserId", context.User.Identity.Name);
}
await _next(context);
}
// Khi khối using kết thúc, các giá trị "RequestId" và "UserId" tự động bị xóa khỏi MDC của luồng này
}
}
// Đăng ký Middleware trong Startup.cs Configure method
// app.UseMiddleware<RequestIdMiddleware>();
// Lưu ý: Cần đặt middleware này ở vị trí phù hợp trong pipeline.
Trong `nlog.config`, bạn thêm Layout Renderer tương ứng:
<target xsi:type="Console" name="logconsole"
layout="${longdate}|${uppercase:${level}}|${logger}|RequestId=${mdc:item=RequestId}|UserId=${mdc:item=UserId}|${message} ${exception}" />
Kết quả log sẽ bao gồm RequestId và UserId, giúp bạn dễ dàng theo dõi luồng xử lý của từng yêu cầu người dùng.
Ghi Log Bất Đồng Bộ (Asynchronous Logging)
Việc ghi log (đặc biệt là vào file, database, hay network) có thể tốn thời gian và chặn luồng thực thi của ứng dụng, làm giảm hiệu suất. NLog giải quyết vấn đề này bằng các target bất đồng bộ:
- `AsyncWrapper`: Bọc một target khác, chuyển việc ghi log sang một luồng khác.
- `BufferingWrapper`: Tích lũy các log event trong bộ đệm trước khi ghi hàng loạt tới target, thường dùng kết hợp với `AsyncWrapper`.
Ví dụ sử dụng `AsyncWrapper` trong `nlog.config`:
<targets>
<!-- Bọc fileTarget bằng AsyncWrapper -->
<target xsi:type="AsyncWrapper" name="asyncFileTarget" >
<target xsi:type="File" name="fileTarget"
fileName="c:\logs\app-${shortdate}.log"
layout="${longdate} ${level} ${logger} ${message} ${exception}" />
</target>
<!-- Bọc databaseTarget bằng AsyncWrapper và BufferingWrapper -->
<target xsi:type="AsyncWrapper" name="asyncBufferedDbTarget" >
<target xsi:type="BufferingWrapper" bufferSize="100" flushTimeout="5000">
<target xsi:type="Database" name="databaseTarget"
connectionString="..."
commandText="...">
<!-- parameters here -->
</target>
</target>
</target>
</targets>
<rules>
<!-- Ghi log ra asyncFileTarget -->
<logger name="*" minlevel="Debug" writeTo="asyncFileTarget" />
<!-- Ghi lỗi ra asyncBufferedDbTarget -->
<logger name="*" level="Error,Fatal" writeTo="asyncBufferedDbTarget" />
</rules>
Khi sử dụng target bất đồng bộ, NLog sẽ thêm log event vào một hàng đợi và trả về ngay lập tức. Một luồng riêng sẽ xử lý hàng đợi và ghi log vào target gốc. Điều này cải thiện đáng kể hiệu năng ghi log.
Xử Lý Lỗi trong NLog
Điều gì xảy ra nếu NLog không ghi log được (ví dụ: không kết nối được database, tệp tin bị khóa)? Theo mặc định, NLog sẽ nuốt (swallow) exception để không làm crash ứng dụng của bạn. Tuy nhiên, bạn có thể cấu hình để xử lý các lỗi này:
- `internalLogFile` và `internalLogLevel` trong <nlog>: Ghi log nội bộ của NLog vào một tệp riêng để debug các vấn đề cấu hình hoặc ghi log.
- `ThrowExceptions=”true”` trong <nlog>: NLog sẽ ném exception khi có lỗi (chỉ nên dùng trong môi trường phát triển/debug).
- `onunhandledexception=”[Method]”` trong <nlog>: Chỉ định hành động khi có lỗi không được xử lý bởi các target wrappers (ví dụ: `Flush`, `Throw`).
- Wrapper Target `FallbackGroup`: Cung cấp một danh sách các target thay thế để ghi log nếu target chính gặp lỗi.
- Wrapper Target `PostFilteringWrapper`: Lọc log *sau* khi chúng đã được xử lý bởi các target khác, hữu ích cho các kịch bản đặc biệt.
Ví dụ sử dụng `FallbackGroup`:
<targets>
<!-- Fallback group: Thử ghi vào databaseTarget, nếu lỗi thì ghi vào fileTarget -->
<target xsi:type="FallbackGroup" name="safeDatabaseTarget" >
<target xsi:type="Database" name="databaseTarget" connectionString="..." commandText="...">
<!-- parameters -->
</target>
<target xsi:type="File" name="errorFallbackFile" fileName="c:\logs\db-fallback-errors.log"
layout="${longdate}|${level}|${logger}|${message}|${exception}" />
</target>
</targets>
<rules>
<!-- Thay vì ghi trực tiếp vào databaseTarget, ghi vào safeDatabaseTarget -->
<logger name="*" level="Error,Fatal" writeTo="safeDatabaseTarget" />
</rules>
Cấu hình này đảm bảo rằng ngay cả khi không kết nối được database, các log Error/Fatal vẫn được ghi lại vào một tệp tin dự phòng, giúp bạn không bỏ sót thông tin quan trọng.
Best Practices Khi Sử Dụng NLog
- Sử dụng các mức độ log phù hợp: Đừng lạm dụng Debug hoặc Trace trong môi trường production. Chỉ ghi những thông tin cần thiết.
- Log thông tin ngữ cảnh: Luôn thêm Request ID, User ID, hoặc các ID giao dịch khác vào log bằng MDC/NDLC để dễ dàng theo dõi luồng xử lý.
- Không log dữ liệu nhạy cảm: Cẩn thận không đưa mật khẩu, thông tin thẻ tín dụng, PII (Personally Identifiable Information) vào log.
- Sử dụng cấu hình XML: Đối với ứng dụng lớn và phức tạp, tệp cấu hình XML dễ quản lý và thay đổi hơn cấu hình bằng code.
- Tích hợp NLog với DI: Sử dụng `ILogger` được inject tự động bởi framework để tương thích với hệ thống logging của .NET Core.
- Xem xét logging bất đồng bộ: Luôn sử dụng `AsyncWrapper` cho các target có I/O cao (File, Database, Network) để tránh ảnh hưởng đến hiệu suất ứng dụng.
- Cấu hình internal log: Bật `internalLogFile` và `internalLogLevel` trong môi trường phát triển/staging để debug cấu hình NLog của chính nó.
- Kiểm tra cấu hình: Sử dụng `NLog.LogManager.Configuration.Create*******************()` hoặc xem internal log để kiểm tra xem NLog đã load và áp dụng cấu hình của bạn đúng chưa.
Kết Luận
NLog là một công cụ mạnh mẽ và linh hoạt cho việc quản lý log trong các ứng dụng .NET. Việc nắm vững cách cấu hình nâng cao các Target đa dạng, sử dụng Rule để điều hướng luồng log, tùy chỉnh định dạng bằng Layout và thêm ngữ cảnh bằng MDC/NDLC sẽ giúp bạn xây dựng các ứng dụng dễ debug, giám sát và bảo trì hơn rất nhiều. Đây là một kỹ năng không thể thiếu trên Lộ trình học ASP.NET Core và Hệ Sinh Thái .NET.
Logging không chỉ là công việc phụ, nó là một phần thiết yếu của vòng đời phát triển phần mềm. Hãy dành thời gian thử nghiệm các cấu hình khác nhau của NLog và áp dụng chúng vào dự án của bạn. Bạn sẽ thấy việc quản lý và giải quyết vấn đề trong ứng dụng trở nên hiệu quả hơn rất nhiều.
Ở các bài viết tiếp theo trong series “.NET Roadmap”, chúng ta sẽ tiếp tục khám phá những khía cạnh thú vị khác của .NET. Hãy theo dõi nhé!