日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/4 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动手造轮子:实现一个简单的基于 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

總結

以上是生活随笔為你收集整理的动手造轮子:实现一个简单的基于 Console 的日志输出的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。