作为技术人员的我们,应该如何有效的书写日志,如何更好地查看日志,如何有效地定位BUG呢? 我希望您花时间看完,这对您的职业生涯将有非常大的帮助。

什么是Nlog

NLog is a flexible and free logging platform for various .NET platforms, including .NET standard. NLog makes it easy to write to several targets. (database, file, console) and change the logging configuration on-the-fly.

NLog是一个灵活的、免费的日志记录平台,适用于各种。net平台,包括。net标准平台。NLog使写入多个目标变得容易。(数据库、文件、控制台),并动态更改日志记录配置。

Alt text

为什么推荐使用它

很多开发人在编写程序代码时,常常忘记处理异常 try catch ,导致没有记录异常日志,在bug来临时,无处安放的小手这才开始try catch

  • Nlog可以检测系统引发的异常,自动记录日志,避免我们忘记try catch
  • 同时支持自定义日志模板、写入渠道等
  • 还支持写入seq,一种微服务的日志服务框架

如何使用

1、在 csproj 中手动添加依赖项或使用 NuGet 添加依赖项

<ItemGroup>
<PackageReference Include="NLog.Web.AspNetCore" Version="4.*" />
<PackageReference Include="NLog" Version="4.*" />
</ItemGroup>

2、创建一个配置文件。

在项目的根目录中创建 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"
internalLogLevel="Info"
internalLogFile="c:\temp\internal-nlog-AspNetCore.txt">

<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>

<!-- the targets to write to -->
<targets>
<!-- File Target for all log messages with basic details -->
<target xsi:type="File" name="allfile" fileName="c:\temp\nlog-AspNetCore-all-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" />

<!-- File Target for own log messages with extra web details using some ASP.NET core renderers -->
<target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-AspNetCore-own-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}" />

<!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection -->
<target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" />
</targets>

<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />

<!--Output hosting lifetime messages to console target for faster startup detection -->
<logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" />

<!--Skip non-critical Microsoft logs and so log only own logs (BlackHole) -->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<logger name="System.Net.Http.*" maxlevel="Info" final="true" />

<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>

有关配置文件的更多详细信息,请单击此处
Configuration options

3、更新程序

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using NLog.Web;

namespace ASP.NET_Core_5_NLog_Example
{
public class Program
{
public static void Main(string[] args)
{
var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().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(LogLevel.Trace);
})
.UseNLog(); // NLog: Setup NLog for Dependency injection
}
}

4、写入日志

将 ILogger 注入控制器:

using Microsoft.Extensions.Logging;

public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
_logger.LogDebug(1, "NLog injected into HomeController");
}

public IActionResult Index()
{
_logger.LogInformation("Hello, this is the index!");
return View();
}

6、示例输出

Alt text

使用appsettings.json配置Nlog

https://github.com/NLog/NLog.Extensions.Logging/wiki/NLog-configuration-with-appsettings.json

以下是我已经配置好的json ,可直接拷贝使用

"Logging": {
"LogLevel": {
"Default": "Debug",
"System": "Warning",
"Microsoft": "Warning",
"Hangfire": "Warning"
},
"NLog": {
"IncludeScopes": true //true启用nlog ,false 禁用
}
},
"NLog": {
"autoReload": true,
"throwConfigExceptions": true,
"internalLogLevel": "Info",
"internalLogFile": "logs/internal-nlog.txt", //修改项目名称即可
"extensions": [
{
"assembly": "NLog.Extensions.Logging"
},
{
"assembly": "NLog.Web.AspNetCore"
},
{
"assembly": "NLog.Targets.Seq"
}
],
"variables": {
"var_logdir": "logs" //修改项目名称即可
},
"time": {
"type": "AccurateUTC"
},
"default-wrapper": {
"type": "AsyncWrapper",
"overflowAction": "Block"
},
"targets": {
"all-file": {
"type": "File",
"fileName": "${var_logdir}/nlog-all-${shortdate}.log",
"layout": {
"type": "JsonLayout",
"Attributes": [
{
"name": "eventId",
"layout": "${event-properties:item=EventId_Id:whenEmpty=0}"
},
{
"name": "time",
"layout": "${longdate}"
},
{
"name": "level",
"layout": "${level:upperCase=true}"
},
{
"name": "message",
"layout": "${message}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "httpRequestId",
"layout": "${aspnet-traceidentifier}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "url",
"layout": "${aspnet-request-url}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "method",
"layout": "${aspnet-request-method}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "queryString",
"layout": "${aspnet-request-querystring}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "referrer",
"layout": "${aspnet-request-referrer}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "host",
"layout": "${aspnet-request-host}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "serverIp",
"layout": "${local-ip}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "clientIp",
"layout": "${aspnet-request-ip}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "iisSiteName",
"layout": "${iis-site-name}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "responseCode",
"layout": "${aspnet-response-statuscode}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "identity",
"layout": "${aspnet-user-identity}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
},
{
"name": "exception",
"layout": "${exception:format=tostring}",
"escapeUnicode": false,
"escapeForwardSlash": false,
"includeEmptyValue": true
}
]
}
},
"own-console": {
"type": "LimitingWrapper",
"interval": "00:00:01",
"messageLimit": 100,
"target": {
"type": "ColoredConsole",
"layout": "${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|${callsite}",
"rowHighlightingRules": [
{
"condition": "level == LogLevel.Error",
"foregroundColor": "Red"
},
{
"condition": "level == LogLevel.Fatal",
"foregroundColor": "Red",
"backgroundColor": "White"
},
{
"condition": "level == LogLevel.Info",
"foregroundColor": "Blue"
},
{
"condition": "level == LogLevel.Warning",
"foregroundColor": "Yellow"
}
]
}
},
"seq": {
"type": "BufferingWrapper",
"bufferSize": 200,
"flushTimeout": 2000,
"slidingTimeout": false,
"target": {
"type": "Seq",
"serverUrl": "http://192.168.1.227:5341/", //修改接受地址即可 http://dev.iduo.cc:8008/
"apiKey": "d2Zt3M8OZCtgUVkjctyx", //修改ApiKey 即可
"properties": [
{
"name": "eventId",
"value": "${event-properties:item=EventId_Id:whenEmpty=0}"
},
{
"name": "time",
"value": "${longdate}"
},
{
"name": "level",
"value": "${level:upperCase=true}"
},
{
"name": "message",
"value": "${message}"
},
{
"name": "httpRequestId",
"value": "${aspnet-traceidentifier}"
},
{
"name": "url",
"value": "${aspnet-request-url}"
},
{
"name": "method",
"value": "${aspnet-request-method}"
},
{
"name": "queryString",
"value": "${aspnet-request-querystring}"
},
{
"name": "referrer",
"value": "${aspnet-request-referrer}"
},
{
"name": "host",
"value": "${aspnet-request-host}"
},
{
"name": "serverIp",
"value": "${local-ip}"
},
{
"name": "clientIp",
"value": "${aspnet-request-ip}"
},
{
"name": "iisSiteName",
"value": "${iis-site-name}"
},
{
"name": "responseCode",
"value": "${aspnet-response-statuscode}"
},
{
"name": "exception",
"value": "${exception:format=tostring}"
}
]
}
}
},
"rules": [
//{
// "logger": "*",
// "minLevel": "Info",
// "writeTo": "all-file"
//},
{
"logger": "*",
"minLevel": "Error",
"writeTo": "own-console"
},
//{
// "logger": "*",
// "minLevel": "Info",
// "writeTo": "seq"
//}
]
}

对接Seq

部署

开放 5340 和 5341 端口

docker部署命令

docker run \
--name seq \
-d \
--restart unless-stopped \
-e ACCEPT_EULA=Y \
-v /home/administrator/applogs:/data \
-p 5340:80 \
-p 5341:5341 \
datalust/seq

此参数是seq日志文件存放路径,根据实际情况做出调整

-v /home/administrator/applogs:/data 

程序修改

安装NuGet包 NLog.Targets.Seq

5341端口是写入日志的接口,换掉时,对应的appsetting.json 里 seq的 配置地址Nlog:targets:seq:serverUrl也需要变化。

针对每个项目使用不同的apiKey Nlog:targets:seq:apiKey

Alt text

Alt text

示例效果

Alt text