动手造轮子:实现一个简单的基于 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…

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;我所…

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

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

阿里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;各行各业都在大力开发或者引进人工…

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

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

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

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

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;但是由于网络带宽的限…

linux ns3配置环境,Ubuntu下的NS3安装与入门

NS3的开发旨在为网络研究和教育提供开放&#xff0c;可扩展的网络仿真平台。简而言之&#xff0c;NS3提供了分组数据网络如何工作和执行的模型&#xff0c;并为用户提供了进行模拟实验的模拟引擎。使用ns-3的一些原因包括进行更难或不可能用真实系统执行的研究&#xff0c;在高…

龙芯3A5000完成流片 同主频性能追平AMD Zen1

前不久&#xff0c;龙芯3A5000完成流片&#xff0c;该CPU基于龙芯自主定义指令集LoongArch。据小道消息&#xff0c;SPEC06测试定点26分&#xff08;base 2.5Ghz&#xff09;。这个成绩已经达到预期指标&#xff0c;而且经过进一步优化&#xff0c;SPEC06成绩还有进一步优化的…

熬夜族又一噩耗:“早死”风险更高!

根据美国西北大学范伯格医学院和英国萨里大学最近的一项研究&#xff0c;相比于早睡早起的人&#xff08;百灵鸟一般&#xff09;&#xff0c;晚上不睡、早晨不起&#xff0c;或起床困难的“夜猫子”们&#xff0c;“早死”的风险更高。这项基于英国生物库中将近50万人样本的研…

iNeuOS工业互联平台,生产过程业务联动控制

1. 概述工业物联网也好、工业互联网也好或是其他生产系统&#xff0c;反向控制始终无法回避。搞工业最直接、最体现效果的两个方面是采集各种数据和生产过程业务控制&#xff0c;所谓大数据预测和分析&#xff0c;那是仁者见仁、智者见智&#xff0c;下一篇文章我们会专业来讨…

如何使用 BenchmarkDotNet 对 C# 代码进行基准测试

BenchmarkDotNet 是一个轻量级&#xff0c;开源的&#xff0c;强大的 .NET 工具包&#xff0c;它可以将你的方法转化为基准并跟踪这些方法&#xff0c;最后对这些方法的性能提供一些测试报告&#xff0c;使用 BenchmarkDotNet 玩 基准测试 是非常容易的。你可以利用 BenchmarkD…

原来论文排版还有这样的学问

论文的重要加分点除了内容&#xff0c;还有它小天最近经常遇到小伙伴的诉苦&#xff1a;“我的毕业论文提交一次就被导师批评一次&#xff0c;内容不行就算了&#xff0c;格式也有问题&#xff01;改论文改到绝望”“期刊的版式要求不是统一的&#xff0c;为了多投几家&#xf…

红帽linux5安装Oracle 9i enterprise

红帽linux5安装Oracle 9i enterprise 本文转自&#xff1a;http://database.51cto.com/art/201004/194082.htm摘要&#xff1a;如果你在红帽Linux5上安装Oracle 9i enterprise遇到了问题&#xff0c;不防看一看下面的文章&#xff0c;希望能帮你解决有关Oracle9i实际应用的问题…

神经网络告诉我,谁是世界上最「美」的人?

「魔镜魔镜告诉我&#xff0c;谁是世界上最美的女人&#xff1f;」这句伴随童年的话也有现实版哦&#xff5e;神经网络可以预测人脸颜值&#xff0c;这方面也出现了不少研究。今年年初华南理工大学的研究者发布论文&#xff0c;并公开了数据集 SCUT-FBP5500。本文作者 Dima Shu…