C#-使用Serilog框架快速实现日志及其相关扩展

目录

一、Serilog日志实现

1、实现 ILogEventSink接口

2、日志类Log

3、日志级别LogLevel 

4、ILogger接口

5、日志服务实现

6、日志视图View

7、ViewModel

二、功能扩展

1、日志扩展方法

2、Trace追踪扩展日志

3、自动滚动至底部


一、Serilog日志实现

安装NuGet包:Serilog

Sink有很多种,这里介绍两种:

                Console接收器(安装Serilog.Sinks.Console);

                File接收器(安装Serilog.Sinks.File);

MinimumLevel:最小记录级别

rollingInterval:生成日志文件周期

outputTemplate:输出日志模板

继承ILogEventSink接口实现 Emit:当Sink器接收到新日志时触发

通过该接口将接收器接收的日志添加进内部日志集合

将该接口实现类实例化对象通过WriteTo.Sink(myEventSink)与Logger绑定

1、实现 ILogEventSink接口

    public class LogEveSink : ILogEventSink{readonly object _lock = new object();static readonly Lazy<LogEveSink> _sink = new Lazy<LogEveSink>(() => new LogEveSink());public static LogEveSink Instance => _sink.Value;/// <summary>/// 日志内部集合/// </summary>private ObservableCollection<Log> _logs;/// <summary>/// 绑定到前台的日志视图/// </summary>public ListCollectionView Logs { get; set; }private LogEveSink(){_logs = new ObservableCollection<Log>();Logs = new ListCollectionView(_logs);}//private readonly ITextFormatter _formatter =//           new MessageTemplateTextFormatter("{Message} Location:{FilePath}[{LineNumber}]");public void Emit(LogEvent logEvent){lock (_lock){if (_logs.Count > 500){if (!Application.Current.CheckAccess())Application.Current.Dispatcher.Invoke(() =>{_logs.Clear();});else_logs.Clear();}if (logEvent != null){//var textWriter = new StringWriter();//_formatter.Format(logEvent, textWriter);if (!Application.Current.CheckAccess())Application.Current?.Dispatcher.InvokeAsync(() =>{_logs.Insert(0, new Log(){Time = logEvent?.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff"),Level = logEvent.Level,User = "Auston",Message = logEvent.MessageTemplate.ToString() //textWriter.ToString()});});else{_logs.Insert(0, new Log(){Time = logEvent?.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff"),Level = logEvent.Level,User = "Auston",Message = logEvent.MessageTemplate.ToString() //textWriter.ToString()});}}}}}

2、日志类Log

    public class Log{public string Time { get; set; }public LogEventLevel Level { get; set; }public string User { get; set; }public string Message { get; set; }}

3、日志级别LogLevel 

    public enum LogLevel{INFO,WARN,ERROR,DEBUG,FATAL}

4、ILogger接口

    public interface ILogger{void WriteLog(string message, LogLevel level=LogLevel.INFO);UserControl GetLogView();}

5、日志服务实现

    public class LoggerService : ILogger{public static LoggerService Default => new LoggerService();Serilog.ILogger logger;LogView logView = new LogView();//string outputTemplate = "{NewLine}Date: {Timestamp:yyyy-MM-dd HH:mm:ss.fff}\tLevel: {Level}\tCallName: {SourceContext}->{MemberName}"//     + "{NewLine}Path: {FilePath}[{LineNumber}]"//     + "{NewLine}Message: {Message}";string outputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} " +"[{Level:u3}] " +"Message:{Message}{NewLine}" +"{Exception}{NewLine}";public LoggerService(){logger = new LoggerConfiguration().Enrich.FromLogContext()//记录相关上下文信息.MinimumLevel.Debug().WriteTo.Sink(LogEveSink.Instance).WriteTo.File("Logs\\ALL\\.txt", rollingInterval: RollingInterval.Day, outputTemplate: outputTemplate).WriteTo.Logger(log => log.Filter.ByIncludingOnly(p => p.Level == LogEventLevel.Error).WriteTo.File("Logs\\Error\\.txt", rollingInterval: RollingInterval.Day, outputTemplate: outputTemplate)).CreateLogger();}public void WriteLog(string message, LogLevel level = LogLevel.INFO){switch (level){case LogLevel.DEBUG:logger.Debug(message);break;case LogLevel.INFO:logger.Information(message);break;case LogLevel.WARN:logger.Warning(message);break;case LogLevel.ERROR:logger.Error(message);break;case LogLevel.FATAL:logger.Fatal(message);break;default:logger.Verbose(message);break;}}public UserControl GetLogView(){return logView;}}

6、日志视图View

<UserControl x:Class="Test.Logger.View.LogView"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"xmlns:vm="clr-namespace:Test.Logger.ViewModel"x:Name="LogUC" d:DesignHeight="450"d:DesignWidth="800" mc:Ignorable="d"><UserControl.DataContext><vm:LogViewModel x:Name="viewmodel" /></UserControl.DataContext><UserControl.Resources><ResourceDictionary><ResourceDictionary.MergedDictionaries><ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" /><ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" /></ResourceDictionary.MergedDictionaries><!--<CollectionViewSource x:Key="SortSoruce" Source="{Binding ElementName=viewmodel, Path=LogSink.Logs}"><CollectionViewSource.SortDescriptions><scm:SortDescription Direction="Descending" PropertyName="Time" /></CollectionViewSource.SortDescriptions></CollectionViewSource>--></ResourceDictionary></UserControl.Resources><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="*" /></Grid.RowDefinitions><StackPanel Margin="5" VerticalAlignment="Center"Orientation="Horizontal"><RadioButton Margin="5"Command="{Binding LogFilter}"CommandParameter="0" Content="ALL"FontWeight="Bold" GroupName="filter"IsChecked="True" /><RadioButton Margin="5"Command="{Binding LogFilter}"CommandParameter="1" Content="INFO"FontWeight="Bold" Foreground="Green"GroupName="filter" /><RadioButton Margin="5"Command="{Binding LogFilter}"CommandParameter="2" Content="WARN"FontWeight="Bold" Foreground="Orange"GroupName="filter" /><RadioButton Margin="5"Command="{Binding LogFilter}"CommandParameter="3" Content="ERROR"FontWeight="Bold" Foreground="Red"GroupName="filter" /></StackPanel><DataGrid Grid.Row="1" Margin="5"AutoGenerateColumns="False"EnableColumnVirtualization="True"EnableRowVirtualization="True" FontWeight="Bold"IsReadOnly="True"ItemsSource="{Binding ElementName=viewmodel, Path=LogSink.Logs}"VirtualizingPanel.IsVirtualizing="True"VirtualizingPanel.VirtualizationMode="Recycling"><DataGrid.Columns><DataGridTextColumn Width="Auto"Binding="{Binding Time}"Header="Time" /><DataGridTextColumn Width="Auto"Binding="{Binding Level}"Header="Level" /><DataGridTextColumn Width="Auto"Binding="{Binding User}"Header="User" /><DataGridTextColumn Width="*"Binding="{Binding Message}"Header="Message"><DataGridTextColumn.ElementStyle><Style><Setter Property="TextBlock.TextWrapping" Value="Wrap" /><Setter Property="TextBlock.TextAlignment" Value="Left" /></Style></DataGridTextColumn.ElementStyle></DataGridTextColumn></DataGrid.Columns><DataGrid.RowStyle><Style TargetType="DataGridRow" BasedOn="{StaticResource DataGridRowStyle}"><Style.Triggers><DataTrigger Binding="{Binding Level}" Value="Information"><Setter Property="Foreground" Value="Green" /></DataTrigger><DataTrigger Binding="{Binding Level}" Value="Warning"><Setter Property="Foreground" Value="Orange" /></DataTrigger><DataTrigger Binding="{Binding Level}" Value="Error"><Setter Property="Foreground" Value="Red" /></DataTrigger></Style.Triggers></Style></DataGrid.RowStyle></DataGrid></Grid>
</UserControl>

7、ViewModel

    public class LogViewModel : ObservableObject{private LogEveSink logSink = LogEveSink.Instance;public LogEveSink LogSink{get => logSink;set => SetProperty(ref logSink, value);}/// <summary>/// 日志过滤命令/// </summary>public RelayCommand<string> LogFilter{get{return new RelayCommand<string>((para) =>{DoFilter(para);});}}/// <summary>/// 日志过滤命令注册函数/// </summary>/// <param name="mask"></param>private void DoFilter(string mask){switch (mask){case "0":LogSink.Logs.Filter = null;break;case "1":LogSink.Logs.Filter = i => ((Log)i).Level == LogEventLevel.Information || ((Log)i).Level == LogEventLevel.Verbose || ((Log)i).Level == LogEventLevel.Debug;break;case "2":LogSink.Logs.Filter = i => ((Log)i).Level == LogEventLevel.Warning;break;case "3":LogSink.Logs.Filter = i => ((Log)i).Level == LogEventLevel.Error || ((Log)i).Level == LogEventLevel.Fatal;break;}LogSink.Logs.Refresh();}}

二、功能扩展

1、日志扩展方法

    static class LogExtension{public static void CallError<T>(this ILogger logger, string message,[CallerMemberName] string meberName = "",[CallerFilePath] string filepath = "",[CallerLineNumber] int lineNum = 0)=> logger.ForContext<T>().ForContext("MemberName", meberName).ForContext("FilePath", filepath).ForContext("LineNumber", lineNum).Error(message);public static void CallError<T>(this ILogger logger, Exception e, string message,[CallerMemberName] string meberName = "",[CallerFilePath] string filepath = "",[CallerLineNumber] int lineNum = 0)=> logger.ForContext<T>().ForContext("MemberName", meberName).ForContext("FilePath", filepath).ForContext("LineNumber", lineNum).Error(e, message);}

2、Trace追踪扩展日志

继承抽象类TraceListener,重写方法TraceEvent

注意:添加监听对象Trace.Listeners.Add(this),推荐放到App.cs;

    public class TraceLog{public string Message { get; set; }public LogLevel Level { get; set; }}public class LoggerTraceListener : TraceListener{public static LoggerTraceListener Default => new LoggerTraceListener();public LoggerTraceListener(){Trace.Listeners.Add(this);}ILogger logger;ConcurrentQueue<TraceLog> _traceLogs = new ConcurrentQueue<TraceLog>();public void InitLogger(){logger = IOCService.Instance.AccessService<ILogger>();if (logger != null){Task.Run(() =>{while (_traceLogs.TryDequeue(out TraceLog log)){logger.WriteLog(log.Message, log.Level);}});}}public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message){LogLevel logLevel = LogLevel.INFO;switch (eventType){case TraceEventType.Error:logLevel = LogLevel.ERROR;break;case TraceEventType.Warning:logLevel = LogLevel.WARN;break;case TraceEventType.Information:logLevel = LogLevel.INFO;break;default:logLevel = LogLevel.DEBUG;break;}if (logger != null){logger.WriteLog(message, logLevel);return;}_traceLogs.Enqueue(new TraceLog() { Message = message, Level = logLevel });}public override void Write(string message){//MessageBox.Show(message);}public override void WriteLine(string message){//MessageBox.Show(message + "\r\n");}}

3、自动滚动至底部

通过ObservableCollection类的CollectionChanged事件实现日志自动滚动到底部:

        集合改变触发事件,更改附加属性AutoScroll值,值更改触发CallBack将日志滚动到底部;

注意:MouseEnterMouseLeave两事件的响应原因:查看日志时,防止日志自动滚动到底部;

        <DataGrid attach:ScrollHelper.AutoScroll="{Binding AutoScroll}"AutoGenerateColumns="False"CanUserAddRows="False"CanUserDeleteRows="False"CanUserReorderColumns="False"CanUserResizeColumns="False"CanUserResizeRows="False"CanUserSortColumns="False"ItemsSource="{Binding LogService.Logs}"><i:Interaction.Triggers><i:EventTrigger EventName="MouseEnter"><i:InvokeCommandAction Command="{Binding MouseEnterCommand}" /></i:EventTrigger><i:EventTrigger EventName="MouseLeave"><i:InvokeCommandAction Command="{Binding MouseLeaveCommand}" /></i:EventTrigger></i:Interaction.Triggers><DataGrid.Columns><DataGridTextColumn Binding="{Binding Time}" Header="时间" /><DataGridTextColumn Binding="{Binding Lev}" Header="级别" /><DataGridTextColumn Binding="{Binding Message}" Header="信息"><DataGridTextColumn.ElementStyle><Style><Setter Property="TextBlock.TextWrapping" Value="Wrap" /><Setter Property="TextBlock.TextAlignment" Value="Left" /></Style></DataGridTextColumn.ElementStyle></DataGridTextColumn></DataGrid.Columns><DataGrid.RowStyle><Style TargetType="DataGridRow" BasedOn="{StaticResource DataGridRowStyle}"><Style.Triggers><DataTrigger Binding="{Binding Lev}" Value="Error"><Setter Property="Foreground" Value="Red"/></DataTrigger><DataTrigger Binding="{Binding Lev}" Value="Warn"><Setter Property="Foreground" Value="Orange"/></DataTrigger></Style.Triggers></Style></DataGrid.RowStyle></DataGrid>
        public LogService LogService { get; set; }=LogService.GetInstance();private bool _autoScroll;public bool AutoScroll{get { return _autoScroll; }set => SetProperty(ref _autoScroll, value);}[RelayCommand]public void MouseEnter(){LogService._logs.CollectionChanged -= Scroll;}[RelayCommand]public void MouseLeave(){LogService._logs.CollectionChanged += Scroll;}private void Scroll(object sender, NotifyCollectionChangedEventArgs e){AutoScroll = !AutoScroll;}public MainWinViewModel(){LogService.OpenListen();LogService._logs.CollectionChanged += Scroll;}

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

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

相关文章

SpringBoot集成Thymeleaf模板引擎,为什么使用(详细介绍)

学习本技术第一件事&#xff1a;你为什么要使用&#xff0c;解决什么问题的&#xff1f; 1.为什么使用&#xff08;使用背景&#xff09;&#xff1f; 首先应用场景是单体项目&#xff0c;如果是前后端分离就不用关注这个了&#xff0c;因为单体项目你前后端都是写在一个项目…

性能小白终于能看懂Jmeter报告了

对于刚接触性能测试的初学者来说&#xff0c;分析JMeter生成的测试报告无疑是一个巨大的挑战。面对大量的数据信息&#xff0c;如何快速理解响应时间、吞吐量、错误率等关键指标&#xff0c;往往让人感到困惑。今天&#xff0c;让我们一起探讨如何轻松看懂JMeter的性能测试报告…

ResNet(Residual Network)网络介绍

1. ResNet的背景与创新&#xff1a; ResNet是由微软研究院的何恺明等人在2015年提出&#xff0c;并在ILSVRC&#xff08;ImageNet Large Scale Visual Recognition Challenge&#xff09;中取得了冠军。这一网络结构的提出&#xff0c;标志着深度学习领域的一个重要突破&#x…

多线程篇(其它容器- CopyOnWriteArrayList)(持续更新迭代)

一、CopyOnWriteArrayList&#xff08;一&#xff09; 1. 简介 并发包中的并发List只有CopyOnWriteArrayList。 CopyOnWriteArrayList是一个线程安全的ArrayList&#xff0c;对其进行的修改操作都是在底层的一个复制的数 组&#xff08;快照&#xff09;上进行的&#xff0…

HTML5下<hr>标签的CSS样式定制

引言 在HTML中&#xff0c;<hr>标签扮演着分隔内容的重要角色。作为水平线标签&#xff0c;<hr>在视觉上将文档分隔成各个部分&#xff0c;帮助用户更好地理解页面内容。本文将深入解析<hr>标签的定义、用法、属性以及在不同HTML规范中的地位和浏览器兼容性…

基于STM32C8T6的CubeMX:HAL库点亮LED

三个可能的问题和解决方法&#xff1a; 大家完成之后回来看&#xff0c;每一种改错误都是一种成长&#xff0c;不要畏惧&#xff0c;要快乐&#xff0c;积极面对&#xff0c;要耐心对待 STMCuBeMX新建项目的两种匪夷所思的问题https://mp.csdn.net/mp_blog/creation/editor/1…

【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树

在安卓源码的设计中&#xff0c;将将屏幕分为了37层&#xff0c;不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析&#xff0c;整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…

基于STM32的汽车仪表显示系统:集成CAN、UART与I2C总线设计流程

一、项目概述 项目目标与用途 本项目旨在设计和实现一个基于STM32微控制器的汽车仪表显示系统。该系统能够实时显示汽车的速度、转速、油量等关键信息&#xff0c;并通过CAN总线与其他汽车控制单元进行通信。这种仪表显示系统不仅提高了驾驶的安全性和便捷性&#xff0c;还能…

前端技术(七)——less 教程

一、less简介 1. less是什么&#xff1f; less是一种动态样式语言&#xff0c;属于css预处理器的范畴&#xff0c;它扩展了CSS语言&#xff0c;增加了变量、Mixin、函数等特性&#xff0c;使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 &#xff0c;也可以借助Node.js在服…

Semaphore UI --Ansible webui

1、安装python python下载地址 https://www.python.org/downloads/ 选好版本下载 wget https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tar.xz安装编译工具 sudo dnf groupinstall "Development Tools"安装依赖包 dnf install bzip2-devel ncurses-deve…

IDEA 常用配置和开发插件

件市场中搜索并安装“Git Integration”插件。 一、前言 在本篇文章中我会为大家总结一些我自己常用的配置和开发插件&#xff0c;此外也给大家提供一个建议&#xff0c;可以根据自己的项目需求和个人偏好选择适合的插件。另外&#xff0c;IDEA 也在不断更新&#xff0c;可能会…

哈希表、算法

哈希表 hash&#xff1a; 在编程和数据结构中&#xff0c;"hash" 通常指的是哈希函数&#xff0c;它是一种算法&#xff0c;用于将数据&#xff08;通常是字符 串&#xff09;映射到一个固定大小的数字&#xff08;哈希值&#xff09;。哈希函数在哈希表中尤为重要…

使用vue2+axios+chart.js画折线图 ,出现 RangeError: Maximum call stack size exceeded 错误

目录 效果图 解决方案 修正要点 效果图 修改前App.vue代码&#xff1a; <template><div id"app"><canvas id"myChart"></canvas></div> </template><script> import axios from axios; import { Chart, regis…

中级练习[7]:Hive SQL

目录 1. 统计每天男性和女性用户的订单总金额 1.1 题目需求 1.2 代码实现 2. 订单金额趋势分析 2.1 题目需求 2.2 代码实现 3. 购买过商品1和商品2但是没有购买商品3的顾客 3.1 题目需求 3.2 代码实现 1. 统计每天男性和女性用户的订单总金额 1.1 题目需求 从订单信…

stm32 W25Q数据存储

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、cubemx配置二、keil中文件修改与配置三、几个重要函数的说明四、DMA方式传输&#xff08;待写&#xff09;总结 前言 W25Q128 容量为128位 128/8 16 也就…

Mac 上终端使用 MySql 记录

文章目录 下载安装终端进入 MySql常用操作查看数据库选择一个数据库查看当前选择的数据库Navcat 打开提示报错参考文章 下载安装 先下载社区版的 MySql 安装的过程需要设置 root 的密码&#xff0c;这个是要进入数据库所设定的&#xff0c;所以要记住 终端进入 MySql 首先输…

Linux基础-Makefile的编写、以及编写第一个Linux程序:进度条(模拟在 方便下载的同时,更新图形化界面)

目录 一、Linux项目自动化构建工具-make/Makefile ​编辑 背景&#xff1a; makefile小技巧&#xff1a; 二、Linux第一个小程序&#xff0d;进度条 先导&#xff1a; 1.如何利用/r,fflush(stdout)来实现我们想要的效果&#xff1b; 2.写一个倒计时&#xff1a; 进度条…

智能家居环境监测系统设计(论文+源码)

1. 系统方案 系统由9个部分构成&#xff0c;分别是电源模块、烟雾传感器模块、GSM发送短信模块、报警模块、温度传感器模块、人体红外感应模块、按键设置模块、显示模块、MCU模块。各模块的作用如下&#xff1a;电源模块为系统提供电力&#xff1b;烟雾传感器模块检测烟雾浓度&…

猫狗识别大模型——基于python语言

目录 1.猫狗识别 2.数据集介绍 3.猫狗识别核心原理 4.程序思路 4.1数据文件框架 4.2 训练模型 4.3 模型使用 4.4 识别结果 5.总结 1.猫狗识别 人可以直接分辨出图片里的动物是猫还是狗&#xff0c;但是电脑不可以&#xff0c;要想让电脑也分辨出图片里的动物是猫还是小…

C++面试3

一、常用设计模式 https://blog.csdn.net/m0_71530237/article/details/141140118?spm1001.2014.3001.5501 二、死锁以及解决方式&#xff1f; 死锁&#xff1a;一种常见的并发问题&#xff0c;发生在多个进程或线程因为竞争资源而陷入相互等待的状态&#xff0c;导致这些进…