[WPF] 在RichTextBox中输出Microsoft.Extension.Logging.Logger库的日志消息

news/2025/3/21 23:51:27/文章来源:https://www.cnblogs.com/echo-sama/p/18786094

背景

微软的日志库一般是输出到控制台的,但是在WPF中并不能直接使用控制台,需要AllocConsole。
但是这种做法个人觉得不太安全(一关闭控制台整个程序就退出了?)。这时候就需要一个更加友好的方式输出日志。

问题

那如何将日志的内容显示到RichTextBox中?

实现LoggerProcessor

  • 这里参照官方的ConsoleLoggerProcessor,但是需要有点区别。
public class RichTextBoxLoggerProcessor:IDisposable
{///...其他实现请参照Microsoft.Extension.Logging的源码private readonly RichTextBoxDocumentStorage _storage;private readonly Thread _outputThread;/// 这个构造函数传入RichTextBoxDocumentStorage,用于显示单条日志记录public RichTextBoxLoggerProcessor(RichTextBoxDocumentStorage storage,       LoggerQueueFullMode fullMode, int maxQueueLength){_storage = storage;_messageQueue = new();FullMode = fullMode;MaxQueueLength = maxQueueLength;_outputThread = new Thread(ProcessMessageQueue){IsBackground = true,Name = "RichTextBox logger queue processing thread"};_outputThread.Start();}///改写WriteMessage方法,熟悉FlowDocument的兄弟应该都知道Paragraph是什么吧public void WriteMessage(Paragraph message){try{//发送回FlowDocument所在的线程后添加Paragraph_storage.Document?.Dispatcher.BeginInvoke(() =>{_storage.Document.Blocks.Add(message);});}catch{CompleteAdding();}}//同理改写EnqueMessage方法和Enqueue等方法public void EnqueMessage(Paragraph message){//...具体逻辑请参阅github源码}public bool Enqueue(Paragraph message){//...}public bool TryDequeue(out Paragraph entry){//...}
}public class RichTextBoxDocumentStorage
{///因为要使用到DI,所以创建一个类来存放FlowDocument;public FlowDocument? Document{ get; set; }
}

实现RichTextBoxLogger

  • 这里继承ILogger接口
public class RichTextBoxLogger:ILogger
{private string _category;private RichTextBoxLoggerProcessor _processor;public RichTextBoxLogger(string category, RichTextBoxLoggerProcessor processor, RichTextBoxFormatter formatter){_category = category;_processor = processor;Formatter = formatter;}//LogEntry格式化器public RichTextBoxFormatter Formatter { get; set; }public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter){var logEntry = new LogEntry<TState>(logLevel, _category, eventId, state, exception, formatter);//paragraph 需要在主线程创建App.Current.Dispatcher.BeginInvoke(() =>{var message = Formatter.Write(in logEntry);if (message is null){return;}_processor.EnqueMessage(message);});}
}public abstract class RichTextBoxFormatter
{protected RichTextBoxFormatter(string name){Name = name;}public string Name { get; }public abstract Paragraph? Write<TState>(in LogEntry<TState> logEntry);
}

创建LoggerProvider

public class RichTextBoxLoggerProvider: ILoggerProvider
{private readonly RichTextBoxFormatter _formatter;private readonly ConcurrentDictionary<string,RichTextBoxLogger> _loggers = [];private readonly RichTextBoxLoggerProcessor _processor;public RichTextBoxLoggerProvider(RichTextBoxDocumentStorage storage, RichTextBoxFormatter formatter){_formatter = formatter;_processor = new RichTextBoxLoggerProcessor(storage, LoggerQueueFullMode.Wait, 2500);_formatter = formatter;}public ILogger CreateLogger(string categoryName){return _loggers.GetOrAdd(categoryName, new RichTextBoxLogger(categoryName, _processor, _formatter));}
}

创建真正的LogViewer

  • 这里使用的是Window来展现日志
