背景

微软的日志库一般是输出到控制台的,但是在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库的日志消息的更多相关文章

  1. Laravel 中输出 SQL 语句的到 log 日志

    在 AppServiceProvider.php 中的 boot 方法中添加如下代码 即可 public function boot() { //数据库监听 DB::listen(function ( ...

  2. asp.net core 2.0 Microsoft.Extensions.Logging 文本文件日志扩展

    asp.net core微软官方为日志提供了原生支持,有如下实现 Console Debug EventLog AzureAppServices TraceSource EventSource 并且在 ...

  3. python的logging库

    logging库 简介 logging库提供日志打印功能. 值得一提的是,不仅能打印到日志文件,还能打印到控制台. 日志级别 logging一共分为5个级别,从低到高依次为:  DEBUG<IN ...

  4. 微软日志工厂 Microsoft.Extensions.Logging 中增加 log4net 的日志输出

    前提: 需要nuget   Microsoft.Extensions.Logging.Log4Net.AspNetCore   2.2.6: 描述:解决 .net core 微软日志工厂 Micros ...

  5. WPF中, 启用添加到RichTextBox中的控件

    原文:WPF中, 启用添加到RichTextBox中的控件   WPF中, 启用添加到RichTextBox中的控件                                           ...

  6. 年度巨献-WPF项目开发过程中WPF小知识点汇总(原创+摘抄)

    WPF中Style的使用 Styel在英文中解释为”样式“,在Web开发中,css为层叠样式表,自从.net3.0推出WPF以来,WPF也有样式一说,通过设置样式,使其WPF控件外观更加美化同时减少了 ...

  7. WindowsXamlHost:在 WPF 中使用 UWP 控件库中的控件

    在 WindowsXamlHost:在 WPF 中使用 UWP 的控件(Windows Community Toolkit) 一文中,我们说到了在 WPF 中引入简单的 UWP 控件以及相关的注意事项 ...

  8. 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 ...

  9. microsoft.extensions.logging日志组件拓展(保存文本文件)

    Microsoft.Extensions.Logging 日志组件拓展 文件文本日志 文件文本日志UI插件 自定义介质日志 Microsoft.Extensions.Logging.File文件文本日 ...

  10. WPF Prism8.0中注册Nlog日志服务

    无论是Nlog还是Serilog, 它们都提供了如何快速在各类应用程序当中的快速使用方法. 尽管,你现在无论是在WPF或者ASP.NET Core当中, 都可以使用ServiceCollection来 ...

随机推荐

  1. Javascript中不同的<script.../>元素中变量或函数的作用范围的说明

    在同一个<script.../>元素中,Javascript允许先调用函数,然后再定义该函数:但是在不同的<script.../>元素中,必须先定义函数,再调用该函数,也就是说 ...

  2. AI Agent系列-什么是AI智能体,使用Semantic Kernel开发一个AI Agent

    今年最热的技术除了LLM大语言模型外,AI Agent智能体成为下一个最热的技术发展热点.. 近期准备整理几篇AI智能体的博客,带着大家了解并学习AI 智能体的开发和应用. 一.什么是AI 智能体 A ...

  3. 即时通讯技术文集(第24期):音视频WebRTC好文合集 [共20篇]

    为了更好地分类阅读 52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第 24 期. [- 1 -] 开源实时音视频技术WebRTC的现状 [链接] http://w ...

  4. nginx升级与版本回退

    ginx官网下载安装包http://nginx.org/en/download.html 查看nginx文件或目录find / -name nginx 2>/dev/null 查看已安装的 Ng ...

  5. 在 .NET 9 中使用 Scalar 替代 Swagger

    前言 在.NET 9发布以后ASP.NET Core官方团队发布公告已经将Swashbuckle.AspNetCore(一个为ASP.NET Core API提供Swagger工具的项目)从ASP.N ...

  6. ABAP配置:OY01 定义国家/地区

    配置:OY01 定义国家/地区 事务代码:OY01 配置路径: SPRO-ABAP平台-常规设置-设置国家-定义国家/地区 配置路径截图: 配置描述: 国家是SAP里面一个非常重要的概念,SAP国家概 ...

  7. c# Moq Ref/out 参数

    public interface IService { void DoSomething(ref string a); void DoSomething2(out string a); } [Test ...

  8. idea社区版本创建springboot项目的三种方式

    文章目录一.前言一.方式1:spring 官方创建 springboot项目1.打开在线的 spring initializr2.选择项目的语言.版本.依赖等3. 解压源码包,并使用IDEA打开4.测 ...

  9. atomic 包底层实现原理

    一.概念介绍(一)volatile关键字 Java 因为指令重排序,优化我们的代码,让程序运行更快,也随之带来了多线程下,指令执行顺序的不可控. 1.volatile关键字的作用: 内存可见性,修饰的 ...

  10. ctfshow 红包题第七弹 .git

    .git源码泄露 发现有后们 flag在上级目录里面 直接Letmein=show_source('../flag.txt');就出来了