在這篇文章中,我們將建立起一個.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,這個中介軟件類別將會用來記錄下每次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 時候,所寫入的例外異常日誌內容
沒有留言:
張貼留言