本人老卡卡西了。 内容源自知乎、大佬博客、不知名网站
本文只起到介绍作用。只起到介绍作用。只起到介绍作用。
深入学习请自行查找官网、wiki或帮助文档。

日志
当系统上线被广泛使用或者时间久远之后,不可避免日志的大量出现。且日志本身作为一种数据,也有其重要的价值。因此,如何有效地对其进行查询以及最大价值化地分析处理便成了一个重要的问题。
对于日志的处理,需要权衡对开发者的友好性与对程序解析的方便性。
非结构化日志
传统的非结构化日志更倾向于前者。当开发者想要记录一段日志时,他可以很简单地加上一行代码,即可达成其目的。这样的日志以开发者的角度来看,清晰易懂,十分友好。但如果要使用程序去查找指定条件的相关日志,则很难高效地完成这一要求,因为需要对每个日志进行字符串解析。
结构化日志
结构化日志,也被称为语义化日志。其作用有二,利于查询与方便分析。在结构化日志里,是以对象而非字符串处理日志内容的。
消息模板规范是结构化日志的通用语法,其是一个与开发语言无关的规范,能以特定格式捕获及呈现结构化日志,同时提供对开发者与程序解析的友好支持。
例子1:
// 非结构化日志
log.Debug($"User id is: {userId}");
在大多数日志记录框架中,这只是转换为日志文件中的文本。但是知道记录的值称为userId
并能够搜索该值实际上非常有用。结构化日志则会保持属性一直可用到目的地。
// 结构化日志特殊语法
log.Debug("User id is {@userId}", userId);
非结构化日志和结构化日志,它们之间具有本质的区别,在结构化日志里,是以对象而非字符串处理日志内容的。
例子2:
// 非结构化语法 {0} 占位符
logger.Info("Logon by user:{0} from ip_address:{1}", "Kenny", "127.0.0.1");
// 输出
2018-12-22 16:29:29.2793|Info|Logon by user:Kenny from ip_address:127.0.0.1
// 结构化日志语法 {filedname} 字段名 前面可以有@或$前缀代表不同格式
logger.Info("Logon by {user} from {ip_address}", "Kenny", "127.0.0.1");
// 输出
2018-12-22 16:29:29.2976|Info|Logon by "Kenny" from "127.0.0.1"
// 非结构化转换为json格式
{ "time": "2018-12-22 16:30:15.1314", "level": "INFO", "message": "Logon by user:Kenny from ip_address:127.0.0.1" }
// 结构化转换为json格式
{ "time": "2018-12-22 16:30:15.1569", "level": "INFO", "message": "Logon by \"Kenny\" from \"127.0.0.1\"", "user": "Kenny", "ip_address": "127.0.0.1" }
因为user
被作为对象的属性独立分离出来,在做程序处理时,可以很方便地以其为条件进行筛选。这对于查询或者分析日志是极为重要的。
NLog插件
在.NET生态圈中,最早被广泛使用的日志库可能是派生自Java世界里的Apache log4net。而其后来者,莫过于NLog。
NLog中对于结构化日志的支持是在4.5版本才开始的。这一改动并不会破坏原有的代码,而如果想要使用新的特性,则只要用符合消息模板的语法编写所需的代码即可。
Object o = null;
logger.Info("Test {value1}", o);
logger.Info("Test {value1}", new DateTime(2018,03, 25));
logger.Info("Test {value1}", new List<string> { "a", "b" });
logger.Info("Test {value1}", new[] { "a", "b" });
logger.Info("Test {value1}", new Dictionary<string, int> { { "key1", 1 }, { "key2", 2 } });
//Test NULL
//Test 25-3-2018 00:00:00
//Test "a", "b"
//Test "a", "b"
//Test "key1"=1, "key2"=2
var order = new Order
{
OrderId = 2,
Status = OrderStatus.Processing
};
logger.Info("Test {value1}", order);
logger.Info("Test {@value1}", order);
logger.Info("Test {value1}", new { OrderId = 2, Status = "Processing"});
logger.Info("Test {@value1}", new { OrderId = 2, Status = "Processing"});
//Test MyProgram.Program+Order
//Test {"OrderId":2, "Status":"Processing"}
//Test { OrderId = 2, Status = Processing }
//Test {"OrderId":2, "Status":"Processing"}
代码的格式化结果依据数据的类型而定。
- 字符串类型将被双引号包围
- 数值类型没有引号
- null显示为
NULL
- 列表或数组类型,以逗号分隔,例如:
"item1", "item2"
- 字典类型,健与值之间用等号相联,例如:
"key1"="value1", "key2"="value2"
- 对象类型,调用
ToString
方法显示结果
此外,还可以有@
与$
符号:
@
以JSON格式格式化数据,( Serilog保留传入的对象的结构 )
- 省略前置符号会识别简单类型,其他对象类型会调用ToString方法
$
强制调用ToString方法,使其字符串化
而将日志输出格式改成JSON的方法,是在NLog.config配置文件里将布局切换成JsonLayout
类型,同时设置includeAllProperties
为true
,以显示所有对象属性。
<target name="console" xsi:type="Console">
<layout xsi:type="JsonLayout" includeAllProperties="true">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
</layout>
</target>
Serilog插件 (推荐)
Serilog通常被称为.NET平台上的新日志记录框架,而NLog被认为是“旧的”。Serilog于2013年推出,NLog于2006年推出,他们俩都有悠久的历史,并且周围都有很棒的社区。
Serilog最初是由Nicholas Blumhardt于2013年创建的。当它问世时,它通过引入结构化日志而彻底改变了.NET日志空间。以前的所有框架或多或少都将日志消息视为串联字符串。随着更多NoSQL数据库(如MongoDB和Elasticsearch)的出现,社区需要一种能够利用NoSQL长期发展的新功能和结构数据的方法。Serilog成为增长最快的.NET日志记录框架。
Serilog的一项不错的功能是其强类型化(用C#编写)的配置语言。虽然还支持XML配置,但使用它并没有太大好处。这是一个非常简单的示例,说明如何配置Serilog将日志消息写入文件:
// API更现代,更易于设置,维护更好
Log.Logger = new LoggerConfiguration()
.WriteTo.File("log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
对比一下NLog,与Serilog一样,NLog可以使用基于XML或C#的配置(大多数人使用XML来配置)。这是将日志消息写入文件的示例:
XML:
<nlog>
<targets>
<target xsi:type="File" name="file" fileName="log-file.log"
layout="${longdate} ${level} ${message}" />
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="file" />
</rules>
</nlog>
C#例子:
//NLog的 API比Serilog的流式(fluent)API更难使用
var config = new NLog.Config.LoggingConfiguration();
var logfile = new NLog.Targets.FileTarget("logfile") { FileName = "log-file.log" };
config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile);
NLog.LogManager.Configuration = config;
由于Serilog和NLog都具有很多功能,例如结构化日志记录和基于C#的配置,因此很难在Serilog和NLog之间进行选择。尽管NLog还提供结构化日志记录,但很容易看出Serilog拥有此功能的时间更长。如果今天要选择一个日志记录框架,我会选择Serilog。
话虽如此,如果您已经在使用NLog,我不确定切换到Serilog是否会有足够的好处。Serilog中的大多数功能都可以以某种形式或在NLog中使用。与往常一样,我建议您同时尝试两种方法,然后选择最喜欢的一种。
使用静态Log
类,将始终可以轻松访问日志记录。如果不喜欢静态调用,则也可以使用依赖项注入来注入Serilog ILogger
。使用静态类的好处在于,它永远不会因内部null引用异常而失败。
接收器
在https://github.com/serilog/serilog/wiki/Provided-Sinks中查看非常全面的“接收器”列表。
LoggerConfiguration对象
它主要用于创建和设置Log对象,类似于Nlog里面的LogManager类。这里主要用它两个方法:
WriteTo
:WriteTo属性用来设置日志的输出,Serilog将其称为Sink(水槽)也可以叫做接收器,通过Sink可以将日志发送到很多目的地
CreateLogger
:用于创建一个ILogger类型的Logger对象.
ILogger对象
ILogger对象用于记录日志,和其他日志框架差不多。Serilog日志级别分为如下5级
- Verbose // 详细(NLog第1级叫Trace跟踪,其它的一致)
- Debug
- Information
- Warning
- Error
- Fatal // 致命
大多数的日志也是这样5级,只是有的名称叫的不同,每一级别对应一个写Log的函数:
log.Verbose("verbose");
log.Information("info");
log.Debug("debug");
log.Warning("warning");
log.Error("err");
log.Fatal("fatal");
ILogger对象还有一个Dispose方法,用于关闭日志对象。在实际的使用过程中,往往并不是每次使用都去创建一个ILogger,一种方式是通过依赖注入的方式创建一个全局的Logger。不过这种方式需要引入DI框架。在小程序中使用不算方便。
另一种方式是直接使用静态的Log类,它也携带了写入日志的方法,用起来非常方便。
入门
https://github.com/serilog/serilog/wiki
框架
查看日志的传统方法是:登录操作系统,使用命令工具如cat、tail、sed、awk、grep等等进行过滤输出后分析,处理的日志少量还行,大量日志效率就不高了。很多情况下开发人员需要查看并分析日志,如果对Linux命令不熟悉,有时候又不能赋予他们服务器权限,更多时候是运维把日志文件导出来发给开发人员,无疑会增加工作量。
将日志消息写入控制台或文件的日子已经过去了。为了存储,查询和创建关于日志消息的通知,需要某种存储。这可以是基于云的存储:例如sentry.io 、elmah.io。本地存储:例如SQL Server或Elasticsearch。
ELK架构就是专门为采集、分析、存储日志所设计的。
ELK Stack

来自知乎: ELK是用来做日志采集、处理、存储、搜索、统计的。在这里面有一部分统计的内容是从日志的部分维度总结出指标值,可以用来做告警。zabbix是专门做指标值采集、存储、告警的。
也就是说:如果你就是几个阈值告警的需求,直接zabbix好了。用不着elk。眼红可视化效果的,可以用grafana,有zabbix source支持。
如果你确实有日志搜索查询需求,比如应用错误定位啊等等,那elk就有用了
ELK 并不是一款软件,而是一整套解决方案,是三个软件产品的首字母缩写
Elasticsearch:负责日志检索和储存
Logstash:负责日志的收集和分析、处理
Kibana:负责日志的可视化
机器上面要有收集日志的 Agent,这些 Agent 被生动的叫做:Shippers(直译:发货商),将日志像货物一样发送出去。Logstash 主要用来收集各个 Shipper 发过来的日志,并做一个过滤,然后发给 Elasticsearch 保存,Elasticsearch 保存日志,建索引,然后提供接口,Kibana 作为前端调接口提供可配置的可视化。
分场景收集日志
这里为了方便我们查看日志,提出一个分场景收集日志的概念,把日志分为以下四种。
- 调试日志:最全日志,包含了应用中所有DEBUG级别以上的日志,仅在开发、测试环境中开启收集;
- 错误日志:只包含应用中所有ERROR级别的日志,所有环境只都开启收集;
- 业务日志:在我们应用对应包下打印的日志,可用于查看我们自己在应用中打印的业务日志;
- 记录日志:每个接口的访问记录,可以用来查看接口执行效率,获取接口访问参数。
参考文章
https://github.com/serilog/serilog/wiki
https://www.cnblogs.com/TianFang/p/9416238.html
https://www.cnblogs.com/mq0036/p/8479956.html
https://blog.elmah.io/serilog-vs-nlog/
https://www.darylcumbo.net/serilog-vs-nlog-benchmarks/