public class LogViewer : Window
{public LogViewer(RichTextBoxDocumentStorage storage){InitializeComponent();if(storage.Document is null){//确保FlowDocument是在主线程上创建的App.Current.Dispatcher.Invoke(()=>{_storage.Document =  new FlowDocument() { TextAlignment = System.Windows.TextAlignment.Left }; });}logPresenter.Document = storage.Document;}
}

注册服务

public static class RichTextBoxLoggingExtension 
{public static ILoggingBuilder AddRichTextBoxLogger(this ILoggingBuilder builder){builder.Services.AddSingleton<RichTextBoxDocumentStorage>();//格式化的实现就不写了,按自己的喜好来写写格式化器;这里是参照的SimpleConsoleFormatter实现的builder.Services.AddSingleton<RichTextBoxFormatter, SimpleRichTextBoxFormatter>();builder.Services.AddSingleton<ILoggerProvider,RichTextBoxLoggerProvider>();return builder;}
}

具体使用

  • 任意位置使用ServiceProvider唤起LogViewer即可
public class SomeClass
{public void OpenLogViewer(){App.Current.Services.GetRequiredService<LogViewer>().Show();}
}

结尾

这里只是实现了个简单的输出,还有好多好多功能没有实现。
不喜欢写太长的解释说明,感觉好麻烦。代码就是最好的说明(
看哪天心血来潮了,做个nuget包吧。

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

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

相关文章

小白与Android Studio的初遇

新手小白在Android Studio上运行程序的时候遇到俩问题,通过在网上各种找解决方法,最终解决了(然后就想起来继续写博客这件事儿了)。 【第一个问题】关于“Error runningapp:No target device found”。就是它找不到设备(虚拟机或者真机),所以配置虚拟机就行了。 (当我以…

20234220 实验一《Python程序设计》实验报告

20234220 2024-2025-2 《Python程序设计》实验一报告 课程:《Python程序设计》 班级: 2342 姓名: 马燕秋 学号:20234220 实验教师:王志强 实验日期:2025年3月18日 必修/选修: 公选课 1.实验内容 1.熟悉Python开发环境; 2.练习Python运行、调试技能; 3.编写程序,练…

Echarts-普通地图和3D地图实现

效果图实现代码 <template><div class="app"><h1>普通地图----------------</h1><div class="map-container" ref="map_ref"></div><h1>3D地图----------------</h1><div class="map-c…

Navicat Premium 16 For Mac 激活,无限试用,非破解,官网安装程序,Mac版Navicat无限试用

Navicat Premium 16 For Mac 激活,无限试用,非破解,官网安装程序,Mac版Navicat无限试用Navicat Premium是一个可多重连线资料库的管理工具,它可以让你以单一程式同时连线到 MySQL、SQLite、Oracle、MariaDB、Mssql、及 PostgreSQL 资料库,让管理不同类型的资料库更加的方…

Vue3 slot

6.9. 【slot】 1. 默认插槽父组件中:<Category title="今日热门游戏"><ul><li v-for="g in games" :key="g.id">{{ g.name }}</li></ul></Category> 子组件中:<template><div class="item&…

英语四级计划第三天

第三天 单词阅读 Smaller Museums From Niche to Mainstream “小而精”的小众博物馆,正在出圈出彩 Chinas museum boom has continued to rise, leading to increased attention and visibility for smaller but more specialized museums. 中国的博物馆热潮持续升温,导致规…

今日总结(app链接数据库的简单实现以及AI训练学习)

所花时间:145min 代码量(行):120 博客量:14 了解到的知识点: 今天又到了周五了,如同往常一样下午进行自学测试,今天是实现一个app数据库连接 在手机上进行查询显示的任务。 因为对于AS开发我并没有进行系统的学习,只是知道大体的框架和对AI进行训练,但是 我发现了一个…

学嵌入式C语言,看这一篇就够了(6)

C语言的语句和块 C语言标准中一共提供6种语句 注意:C语言中的语句要指明执行的操作,并且没有特殊情况,语句是按照顺序执行的一般把实现某些功能的语句整合在一起,构成一个语法单元,C语言标准的语法单元也被称为块,也被称为块语句 复合语句 复合语句可以限制语句的作用范围…

P3375 【模板】KMP

P3375 【模板】KMP 题目描述 给出两个字符串 \(s_1\) 和 \(s_2\),若 \(s_1\) 的区间 \([l, r]\) 子串与 \(s_2\) 完全相同,则称 \(s_2\) 在 \(s_1\) 中出现了,其出现位置为 \(l\)。 现在请你求出 \(s_2\) 在 \(s_1\) 中所有出现的位置。 定义一个字符串 \(s\) 的 border 为 …

Cursor 使用教程

目录核心功能TabAICHATCOMPOSER@DocsWebGitNotepadCodebase技巧和思路如果Cursor乱改代码怎么办? 核心功能 Tab 这个是Cursor最为强大的功能,使用也很简单,在编写代码的时候,如果出现了灰色的提示词,直接按Tab就可以自动补全了最后的灰色的就是提示词 Cursor的光标预测也很…

C语言学习打卡第二天(2025.3.21)

时间有点少,今天也没学多少,只把指针基础概念学了一下(例如指针变量,下标法等),题也只做了三道。