动手造轮子:实现一个简单的基于 Console 的日志输出

动手造轮子:实现一个简单的基于 Console 的日志输出

Intro

之前结合了微软的 Logging 框架和 Serilog 写了一个简单的日志框架,但是之前的用法都是基于 log4net、serilog 的,没有真正自己实现一个日志输出,比如 Console、文件、数据库、ES等,关于日志框架的设计可以参考之前的文章 动手造轮子:写一个日志框架

实现思路

把日志放在一个队列中,通过队列方式慢慢的写,避免并发问题,同时异步写到 Console 避免因为写日志阻塞主线程的执行

输出的格式如何定义呢,像 log4net/nlog/serilog 这些都会支持自定义日志输出格式,所以我们可以设计一个接口,实现一个默认日志格式,当用户自定义日志格式的时候就使用用户自定义的日志格式

针对不同的日志级别的日志应该使用不同的颜色来输出以方便寻找不同级别的日志

使用示例

来看一个使用的示例:

LogHelper.ConfigureLogging(builder =>
{builder.AddConsole()//.AddLog4Net()//.AddSerilog(loggerConfig => loggerConfig.WriteTo.Console())//.WithMinimumLevel(LogHelperLogLevel.Info)//.WithFilter((category, level) => level > LogHelperLogLevel.Error && category.StartsWith("System"))//.EnrichWithProperty("Entry0", ApplicationHelper.ApplicationName)//.EnrichWithProperty("Entry1", ApplicationHelper.ApplicationName, e => e.LogLevel >= LogHelperLogLevel.Error);
});var abc = "1233";
var logger = LogHelper.GetLogger<LoggerTest>();
logger.Debug("12333 {abc}", abc);
logger.Trace("122334334");
logger.Info($"122334334 {abc}");logger.Warn("12333, err:{err}", "hahaha");
logger.Error("122334334");
logger.Fatal("12333");

日志输出如下:

log output

默认的日志格式是 JSON 字符串,因为我觉得 JSON 更加结构化,也会比较方便的去 PATCH 和日志分析,微软的 Logging 框架也是在 .NET 5.0 中加入了 JsonConsoleFormatter,可以直接输出 JSON 到控制台,如果需要也可以自定义一个 Formatter 来实现自定义的格式化

实现源码

使用 IConsoleLogFormatter 接口来自定义日志格式化

public interface IConsoleLogFormatter
{string FormatAsString(LogHelperLoggingEvent loggingEvent);
}internal sealed class DefaultConsoleLogFormatter : IConsoleLogFormatter
{private static readonly JsonSerializerSettings _serializerSettings = new(){Converters ={new StringEnumConverter()},ReferenceLoopHandling = ReferenceLoopHandling.Ignore,};public string FormatAsString(LogHelperLoggingEvent loggingEvent){return loggingEvent.ToJson(_serializerSettings);}
}

实现的代码比较简单,队列的话使用了 BlockingCollection 来实现了一个内存中的队列

ConsoleLoggingProvider实现如下:

internal sealed class ConsoleLoggingProvider : ILogHelperProvider
{private readonly IConsoleLogFormatter _formatter;private readonly BlockingCollection<LogHelperLoggingEvent> _messageQueue = new();private readonly Thread _outputThread;public ConsoleLoggingProvider(IConsoleLogFormatter formatter){_formatter = formatter;// Start Console message queue processor_outputThread = new Thread(ProcessLogQueue){IsBackground = true,Name = "Console logger queue processing thread"};_outputThread.Start();}public void EnqueueMessage(LogHelperLoggingEvent message){if (!_messageQueue.IsAddingCompleted){try{_messageQueue.Add(message);return;}catch (InvalidOperationException) { }}// Adding is completed so just log the messagetry{WriteLoggingEvent(message);}catch (Exception){// ignored}}public void Log(LogHelperLoggingEvent loggingEvent){EnqueueMessage(loggingEvent);}private void ProcessLogQueue(){try{foreach (LogHelperLoggingEvent message in _messageQueue.GetConsumingEnumerable()){WriteLoggingEvent(message);}}catch{try{_messageQueue.CompleteAdding();}catch{// ignored}}}private void WriteLoggingEvent(LogHelperLoggingEvent loggingEvent){try{var originalColor = Console.ForegroundColor;try{var log = _formatter.FormatAsString(loggingEvent);var logLevelColor = GetLogLevelConsoleColor(loggingEvent.LogLevel);Console.ForegroundColor = logLevelColor.GetValueOrDefault(originalColor);if (loggingEvent.LogLevel == LogHelperLogLevel.Error|| loggingEvent.LogLevel == LogHelperLogLevel.Fatal){Console.Error.WriteLine(log);}else{Console.WriteLine(log);}}catch (Exception ex){Console.WriteLine(ex);}finally{Console.ForegroundColor = originalColor;}}catch{Console.WriteLine(loggingEvent.ToJson());}}private static ConsoleColor? GetLogLevelConsoleColor(LogHelperLogLevel logLevel){return logLevel switch{LogHelperLogLevel.Trace => ConsoleColor.Gray,LogHelperLogLevel.Debug => ConsoleColor.Gray,LogHelperLogLevel.Info => ConsoleColor.DarkGreen,LogHelperLogLevel.Warn => ConsoleColor.Yellow,LogHelperLogLevel.Error => ConsoleColor.Red,LogHelperLogLevel.Fatal => ConsoleColor.DarkRed,_ => null};}
}

为了方便使用和更好的访问控制,上面的 ConsoleLoggingProvider 声明成了 internal 并不直接对外开放,并且定义了下面的扩展方法来使用:

public static ILogHelperLoggingBuilder AddConsole(this ILogHelperLoggingBuilder loggingBuilder, IConsoleLogFormatter? consoleLogFormatter = null)
{loggingBuilder.AddProvider(new ConsoleLoggingProvider(consoleLogFormatter ?? new DefaultConsoleLogFormatter()));return loggingBuilder;
}

DelegateFormatter

需要自定义的 Console 日志的格式的时候就实现一个 IConsoleLogFormatter 来实现自己的格式化逻辑就可以了,不想手写一个类?也可以实现一个 Func<LogHelperLoggingEvent, string> 委托,内部会把委托转成一个 IConsoleLogFormatter,实现如下:

internal sealed class DelegateConsoleLogFormatter : IConsoleLogFormatter
{private readonly Func<LogHelperLoggingEvent, string> _formatter;public DelegateConsoleLogFormatter(Func<LogHelperLoggingEvent, string> formatter){_formatter = formatter ?? throw new ArgumentNullException(nameof(formatter));}public string FormatAsString(LogHelperLoggingEvent loggingEvent) => _formatter(loggingEvent);
}

扩展方法:

public static ILogHelperLoggingBuilder AddConsole(this ILogHelperLoggingBuilder loggingBuilder, Func<LogHelperLoggingEvent, string> formatter)
{loggingBuilder.AddProvider(new ConsoleLoggingProvider(new DelegateConsoleLogFormatter(formatter)));return loggingBuilder;
}

More

在写一些小应用的时候,经常会遇到这样的场景,就是执行一个方法的时候包一层 try...catch,在发生异常时输出异常信息,稍微包装了一个

public static Action<Exception>? OnInvokeException { get; set; }public static void TryInvoke(Action action)
{Guard.NotNull(action, nameof(action));try{action();}catch (Exception ex){OnInvokeException?.Invoke(ex);}
}

原来想突出显示错误信息的时候,我会特别设置一个 Console 的颜色以便方便的查看,原来会这样设置,之前的 gRPC 示例项目原来就是这样做的:

InvokeHelper.OnInvokeException = ex =>
{var originalColor = ForegroundColor;ForegroundColor = ConsoleColor.Red;WriteLine(ex);ForegroundColor = originalColor;
};

有了 Console logging 之后,我就可以把上面的委托默认设置为 Log 一个 Error(OnInvokeException = ex => LogHelper.GetLogger(typeof(InvokeHelper)).Error(ex);),只需要配置 Logging 使用 Console 输出就可以了,也可以设置日志级别忽略一些不太需要的日志

LogHelper.ConfigureLogging(x=>x.AddConsole().WithMinimumLevel(LogHelperLogLevel.Info));

diff

References

  • https://github.com/WeihanLi/WeihanLi.Common

  • https://github.com/WeihanLi/WeihanLi.Common/blob/dev/src/WeihanLi.Common/Logging/ConsoleLoggingProvider.cs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303847.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows Phone 7Silverlight控件之--Panorama

Silverlight for Windows Phone 7的Pivot、Panorama控件是一个类似于Android应用程序列表中可以翻页控件&#xff0c;具有如下特点 1.简单的XAML和编程接口 2.完全支持数据绑定、内容模板属性和项目容器样式 3.内置黑、白两种皮肤样式 4.内置触控导航&#xff0c;可以让用户快速…

linux 往文件写4k大小,[svc]为何linux ext4文件系统目录默认大小是4k?

linux ext4普通盘为什么目录大小是4k?Why does every directory have a size 4096 bytes (4 K)?To understand this, youd better have some basic knowledge of the following (file system):inode (contains file attributes, metadata of file, pointer structure)file (c…

3月份GitHub上最热门的开源项目

本篇文章为大家盘点了3月份GitHub最热门的开源项目&#xff0c;一起来看看你都知道哪些&#xff0c;或者有哪些你已经在使用的了。1Interview-Notebookhttps://github.com/CyC2018/Interview-Notebook Star 11975此项目是作者在准备 2018 年春招实习过程中的学习总结&#xff0…

自定义验证规则ValidationAttribute的使用

概述大家在做项目的时候&#xff0c;在实体类上添加一些特性,可以实现后端实体的数据校验。有时候&#xff0c;可能需要自定义验证属性。实现原理&#xff1a;利用反射获取实体的每一个属性&#xff0c;并通过属性获取属性上标注的特性&#xff0c;调用特性的Validate方法&…

【百度分享】基于内核模块的测试代码编写(二)

4. 用户空间和内核空间的交互 在解决了在内核空间置入可运行代码后&#xff0c;需要解决的是用户空间和内核空间的交互。具体来说&#xff0c;需要达到以下三个功能&#xff1a;用户空间的程序向内核空间下的程序控制&#xff0c;用户空间到内核空间的数据传递&#xff0c;内…

linux sed 空间模式,整理:SED的模式空间与缓冲区及n,N,d,D,p,P,h,H,g,G,x解析...

sed命令n&#xff0c;N&#xff0c;d&#xff0c;D&#xff0c;p&#xff0c;P&#xff0c;h&#xff0c;H&#xff0c;g&#xff0c;G&#xff0c;x解析1、sed执行模板sed ‘模式{命令1;命令2}’ 即逐行读入模式空间&#xff0c;执行命令&#xff0c;最后输出打印出来2、p打印…

你碰到过的最难调试的 Bug 是什么样的?

我们做开发的应该都会有深刻的体会&#xff0c;有时候会遇到一些莫名奇妙的BUG不知所措&#xff0c;解决BUG到近乎崩溃&#xff0c;更有甚者有人居然会在梦中解决掉BUG。下面我们看几个有意思的解决Bug的故事&#xff1a;知乎网友李幼萌&#xff1a;08年的时候&#xff0c;我所…

java面试题1

一、基础问答 1.下面哪些类可以被继承? java.lang.Thread (T)    java.lang.Number (T)    java.lang.Double (F)    java.lang.Math (F)    java.lang.Void (F)    java.lang.Class (F)    java.lang.ClassLoader (T) 2.抽象类和接口的区别 (1)接口可以被多…

云原生 | 阿里巴巴的Dapr实践与探索

【Dapr】| 原文/敖小剑&#xff0c;翻译/Edison本文原文来自Dapr Blog&#xff0c;作者阿里云高级技术专家敖小剑。本文是我根据自己的理解翻译了其中的一部分并加了一些参考文献中的内容&#xff0c;所以并非完整翻译&#xff0c;点击本文底部“阅读原文”即可阅读原版英文全文…

linux安装grpc占用空间大,grpc linux安装-Go语言中文社区

1. go protobuf 安装Protoc Buffer 安装包下载地址&#xff1a;https://github.com/protocolbuffers/protobuf/releases/download/v3.9.0/protoc-3.9.0-linux-x86_64.zip(使用wget命令下载)&#xff0c;最新版本可在此页面查看。下载完成后&#xff0c;解压。将解压出来的inclu…

《Objective-C基础教程》第二章 对C的扩展

.m代表message。编译工作由GCC(GNU Compiler Collection&#xff0c;GNU编译器集合)处理&#xff0c;可以理解C语言的全部3个变体。NS代码前缀表示来自Cocoa&#xff0c;苹果收购的NeXT Software公司开发的NextSTEP工具包。布尔类型BOOL具有YES和NO值&#xff0c;但使用8位存储…

阿里25k招.NET,要求WPF!

说到WPF、上位机&#xff0c;很多.NET开发者可能只是听过并不了解&#xff0c;然而随着工业4.0的火爆大势&#xff0c;当下.NET开发者又多了一个高薪选型&#xff0c;在各大招聘网站上都能找到5年经验25k左右的岗位&#xff0c;近日阿里对.NET的招聘&#xff0c;明确就要求的WP…

我们计划招收300名学员,免费攻读人工智能专业

给大家看一份最新的数据&#xff1a;&#xff08;薪资表&#xff09;2018年最新数据&#xff1a;python、大数据、人工智能从业者工资为什么人工智能行业的工资那么高&#xff1f;无论是科研院所&#xff0c;商业巨头还是初创企业&#xff0c;各行各业都在大力开发或者引进人工…

Linux查看设备 eth,lspci grep Eth,查看Linux下的各种硬件设备是否识别或存在之用

背景&#xff1a;Linux下的硬件有很多&#xff0c;如何看网卡是啥样的&#xff0c;声卡是啥样的有几个USB口及品牌。[rootemulMachine htdocs]# lspci |grep USB00:03.0 USB controller: Broadcom BCM5785 [HT1000] USB (rev 01)00:03.1 USB controller: Broadcom BCM5785 [HT1…

一日一技:ASP.NET Core 判断请求是否为Ajax请求

概述在写后台程序时&#xff0c;有时候需要知道客户端发送的是普通的请求&#xff0c;还是ajax 请求&#xff0c;最近在做项目的时候&#xff0c;有些地方需要判断当前的请求是不是ajax。特地找了下发现&#xff0c;jQuery 发出 ajax 请求时&#xff0c;会在请求头部添加一个名…

HttpHandler浅析

声明:本文为翻译文章,如有侵权,请告知,我将会在1个工作日内删除,谢谢. ★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆ 总是想着展翅飞翔,但是却不料一直受伤,但是那寻梦的心啊,永远向往着远方. ★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★…

神经网络简史:BP算法后的又一突破—信念网络

随着训练多层神经网络的谜题被揭开&#xff0c;这个话题再一次变得空前热门&#xff0c;罗森布拉特的崇高雄心似乎也将得以实现。直到1989年另一个关键发现被公布&#xff0c;现在仍广为教科书及各大讲座引用。多层前馈神经网络是普适模拟器&#xff08; universal approximato…

Linux网络模块全局变量,()不是Linux系统的特色.

相关题目与解析UNIX是一个交互式的______操作系统&#xff0c;采用以全局变量为中心的模块结构。下列关于UNIX系统结构的描述中&#xff0c;正确的是______。A&#xff0e;UNIX系统是一个交互式的多用户、多任务分时操作按照变量的作用域可将变量划分为()A.公有、私有、系统B.全…

NET问答:Select 和 SelectMany 的区别

咨询区 Tarik&#xff1a;我已经 google 搜索了 Select 和 SelectMany 之间的区别&#xff0c;但我并没有找到合适的答案&#xff0c;我现在急切的需要知道在 Linq to SQL 时两者的区别而不是给我用Array展示...能否有人帮忙提供 Linq To SQL 的例子吗&#xff1f;回答区 Mike …

最好的FLV视频下载器 维棠 (支持优酷视频下载、土豆视频下载等)

维棠FLV视频下载能帮助你轻松下载国内外大多数FLV视频分享网站的视频内容;并且维棠FLV视频下载具有断点续传功能。 现在国内外出现了很多视频分享网站&#xff0c;每个用户都可以把自己的视频节目和其他人分享。这些网站有大量的在线视频资源&#xff0c;但是由于网络带宽的限…