开源高性能结构化日志模块NanoLog

nanolog · 浏览次数 : 0

小编点评

本文介绍了一个高性能的结构化日志记录组件,命名为NanoLog。作者在数据库程序开发过程中需要一个高性能的日志记录功能,研究并比较了Microsoft.Extensions.Logging和Serilog,最终决定自行实现一个轮子。 **1. 使用方法** 首先,通过以下示例代码启动NanoLogger: ```csharp NanoLogger.Start(); DateTime? nullable = null; const bool boolValue = true; const char charValue = 'C'; const int intValue1 = 12345; const int intValue2 = 0xABCDEF; const string stringValue = "你好世界"; var point = new Point { X = 123, Y = 456 }; var person = new Person { Name = "Rick", Birthday = new DateTime(1977, 3, 1), Phone = "13861838709" }; var log = new NanoLogger(); log.Trace("Trace message"); log.Trace($"Trace {DateTime.Now}, {intValue1}, 0x{intValue2:X}"); log.Debug($"Debug {DateTime.Now:yyyy-MM-dd hh:mm:ss}, 你好世界!"); log.Info($"Info {point}, {person}, {charValue}"); log.Warn("这是警告:", boolValue); log.Error($@"发生异常: {nullable}, Msg={stringValue}\"); NanoLogger.Stop(); ``` 执行后,控制台将输出结构化的日志记录,其中的记录值会高亮显示。 **2. 性能测试** 性能测试仅使用一个日期类型参数进行,比较NanoLog与其他两种日志记录方式的性能差异: - NanoLog: 154.6 ns, 0.91 ns, 3.48 ns, 0.04 - 0.00 Ms - Log: 3,922.2 ns, 49.13 ns, 202.60 ns, 1.00 - LogCodeGen: 4,079.3 ns, 52.49 ns, 218.77 ns, 1.04 **3. 实现原理** NanoLog的实现主要包括以下几个部分: - **日志级别与启用**:判断日志级别是否启用,不启用则忽略该日志。 - **日志消息构建**:使用C# 6的InterpolatedStringHandlerAttribute自定义实现LogMessageBuilder,记录结构化信息(名称、类型、值、格式化)。 - **序列化与缓冲**:序列化后的日志信息存入内存块或ArrayPool的缓冲块,根据大小选择不同的存储方式。 - **多生产者-单消费者消息队列**:将日志消息、属性类型及属性值加入消息队列,实现异步日志记录,避免阻塞后续代码执行。 **4. 日志搜索** 结构化日志方便支持结构化搜索,可以通过Roslyn解析字符串表达式来过滤日志记录。例如,对于日志记录中的某个事件属性条件,可以使用以下表达式: ``` e.XXX e[\"xxx\"][\"yyy\"] ``` 其中,`e`为日志事件对象,`e.XXX`表示事件对象的属性值条件,`e[\"xxx\"][\"yyy\"]`表示日志消息结构化记录的值条件。 **5. 结语** 本文详细介绍了NanoLog的实现原理和使用方法,并进行了性能测试。NanoLog具有高性能、可扩展性好的特点,适用于数据库程序等需要高性能日志记录的场景。作者 GitHub 地址为: https://github.com/enjoycode/NanoLog.git,如有问题请邮件联系或通过 Github Issue 反馈。

正文

  最近在写数据库程序,需要一个高性能的结构化日志记录组件,简单研究了一下Microsoft.Extensions.Logging和Serilog,还是决定重造一个轮子。

一、使用方法

  直接参考以下示例代码:

NanoLogger.Start();

DateTime? nullable = null;
const bool boolValue = true;
const char charValue = 'C';
const int intValue1 = 12345;
const int intValue2 = 0xABCDEF;
const string stringValue = "你好世界";
var point = new Point { X = 123, Y = 456 };
var person = new Person { Name = "Rick", Birthday = new DateTime(1977, 3, 1), Phone = "13861838709" };

var log = new NanoLogger();

log.Trace("Trace message");
log.Trace($"Trace {DateTime.Now}, {intValue1}, 0x{intValue2:X}");
log.Debug($"Debug {DateTime.Now:yyyy-MM-dd hh:mm:ss}, 你好世界!");
log.Info($"Info {point}, {person}, {charValue}");
log.Warn($"这是警告: {boolValue}");
log.Error($"发生异常: {nullable}, Msg={stringValue}");

NanoLogger.Stop();

执行后控制台输出如下图(记录的结构化值会高亮显示):

二、性能测试

  以下测试仅用一个日期类型参数: nanoLogger.Info($"Hello World {now}");

Method Mean Error StdDev Ratio Lock Contentions Allocated Alloc Ratio
NanoLog 154.6 ns 0.91 ns 3.48 ns 0.04 - - 0.00
MsLog 3,922.2 ns 49.13 ns 202.60 ns 1.00 0.0004 264 B 1.00
MsLogCodeGen 4,079.3 ns 52.49 ns 218.77 ns 1.04 0.0010 208 B 0.79
  • NanoLog 本组件控制台输出
  • MsLog Microsoft.Extensions.Logging控制台输出
  • MsLogCodeGen 使用[LoggerMessageAttribute]代码生成方式

三、实现原理


+-----Logger Threads-----+                               +-----Background Thread----+
|                        |                               |                          |
|   logger.Info(xxx)     |                               |    ConsoleLogger.Log()   |
|                        |      +-----Log Queue---+      |                          |
|   logger.Debug(xxx)    |  ==> |-Log-|-Log-|-...-| ==>  |    FileLogger.Log()      |
|                        |      +-----------------+      |                          |
|   logger.Warn(xxx)     |                               |    OtherLogger.Log()     |
|                        |                               |                          |
+------------------------+                               +--------------------------+
  • 日志记录时先判断对应的日志级别是否启用,不启用直接忽略。这里使用C# 6的InterpolatedStringHandlerAttribute自定义实现LogMessageBuilder,一方面避免值类型的装箱,另一方面可以记录结构化信息(名称、类型、值、格式化);

  • 启用则将日志消息、对应的属性类型及属性值序列化后写入LogMessage内。这里的序列化非常简单,仅相当于一个内存复制(参考下图)。LogMessage是一个结构体,如果序列化后的数据小于阀值则直接存储在内置的缓冲块内(没有Heap内存分配的问题),否则从ArrayPool内租用一个缓冲块存储超出部分;

+--------------------LogMessage 缓冲块-----------------------+
|-TokenType-|---Value---|-TokenType-|--------Value------|---|
|  Literal  | 5,"Hello" |   Int     | "name",12345,"X2" |   |
+-----------------------------------------------------------+
  • 序列化后的事件信息(LogEvent)及消息数据(LogMessage)直接加入一个多生产者-单消费者的消息队列,至此前端日志记录过程结束,不阻塞后续代码执行;

  • 后台线程循环从队列取出待处理日志,由配置的ILogger实现处理。例如ConsoleLogger格式化后输出至控制台;FileLogger将数据写入文件存储。

四、日志搜索

  结构化日志当然得支持结构化搜索,参考控制台工程NanoLog.File.Viewer使用Roslyn解析字符串表达式编译后过滤日志记录(参考下图):

  • 表达式中e.XXX对应LogEvent的相关属性条件;
  • 表达式中e["xxx"]["yyy"]对应LogMessage结构化记录的值条件。

五、本文小结

  最后GitHub地址:https://github.com/enjoycode/NanoLog.git, 作者个人能力实在有限Bug在所难免,如有问题请邮件联系或Github Issue,欢迎感兴趣的小伙伴们加入共同完善,当然更欢迎赞助项目或给作者介绍工作(目前找工作中)。

与开源高性能结构化日志模块NanoLog相似的内容:

开源高性能结构化日志模块NanoLog

最近在写数据库程序,需要一个高性能的结构化日志记录组件,简单研究了一下Microsoft.Extensions.Logging和Serilog,还是决定重造一个轮子。 一、使用方法 直接参考以下示例代码: NanoLogger.Start(); DateTime? nullable = null;

初步搭建一个自己的对象存储服务---Minio

