[WPF] 在RichTextBox中输出Microsoft.Extension.Logging库的日志消息
背景
微软的日志库一般是输出到控制台的,但是在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包吧。
[WPF] 在RichTextBox中输出Microsoft.Extension.Logging库的日志消息的更多相关文章
- Laravel 中输出 SQL 语句的到 log 日志
在 AppServiceProvider.php 中的 boot 方法中添加如下代码 即可 public function boot() { //数据库监听 DB::listen(function ( ...
- asp.net core 2.0 Microsoft.Extensions.Logging 文本文件日志扩展
asp.net core微软官方为日志提供了原生支持,有如下实现 Console Debug EventLog AzureAppServices TraceSource EventSource 并且在 ...
- python的logging库
logging库 简介 logging库提供日志打印功能. 值得一提的是,不仅能打印到日志文件,还能打印到控制台. 日志级别 logging一共分为5个级别,从低到高依次为: DEBUG<IN ...
- 微软日志工厂 Microsoft.Extensions.Logging 中增加 log4net 的日志输出
前提: 需要nuget Microsoft.Extensions.Logging.Log4Net.AspNetCore 2.2.6: 描述:解决 .net core 微软日志工厂 Micros ...
- WPF中, 启用添加到RichTextBox中的控件
原文:WPF中, 启用添加到RichTextBox中的控件 WPF中, 启用添加到RichTextBox中的控件 ...
- 年度巨献-WPF项目开发过程中WPF小知识点汇总(原创+摘抄)
WPF中Style的使用 Styel在英文中解释为”样式“,在Web开发中,css为层叠样式表,自从.net3.0推出WPF以来,WPF也有样式一说,通过设置样式,使其WPF控件外观更加美化同时减少了 ...
- WindowsXamlHost:在 WPF 中使用 UWP 控件库中的控件
在 WindowsXamlHost:在 WPF 中使用 UWP 的控件(Windows Community Toolkit) 一文中,我们说到了在 WPF 中引入简单的 UWP 控件以及相关的注意事项 ...
- How do I duplicate a resource reference in code behind in WPF?如何在WPF后台代码中中复制引用的资源?
原文 https://stackoverflow.com/questions/28240528/how-do-i-duplicate-a-resource-reference-in-code-behi ...
- microsoft.extensions.logging日志组件拓展(保存文本文件)
Microsoft.Extensions.Logging 日志组件拓展 文件文本日志 文件文本日志UI插件 自定义介质日志 Microsoft.Extensions.Logging.File文件文本日 ...
- WPF Prism8.0中注册Nlog日志服务
无论是Nlog还是Serilog, 它们都提供了如何快速在各类应用程序当中的快速使用方法. 尽管,你现在无论是在WPF或者ASP.NET Core当中, 都可以使用ServiceCollection来 ...
随机推荐
- SSM或Spring Boot开发中,mapper包中的mapper.xml没有编译到targger中的解决办法
SSM或Spring Boot开发中,mapper包中的mapper.xml没有编译到targger中的解决办法 在pom文件中加入如下配置: <resources> <!-- 编译 ...
- js操作shadow-root内的DOM元素
其实就是 documentfragment元素,就是动态生成的文档碎片元素. 1,项目中在DOM结构里遇到了shadow-root(open),用JS方法无法直接获取其内的DOM元素 2.shadow ...
- 一致性hash和普通hash和hash槽
普通hash Hash函数:一般翻译做散列.杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值.碰撞(冲突):如果两个关键字通过 ...
- 如何快速的开发一个完整的iOS直播app(点赞功能)
客户端代码 点击小红心,发送socket给服务器,并且要传递房间Key给服务器,通知给哪个主播点赞,就能传入到对应的分组socket中 怎么传递房间key,房间Key在主播界面,一般一个客户端,只会产 ...
- 第一个helloworld,有点小兴奋
@SpringBootApplication package com.ch.boot; import org.springframework.boot.SpringApplication; imp ...
- C#客户端Json转DataTable
本文转自 https://blog.csdn.net/pinebud55/article/details/52240287 感谢pinebud55分享 之前我们有讨论过c#是如何处理json的,在我的 ...
- WitAwards 2024荣耀登榜!AOne载誉而归!
近日,FCIS 2024网络安全创新大会在上海举办.本次大会以"迈向安全服务化时代"为主题,邀请来自全球的网安精英.技术专家.CISO/CSO.白帽子.创业者等展开深度对话,分享与 ...
- JAVA8——StringJoiner类
引言:在阅读项目代码是,突然看到了StringJoiner这个类的使用,感觉很有意思,对实际开发中也有用,实际上是运用了StringBuilder的一个拼接字符串的封装处 介绍 StringJoine ...
- Hive2升hive3报错
一.网上总结的报错 1.coalesce报错 FAILED: SemanticException [Error 10014]: Line 197:4 Wrong arguments ''10'': U ...
- 本地部署Grok2.0
Grok-beta2.0(通过ChatBox实现) 个人使用: 1.注册登录 官方地址https://x.ai/ 2.创建API密钥 地址https://console.x.ai/ 3.登录githu ...