.NET 提供了標準化的 Log (Microsoft.Extensions.Logging),NLog 4.5 的時候支援了結構化日誌,同時也實作標準日誌,由於程式碼已經依賴了標準,引用 NLog 也只是彈指之間的設定,仍然可以使用舊的 NLog.Config。還不知道如何使用標準化日誌請看 這裡
開發環境
- VS 2019
- .NET Framework 4.8
- .NLog 4.7.5
- Microsoft.Extensions.Logging 3.1.9
- Microsoft.Extensions.DependencyInjection 3.1.9
.NET Framework 4.8 or .NET Core 3.1 桌面應用程式
NLog 4.5 之後支援了 .NET Standard
開一個 WInFrom App 、.NET Framework 4.8 專案並安裝以下套件
Install-package Microsoft.Extensions.Logging
Install-Package NLog.Extensions.Logging
Install-Package NLog.Config
Install-Package Microsoft.Extensions.Configuration.Json
NLog 組態設定
NLog.Config 套件裝完就會產生 NLog.config,修改一下檔案
- 增加兩個 target,檔案和 Console
- NLog Provider 本身就內建非同步寫入,
,正好彌補 Microsoft.Extensions.Logging 的 Log 方法沒有支援非同步<targets async="true">
<?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"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<!-- optional, add some variables
https://github.com/nlog/NLog/wiki/Configuration-file#variables
-->
<variable name="myvar" value="myvalue"/>
<variable name="generic" value="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}"/>
<!--
See https://github.com/nlog/nlog/wiki/Configuration-file
for information on customizing logging rules and outputs.
-->
<targets async="true">
<!--
add your targets here
See https://github.com/nlog/NLog/wiki/Targets for possible targets.
See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
-->
<!--
Write events to a file with the date in the filename.
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
-->
<target xsi:type="File" name="logfile" fileName="${basedir}/logs/${shortdate}.log"
layout="${generic}" />
<target xsi:type="Console" name="logconsole"
layout="${generic}" />
</targets>
<rules>
<!-- add your logging rules here -->
<!--
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f"
<logger name="*" minlevel="Debug" writeTo="f" />
-->
<logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
</rules>
</nlog>
LoggerFactory.Create
建立 LoggerFactory,下面代碼可以把它搬到靜態類別再實例化它,我就不實作了。
private static readonly ILogger<Form1> Logger;
static Form1()
{
var factory = LoggerFactory.Create(builder =>
{
builder.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("WindowsFormsApp1", LogLevel.Debug)
.AddNLog();
;
});
Logger = factory.CreateLogger<Form1>();
}
private void button1_Click(object sender, EventArgs e)
{
var button = (Button) sender;
var name = button.Name;
Logger.LogInformation(LogEvent.GenerateItem, "{name} 按鈕被按了", name);
Logger.LogInformation(LogEvent.UpdateItem, "執行更新");
Logger.LogInformation(LogEvent.GenerateItem, "完成");
}
除了輸出到 Console 之外,在 bin 資料夾也有 log 檔案
DI container
安裝以下套件
Install-package Microsoft.Extensions.DependencyInjection
Runner 依賴 ILogger<Runner>
public class Runner
{
private readonly ILogger<Runner> _logger;
public Runner(ILogger<Runner> logger)
{
this._logger = logger;
}
public void DoAction(string name)
{
this._logger.LogInformation(LogEvent.UpdateItem, "Doing hard work! {Action}", name);
}
}
Form1 依賴 ILogger<Runner>、Runner runner
public partial class Form1 : Form
{
public ILogger<Form1> Logger { get; set; }
public Runner Runner { get; set; }
public Form1()
{
this.InitializeComponent();
}
public Form1(ILogger<Form1> logger, Runner runner)
{
this.InitializeComponent();
this.Logger = logger;
this.Runner = runner;
}
private void button1_Click(object sender, EventArgs e)
{
var button = (Button) sender;
var name = button.Name;
this.Logger.LogInformation(LogEvent.GenerateItem, "{name} 按鈕被按了", name);
this.Logger.LogInformation(LogEvent.UpdateItem, "執行更新");
this.Runner.DoAction(name);
this.Logger.LogInformation(LogEvent.GenerateItem, "完成");
}
}
設定組態
private static IConfiguration CreateConfig()
{
var config = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory
.GetCurrentDirectory()) //From NuGet Package Microsoft.Extensions.Configuration.Json
.AddJsonFile("appsettings.json", true, true)
.Build();
return config;
}
實例化 ServiceProvider,注入 Form1、Runner
private static ServiceProvider CreateServiceProvider(IConfiguration config)
{
var serviceCollection = new ServiceCollection();
return serviceCollection.AddTransient<Form1>() // Runner is the custom class
.AddTransient<Runner>()
.AddLogging(loggingBuilder =>
{
// configure Logging with NLog
loggingBuilder.ClearProviders();
loggingBuilder.SetMinimumLevel(LogLevel.Trace);
loggingBuilder.AddNLog(config);
})
.BuildServiceProvider();
}
建立 ServiceProvider 後,取出 Form1,丟給 Application.Run
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var config = CreateConfig();
using (var serviceProvider = CreateServiceProvider(config))
{
var form = serviceProvider.GetService(typeof(Form1)) as Form;
Application.Run(form);
}
}
執行結果如下:
除了輸出到 Console 之外,在 bin 資料夾也有 log 檔案
參考
https://github.com/NLog/NLog/wiki/Getting-started-with-.NET-Core-2---Console-application
ASP.NET Core 3.1
新增一個 ASP.NET Core Web Application -> API 專案,安裝以下套件
Install-package NLog.Web.AspNetCore
手動加入 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"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
throwExceptions="false"
internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- optional, add some variables
https://github.com/nlog/NLog/wiki/Configuration-file#variables
-->
<variable name="myvar" value="myvalue"/>
<variable name="generic" value="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}"/>
<!--<variable name="generic" value="${longdate}|${level}|${message} |${all-event-properties} ${exception:format=tostring}"/>-->
<!--
See https://github.com/nlog/nlog/wiki/Configuration-file
for information on customizing logging rules and outputs.
-->
<targets async="true">
<!--
add your targets here
See https://github.com/nlog/NLog/wiki/Targets for possible targets.
See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers.
-->
<!--
Write events to a file with the date in the filename.
<target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}" />
-->
<target xsi:type="File" name="logfile" fileName="${basedir}/logs/${shortdate}.log"
layout="${generic}" />
<target xsi:type="Console" name="logconsole"
layout="${generic}" />
</targets>
<rules>
<!-- add your logging rules here -->
<!--
Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f"
<logger name="*" minlevel="Debug" writeTo="f" />
-->
<logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
</rules>
</nlog>
將檔案複製到輸出位置
清掉預設的 Log Provider,換成 NLog .UseNLog
public class Program
{
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); })
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(LogLevel.Trace);
})
.UseNLog();
}
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
}
Controller 建構函數依賴 ILogger<WeatherForecastController>
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
this._logger = logger;
}
}
在 Action 裡面就可以使用 ILogger<WeatherForecastController> 的實例寫入日誌this._logger.LogInformation
[HttpGet]
public IActionResult Get()
{
//using (this._logger.BeginScope($"Scope Id:{Guid.NewGuid()}"))
using (this._logger.BeginScope("Scope Id:{id}", Guid.NewGuid()))
{
this._logger.LogInformation(LogEvent.GenerateItem, "開始,訪問 WeatherForecast api");
this._logger.LogInformation(LogEvent.TestItem, "執行流程");
var random = new Random();
var result = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = random.Next(-20, 55),
Summary = Summaries[random.Next(Summaries.Length)]
})
.ToArray();
this._logger.LogInformation(LogEvent.GenerateItem, "結束流程");
return this.Ok(result);
}
}
執行結果
NLog 設定相關資源
targets
輸出目標,NLog 官方實作了很多的目標,官方如果找不到可以嘗試到 Nuget 找,比如 NLog.Slack、NLog.Telegram
https://nlog-project.org/config/?tab=targets
layout-renderers
日誌呈現內容,NLog 提供許多的方法讓你不需要寫扣就能取得相關資訊,比如 ${hostname} 取得電腦名稱
https://nlog-project.org/config/?tab=layout-renderers
rule
根據條件寫入目標,例如
logger name 等於 * 任意字串,且 log level 最小等於 Trace,則寫入 logfile,logconsole 兩個 target
<logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
Fluent API
NLog 還可以用 Fluent API 語法操作,由於這不是 Microsoft.Extensions.Logging 的標準 ,所以沒有支援
using NLog.Fluent;
_logger.Info()
.Message("This is a test fluent message '{0}'.", DateTime.Now.Ticks)
.Property("Test", "InfoWrite")
.Write();
NLog configuration with appsettings.json
如果不喜歡 nlog.config 也可以用 appsettings.json,請參考以下連結:
https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-configuration-with-appsettings.json
相關文章
通過標準化的 Microsoft.Extensions.Logging 實現日誌紀錄
專案位置
https://github.com/yaochangyu/sample.dotblog/tree/master/Log/Lab.MsLogging.ForNLog
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET