日志系统官方文档:
https://learn.microsoft.com/zh-cn/dotnet/core/extensions/logging?tabs=command-line
日志级别:
Trace<Debug<Information<Warning<Error<Critical
日志提供者(LoggingProvider):
把日志输出到控制台、文件、数据库等。
对于业务代码只要注入日志对象记录日志即可,具体哪些日志输出到哪里、什么样的格式、是否输出等都有配置或者初始化代码决定。

控制台日志

安装:Microsoft.Extensions.Logging、Microsoft.Extensions.Logging.Console

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
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Test {
internal class Program {
static void Main(string[] args) {
ServiceCollection services = new ServiceCollection();
services.AddLogging((logBuilder) => {
logBuilder.AddConsole();
//logBuilder.AddEventLog();安装Microsoft.Extension.Logging.EventLog,这个只能windows用所以用的很少
logBuilder.SetMinimumLevel(LogLevel.Debug);//设置最低级别,不写这个看不到Debug信息
});
services.AddScoped<Test1>();
using(var sp = services.BuildServiceProvider()) {
var test1 = sp.GetRequiredService<Test1>();
test1.Test();
}
}
}
public class Test1 {
//<>里面一般为当前类,这样可以直接从找到是哪个类记录了日志
private readonly ILogger<Test1> logger;
public Test1(ILogger<Test1> logger) {
this.logger = logger;
}
public void Test() {
logger.LogDebug("开始执行数据库同步");
logger.LogDebug("连接数据库成功");
logger.LogWarning("查找数据失败,重试第一次");
logger.LogWarning("查找数据失败,重试第二次");
logger.LogError("查找数据最终失败");
try {
File.ReadAllText("A:/1.txt");
logger.LogDebug("读取文件成功");
}catch(Exception ex) {
//通过该方法记录异常,可以传入异常对象
logger.LogError(ex,"读取文件失败");
}
}
}
}

NLog文本日志

NLog官方文档:
https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-6
文本日志一般按照日期区分,为了避免磁盘被撑爆可以限制日志总个数或者总大小。
1、.Net没有内置文本日志提供者。第三方有Log4Net、NLog、Serilog等。老牌的Log4Net另搞一套,不考虑。
安装:
NLog.Extensions.Logging(using NLog.Extension.Logging;)。2、项目根目录下建nlog.config选择文件较新则赋值,注意文件名的大小写(考虑linux)。也可以是其他文件名,但是需要单独配置。约定大于配置。
3、增加logBuilder.AddNLog();

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
//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: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:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />

<!--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>
1
2
3
4
5
6
7
8
9
10
11
12
13
using NLog.Extensions.Logging;
//============================
static void Main(string[] args) {
ServiceCollection services = new ServiceCollection();
services.AddLogging((logBuilder) => {
logBuilder.AddNLog();
});
services.AddScoped<Test1>();
using(var sp = services.BuildServiceProvider()) {
var test1 = sp.GetRequiredService<Test1>();
test1.Test();
}
}

nlog有多个traget表示日志输出到哪,rules中有多个logger,会从上往下匹配,用来配置日志规则(哪个级别输出到哪个target),匹配到final=true就不再往下匹配。

日志分类、过滤

1、为什么要分类?不同级别或者不同模块的日志要记录到不同的地方。
2、为什么要过滤?项目不同阶段(比如刚上线和稳定后)需要记录的日志不同。严重错误可以调用短信Provider等。
3、搞一个新的类,放到SystemServices这个名称空间下,然后输出一些日志。然后调用很多次日志执行。nlog.config如备注所改。

重要的参数

archiveAboveSize为”单个日志文件超过多少字节就把日志存档”,如果不设定maxArchiveFiles参数,则文件日志存档文件的数量会一直增加。
maxArchiveFiles指定数量个数的存档文件,旧的会被删掉。
maxArchiveDays设定保存若干天的日志存档。
通过设定这些参数实现滚动日志策略。

rules

rules节点下可以添加多个logger,每个logger都有name属性,name是通配符格式的。
logger节点的minlevel属性和maxlevel属性,表示这个logger接受日志的最低级别和最高级别。
日志输出时,会从上往下匹配rules节点下所有的logger,若发现当前日志的分类名和level符合这个logger的name的通配符,就会把日志输出给这个logger。如果匹配多个logger,就会把这条日志输出给多个logger。但是如果一个logger设置了final=”true”,那么如果匹配到这个logger,就不继续向下匹配其他logger了。

其他

NLog部分功能和.NET的Logging重复,比如分类、分级、各种Provider。
为了避免冲突,如果用NLog,建议不要再配置.NET的分级等。

结构化日志

结构化日志比普通文本更利于日志的分析,比如统计“邮件发送失败”错误发生了多少次。

集中化日志

集群化配置环境中,有N多个服务器,如果每个服务器都把日志记录到本地文件,不便于查询、分析。
需要把日志保存到集中化的日志服务器中。
NLog可以配置结构化日志不过比较麻烦更推荐——Serilog
Nuget安装:Serilog.AspNetCore。

1
2
3
4
5
6
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.Enrich.FromLogContext()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
builder.AddSerilog();

要记录的结构化数据通过占位符来输出:
logger.LogWaring(“新增用户{@person}”,new {Id = 3,Name = “zack”});
同样可以输出到文件、数据库、MongoDB等。

集中日志服务

使用NLog、Serilog等可以把日志记录到数据库,MongoDB等地方,就可以实现“结构化、集中日志服务”,不过需要自己编写日志分析程序。
公有云厂商一般都提供了日志云服务,都带有分析功能,都有对应Serilog插件,即使没有提供,开发一个插件也不麻烦。
如果没有用云平台或者想自己控制日志服务,可以使用Exceptionless或者ELK等

总结

普通项目用NLog输出到文本文件即可,根据需要设定过滤、分类规则。
集群部署的项目用Serilog+集中式日志服务。如果需要记录结构化日志,再进行结构化输出。
如果用云服务的就够了,就用云服务,免得自己部署;如果想自己控制日志数据就用自部署Exceptionless或者ELK等。