在.NET9 Web API 專案使用NLog教學與紀錄Trace Id
在這篇文章中,我們將建立起一個.NET9 的ASP.NET Core Web API 項目,並且說明如何安裝與設定和使用NLog,由於這是一個Web API 項目,所以,將會透過Trace Id 來記錄下每次HTTP 請求的時候,與此有關的Log 有哪些,透過NLog 來記錄Trace Id 與Exception 的紀錄
請依照底下的操作,建立起這篇文章需要用到的練習項目
打開Visual Studio 2022 IDE 應用程式 從[Visual Studio 2022] 對話窗中,點選右下方的[建立新的項目] 按鈕 在[建立新項目] 對話窗右半部 切換[所有語言(L)] 下拉選單控制項為[C#] 切換[所有項目類型(T)] 下拉選單控制項為[Web API] 在中間的項目模板清單中,找到並且點擊[ASP.NET Core Web API] 項目模板選項 此專案模板可用於ASP.NET Core 控制器或最小API 建立RESTful Web API,並可選擇性支援OpenAPI 和驗證
點擊右下角的[下一步] 按鈕 在[設定新的項目] 對話窗 找到[項目名稱] 字段,輸入 csNlogTraceId
作為項目名稱 在剛剛輸入的[項目名稱] 欄位下方,確認沒有勾選[將解決方案與項目至於相同目錄中] 這個檢查盒控制項 點擊右下角的[下一步] 按鈕 現在將會看到[其他資訊] 對話窗 在[架構] 欄位中,請選擇最新的開發框架,這裡選擇的[架構] 是 : .NET 9.0 (標準字詞支援)
在[驗證類型] 選擇無 在這個練習中,需要去勾選[不要使用最上層陳述式(T)] 這個檢查盒控制項 這裡的這個操作,可以由讀者自行決定是否要勾選這個檢查盒控制項
請點擊右下角的[建立] 按鈕 稍微等候一下,這個項目將會建立完成
因為開發此專案時會使用這些NuGet 套件,請依照底下說明,將需要用到的NuGet 套件安裝。
請依照底下說明操作步驟,將這個套件安裝到專案內
鼠標右擊[方案總管] 視窗內的[專案節點] 下方的[依賴] 節點 從彈出功能表清單中,點選[管理NuGet 套件] 這個功能選項清單 此時,將會看到[NuGet: csNlogTraceId] 視窗 切換此視窗的標籤頁次到名稱為[瀏覽] 這個標籤頁次 在左上方找到一個搜尋文字輸入盒,在此輸入 NLog.Web.AspNetCore
在視窗右方,將會看到該套件詳細說明的內容,其中,右上方有的[安裝] 按鈕 點擊這個[安裝] 按鈕,將這個套件安裝到專案內
建立一個中介軟件類別TraceIdMiddleware 在這個專案中,我們將建立一個中介軟件類別TraceIdMiddleware,這個中介軟件類別將會用來記錄下每次HTTP 請求的Trace Id,這個值將由[HttpContext.TraceIdentifier] 來取得,這個Trace Id 將會被NLog 用來記錄下每次HTTP 請求的Log 訊息,而這裡將會透過了NLog.MappedDiagnosticsLogicalContext 這個類別來設定TraceId 的值。
在[nlog.config] 內,若想要顯示TraceId,可以透過 ${mdlc:TraceId}
這個格式來取得TraceId 的值
namespace csNlogTraceId ;
public class TraceIdMiddleware
{
private readonly RequestDelegate _next ;
private readonly ILogger < TraceIdMiddleware > logger ;
public TraceIdMiddleware ( RequestDelegate next ,
ILogger < TraceIdMiddleware > logger )
{
_next = next ;
this . logger = logger ;
}
public async Task InvokeAsync ( HttpContext context )
{
try
{
var traceId = context . TraceIdentifier ;
NLog . MappedDiagnosticsLogicalContext . Set ( "TraceId" , traceId ) ;
await _next ( context ) ;
}
catch ( Exception ex )
{
// 记录异常
logger . LogError ( ex , "發生不可預期的錯誤" ) ;
// 将异常重新抛出
//throw;
}
}
} 在[InvokeAsync] 方法內,將會使用try...catch 敘述將其捕捉起來,並且透過NLog 來記錄下這個Exception 的訊息,這樣,當發生Exception 的時候,將會記錄下這個Exception 的訊息,並且該Exception 的訊息將包含TraceId 的值
找到並且打開[Program.cs] 這個檔案 使用底下程式碼替換掉這個檔案的內容 using NLog ;
using NLog . Web ;
namespace csNlogTraceId
{
public class Program
{
public static void Main ( string [ ] args )
{
var logger = NLogBuilder . ConfigureNLog ( "nlog.config" ) . GetCurrentClassLogger ( ) ;
try
{
var builder = WebApplication . CreateBuilder ( args ) ;
builder . Logging . ClearProviders ( ) ;
builder . Logging . SetMinimumLevel ( Microsoft . Extensions . Logging . LogLevel . Trace ) ;
builder . Host . UseNLog ( ) ;
// Add services to the container.
builder . Services . AddControllers ( ) ;
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder . Services . AddOpenApi ( ) ;
var app = builder . Build ( ) ;
app . UseMiddleware < TraceIdMiddleware > ( ) ;
// Configure the HTTP request pipeline.
if ( app . Environment . IsDevelopment ( ) )
{
app . MapOpenApi ( ) ;
}
app . UseHttpsRedirection ( ) ;
app . UseAuthorization ( ) ;
app . MapControllers ( ) ;
app . Run ( ) ;
}
catch ( Exception ex )
{
logger . Error ( ex , "Stopped program because of an exception" ) ;
throw ;
}
finally
{
LogManager . Shutdown ( ) ;
}
}
}
} 這裡先使用[NLogBuilder.ConfigureNLog("nlog.config")] 設定NLog 的設定文件,接著,透過[GetCurrentClassLogger()] 來取得Logger 的實例,這樣,就可以透過這個Logger 來記錄下Log 訊息
接著,透過[builder.Logging.ClearProviders()] 來清除原本的Log 訊息提供者,接著,透過[builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace)] 來設定最低的Log 等級為Trace,這樣,就可以記錄所有的Log 訊息
接著,透過[builder.Host.UseNLog()] 設定使用NLog 來記錄Log 訊息
接著,透過[app.UseMiddleware()] 來設定使用TraceIdMiddleware 這個中間軟件類別,這樣,每次HTTP 請求的時候,將會記錄下TraceId 的值
滑鼠右擊專案節點 從彈出的功能表清單中,點選[新增項目] 功能選項 在[新增項目] 對話窗中,點選對話窗左方的[已安裝] > [C#] > [ASP.NET Core] > [資料] 在[新增項目] 對話窗右方,找到[XML 檔] 這個項目,並且點擊它 在[名稱] 欄位中,輸入 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"
throwConfigExceptions =" true"
internalLogLevel =" Info"
internalLogFile =" internal-nlog.log" >
<!-- 日志级别:Trace < Debug < Info < Warn < Error < Fatal -->
<targets >
<!-- 文件输出目标 -->
<target xsi : type =" AsyncWrapper" name =" File_async"
overflowAction=" Discard"
queueLimit=" 500000"
batchSize=" 500" >
<target name =" file" xsi : type =" File" fileName =" logs/logfile-${shortdate}.log"
layout=" ${longdate}|${mdlc:TraceId}|${level:uppercase=true}|${logger}|${message}|${exception:format=toString,StackTrace}" />
</target >
<!-- 控制台输出目标 -->
<target xsi : type =" AsyncWrapper" name =" Console_async"
overflowAction=" Discard"
queueLimit=" 500000"
batchSize=" 500" >
<target name =" console" xsi : type =" Console"
layout=" ${longdate}|${mdlc:TraceId}|${level:uppercase=true}|${logger}|${message}|${exception:format=toString,StackTrace}" />
</target >
</targets >
<rules >
<!-- Suppress output from Microsoft framework when non-critical -->
<logger name =" System.*" finalMinLevel =" Warn" />
<logger name =" Microsoft.*" finalMinLevel =" Warn" />
<!-- Keep output from Microsoft.Hosting.Lifetime to console for fast startup detection -->
<logger name =" Microsoft.Hosting.Lifetime*" finalMinLevel =" Info" writeTo =" console_async" />
<!-- 最低级别为 Info,输出到文件和控制台 -->
<logger name =" *" minlevel =" Info" writeTo =" file_async,console_async" />
</rules >
</nlog > 這裡設定了兩個Target,一個是File_async,另一個是Console_async,這樣,就可以將Log 訊息同時輸出到檔案與Console 上。
對於非同步的日誌寫入,這裡設定了[AsyncWrapper] 這個Target,這樣,就可以將Log 訊息寫入到檔案與Console 上,這樣,就可以提高Log 訊息的寫入效能,這裡用到的參數有[overflowAction] 這個參數,這個參數可以設定當Log 訊息寫入的速度超過了設定的速度時,該如何處理,這裡設定為[Discard],這樣,當Log當訊息寫入的速度超過了設定的速度時,就會捨棄掉這些Log 訊息,這樣,就可以避免Log 訊息寫入的速度過快,而導致系統的效能下降。對於[queueLimit] 這個參數,這個參數可以設定Log 訊息的隊列上限,這樣,就可以避免Log 訊息寫入的速度過快,而導致系統的效能下降,這裡設定為500000,這樣,就可以讓Log訊息的隊列上限為500000,這樣,就可以避免Log 訊息寫入的速度過快,而導致系統的效能下降。對於[batchSize] 這個參數,這個參數可以設定Log 訊息的批次寫入數量,這樣,就可以提高Log 訊息的寫入效能,這裡設定為500,這樣,就可以讓Log 訊息的批次寫入數量為500,這樣,就可以提高Log 訊息的寫入效能。
對於[layout] 這個參數,這個參數可以設定Log 訊息的格式,這裡設定為 ${longdate}|${mdlc:TraceId}|${level:uppercase=true}|${logger}|${message}|${exception:format=toString,StackTrace}
,這樣,就可以讓Log 訊息的格式為 [日期時間]|TraceId|Log等級|Logger名稱|Log訊息|Exception訊息
,這樣,就可以讓Log 訊息的格式更清晰易讀。
在[rules] 區段內,這裡設定了兩個Logger,一個是 System.*
,另一個是 Microsoft.*
,這樣,就可以讓這兩個Logger 的Log 訊息等級最低為Warn,這樣,就可以避免這兩個Logger 的Log訊息過多,而導致系統的效能下降。對於 Microsoft.Hosting.Lifetime*
這個Logger,這裡設定了Log 訊息的等級最低為Info,這樣,就可以讓這個Logger 的Log 訊息等級最低為Info,這樣,就可以讓這個Logger 的Log 訊息在Console 上顯示,這樣,就可以讓開發者在開發時,可以看到這個Logger 的Log 訊息,這樣,就可以讓開發者更容易了解這個Logger 的Log 訊息。對於 *
這個Logger,這裡設定了Log 訊息的等級最低為Info,這樣,就可以讓這個Logger 的Log 訊息等級最低為Info,這樣,就可以讓這個Logger 的Log 訊息在Console 上顯示,這樣,就可以讓開發者在開發時,可以看到這個Logger 的Log 訊息,這樣,就可以讓開發者更容易了解這個Logger 的Log 訊息。
在[Controllers] 資料夾內,建立一個新的C# 類別檔案,並命名為 ExceptionController.cs
使用底下的代碼替換掉這個檔案的內容 using Microsoft . AspNetCore . Mvc ;
using NLog ;
namespace csNlogTraceId . Controllers ;
[ ApiController ]
[ Route ( "[controller]" ) ]
public class ThrowExceptionController : ControllerBase
{
private readonly ILogger < ThrowExceptionController > _logger ;
public ThrowExceptionController ( ILogger < ThrowExceptionController > logger )
{
_logger = logger ;
}
[ HttpGet ]
public IEnumerable < WeatherForecast > Get ( )
{
_logger . LogInformation ( "開始執行測試例外異常 API" ) ;
throw new Exception ( "這是一個測試例外異常" ) ;
}
} 在Visual Studio 2022 IDE 中,按下 F5
鍵,或是在功能表中選擇[調試] -> [開始調試],來執行這個程序 當項目啟動之後,並沒有看到任何瀏覽器出現
這裡是項目一啟動之後,Console顯示的Log 內容
開啟瀏覽器輸入 https://localhost:7014/WeatherForecast
這個網址,這時,將會看到瀏覽器顯示天氣預報資訊的消息
此時,在Console 中,將會看到底下內容,顯示的呼叫這個API 時候,所寫入的日誌內容
開啟瀏覽器輸入 https://localhost:7014/ThrowException
這個網址,這時,這個API 將會拋出例外異常到Log 內
此時,在Console 中,將會看到底下內容,顯示的呼叫這個API 時候,所寫入的例外異常日誌內容