Contents

.NET Logger 寫入資料庫 - NLog

NET Logger 寫入資料庫 - NLog

系統紀錄使用者操作、錯誤訊息或是活動日誌紀錄是很常見的需求,ASP.NET Core 預設上就使用了Logger Framework,並且透過這個Logger API 可以很輕鬆與第三方套件如 NLog、Log4Net 完美搭配,在紀錄上更方便了,這邊主要記錄幾個用過的套件,第一款是 NLog,NLog 也是我的首選,安裝說明文件相當清楚,基本上根據官方Getting started,一步步設定即可,這次趁著升級,順便把筆記重新翻寫一下。

環境

  • .NET Core 3.1
  • Nuget > NLog.Web.AspNetCore
  • Nuget > Microsoft.Data.SqlClient

實作

  1. 安裝 NLog.Web.AspNetCore

    /static/NET_Logger_寫入資料庫_NLog_690b063dd6604287bb92f2f2d0a8db88/2020-11-25_17-58-54.png

    官方文件有另外安裝 NLog,但我沒有安裝也可以正常執行,如果不是AspNetCore專案,建議還是安裝

  2. 安裝 Microsoft.Data.SqlClient( 或 System.Data.SqlClient)

    NLog 寫入MSSQL是使用傳統的 ADO.NET 方式,所以要額外安裝 Microsoft.Data.SqlClient

    安裝Microsoft.Data.SqlClient後要在nlog.config中的target指定dbProvider,如果是安裝System.Data.SqlClient似乎不指定也可以

  3. 新增 nlog.config 檔案

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    
    <?xml version="1.0" encoding="utf-8" ?>
    <!-- 設定internalLogFile可以得知NLog的內部錯誤,在初始設定時很有幫助 -->
    <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          autoReload="true"
          internalLogLevel="Info"
          internalLogFile="c:\temp\internal-nlog.txt">
    
      <!-- enable asp.net core layout renderers -->
      <extensions>
        <add assembly="NLog.Web.AspNetCore"/>
      </extensions>
    
      <!-- 儲存目標 -->
      <targets>
        <!-- 儲存目標類型為 "文字檔案"  -->
        <target xsi:type="File" name="allfile" fileName="c:\temp\nlog-all-${shortdate}.log"
                layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
    
        <!-- another file log, only own logs. Uses some ASP.NET core renderers -->
        <target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-own-${shortdate}.log"
                layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
    
    		<!-- 儲存目標類型為 "資料庫" -->
        <target name="database" xsi:type="Database" dbProvider="Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient">
          <connectionString>Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=LoggerTest;Integrated Security=True</connectionString>
          <commandText>
            INSERT INTO dbo.LogTest
            (Logged, Level, Message, Logger, Exception)
            VALUES
            (@Logged, @Level, @Message, @Logger, @Exception);
          </commandText>
    
          <parameter name="@Logged" layout="${date}" />
          <parameter name="@Level" layout="${level}" />
          <parameter name="@Message" layout="${message}" />
          <parameter name="@Logger" layout="${logger}" />
          <parameter name="@Exception" layout="${exception:tostring}" />
        </target>
      </targets>
    
      <!-- 規則設定 -->
      <rules>
        <!--All logs, including from Microsoft-->
        <logger name="*" minlevel="Trace" writeTo="allfile" />
    
        <!--Skip non-critical Microsoft logs and so log only own logs-->
        <logger name="Microsoft.*" maxlevel="Info" final="true" /> <!-- BlackHole without writeTo -->
        <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
    
        <!--這兩個規則一樣,也可以合併成一行,writeTo="ownFile-web, database"-->
        <logger name="*" minlevel="Trace" writeTo="database" />
      </rules>
    </nlog>
    

    基礎設定使用官方的即可,更進階的設定方法,官方也有提供文件

  4. 修改 ASP.NET Core program.cs 啟用 NLog

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    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();
              logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
          })
          .UseNLog();  // NLog: Setup NLog for Dependency injection
    
  5. 設定 appsettings.json

    ASP.NET Core 專案已經有基礎預設了,更進階的設定方法請參考,要特別留意,這邊的級別比nlog.config 高,也就是如果Default設定Information,即使 nlog.config 的規則為 Trace,也無法寫入Debug 和 Trace 紀錄

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    {
      "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
          "Default": "Trace",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "AllowedHosts": "*"
    }
    
  6. 測試寫入 Logs

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
    
        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }
    
        public IActionResult Index()
        {
            _logger.LogTrace("Level 0 Trace");
            _logger.LogDebug("Level 1 Debug");
            _logger.LogInformation("Level 2 Information");
            _logger.LogWarning("Level 3 Warning");
            _logger.LogError("Level 4 Error");
            _logger.LogCritical("Level 5 Critical");
            return View();
        }
    }
    
  7. 輸出結果

    /static/NET_Logger_寫入資料庫_NLog_690b063dd6604287bb92f2f2d0a8db88/2020-11-26_11-08-58.png

結論

NLog 簡單上手,較麻煩的反而是細節上的規則設定,過度的流水帳在追蹤問題時,是一種阻力,且在系統運作也會增加效能負擔,如何有效紀錄想要的東西更重要。

參考

.NET Core 與 ASP.NET Core 中的記錄

https://docs.microsoft.com/zh-tw/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0

NLog Getting started with ASP.NET Core 3

https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3

NLog Database target

https://github.com/NLog/NLog/wiki/Database-target

NLog Configuration file

https://github.com/NLog/NLog/wiki/Configuration-file