MinIO 是一个高性能的对象存储解决方案,类似于 Amazon S3,但它是开源的。MinIO 可以用于存储大规模的不结构化数据,比如照片、视频、备份和日志文件等。它设计为兼容 Amazon S3 API,因此可以很容易地与现有的使用 S3 的应用程序集成。

【主流技术】聊一聊消息队列 RocketMQ 的基本结构与概念

RocketMQ 是阿里巴巴在 2012 年开源的分布式消息中间件,目前已经捐赠给 Apache 软件基金会,并于 2017 年 9 月 25 日成为 Apache 的顶级项目。 作为经历过多次阿里巴巴双十一这种“超级工程”的洗礼并有稳定出色表现的国产中间件,以其高性能、低延时和高可靠等特性近年来...

MySQL面试题全解析:准备面试所需的关键知识点和实战经验

本次种子题目主要涵盖了MySQL的存储引擎和索引结构,如B+树索引和哈希索引,以及覆盖索引和回表的概念。此外,还包含了MySQL事务的ACID特性和隔离级别。另外,对MySQL主从集群中的binlog日志的执行顺序和作用进行了讨论。最后,还涉及了分库分表和读写分离的概念。这些内容涵盖了MySQL数据库的核心知识和重要技术,不仅在面试中起到关键作用还对于优化数据库性能和应用开发都具有重要意义。

SDK日志上传性能优化

问题描述 在SDK初始化时,会在init方法中开启一个倒计时,在5s倒计时结束后使用子线程将本地保存的历史日志信息上传到后台。 因业务需要,在日志在发送上传前,需要对日志数据做编码和特殊字符替换,而日志文件里包含的日志数据量相比于一般方法中的局部变量要大很多,所以这样集中对日志文件数据的编码和替换就

MySQL如何快速获取binlog的开始时间和结束时间

之前写过一篇文章MySQL如何获取binlog的开始时间和结束时间[1],文章里面介绍了如何获取MySQL数据库二进制日志(binlog)的开始时间与结束时间的一些方法。实际应用当中,我们可能还会遇到效率/性能方面的问题。最近对这个问题做了一些研究,这里就介绍一下如何快速获取MySQL二进制日志(b

跳跃表数据结构与算法分析

目前市面上充斥着大量关于跳跃表结构与Redis的源码解析,但是经过长期观察后发现大都只是在停留在代码的表面,而没有系统性地介绍跳跃表的由来以及各种常量的由来。作为一种概率数据结构,理解各种常量的由来可以更好地进行变化并应用到高性能功能开发中。本文没有重复地以对现有优秀实现进行代码分析,而是通过对跳跃表进行了系统性地介绍与形式化分析,并给出了在特定场景下的跳跃表扩展方式,方便读者更好地理解跳跃表数据

[翻译].NET 8 的原生AOT及高性能Web开发中的应用[附性能测试结果]

随着 .NET 8 的发布,微软迈出了重要一步,为 ASP.NET Core 引入了原生的 Ahead-of-Time (AOT) 编译。这一进步不仅提高了应用程序的性能,还简化了开发过程,标志着 .NET 生态系统进入了新的时代。

【OpenVINO™】YOLOv10在CPU上也能实现50+FPS推理—使用OpenVINO C++部署YOLOv10

英特尔发行版 OpenVINO™ 工具套件基于 oneAPI 而开发,可以加快高性能计算机视觉和深度学习视觉应用开发速度工具套件,适用于从边缘到云的各种英特尔平台上,帮助用户更快地将更准确的真实世界结果部署到生产系统中。YOLOv10是清华大学研究人员近期提出的一种实时目标检测方法,通过消除NMS、...

[转帖]七. PostgreSQL逻辑结构(1)—数据库和模式

https://www.jianshu.com/p/ee8b1bdfdb19 在PostgreSQL里,逻辑结构从高到底依次是:数据库、模式(又叫架构)、表、行。当我们打开PostgreSQL官方自带的客户端软件pgadmin的时候,点开一个实例的连接,如下所示: image.png 从图中我们可以