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

server/2024/10/18 1:33:00/

目录

一、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.ppmy.cn/server/131354.html

相关文章

每天一个数据分析题(五百零四)- 抽取样本

下列哪种方法&#xff0c;会重复抽取训练数据集中的数据&#xff0c;且每笔被抽中的概率始终保持一样&#xff1f; A. 袋装法&#xff08;Bagging&#xff09; B. 提升法&#xff08;Boosting&#xff09; C. 支持向量机&#xff08;SVM&#xff09; D. 以上皆是 数据分析…

Pyppeteer:如何在 Python 中使用 Puppeteer 和 Browserless?

Python 中的 Pyppeteer 是什么&#xff1f; Pyppeteer 是流行的 Node.js 库 Puppeteer 的 Python 移植版本&#xff0c;用于以编程方式控制无头 Chrome 或 Chromium 浏览器。 本质上&#xff0c;Pyppeteer 允许 Python 开发人员在 Web 浏览器中自动执行任务&#xff0c;例如抓…

Python OpenCV精讲系列 - 三维重建深入理解(十七)

&#x1f496;&#x1f496;⚡️⚡️专栏&#xff1a;Python OpenCV精讲⚡️⚡️&#x1f496;&#x1f496; 本专栏聚焦于Python结合OpenCV库进行计算机视觉开发的专业教程。通过系统化的课程设计&#xff0c;从基础概念入手&#xff0c;逐步深入到图像处理、特征检测、物体识…

Django makemigrations时出现ModuleNotFoundError: No module named ‘MySQLdb‘

使用Python 3.11、Django 5.1.2 写完model进行makemigrations时出现报错 查找资料发现说是mysqldb适用于Python2&#xff0c;不支持Python3&#xff1b;python3可以使用pymysql 安装pymsql pip install pymysql 然后要在项目的__init__.py中加如下代码&#xff1a; import …

微服务架构:核心组件解析与设计思考(服务发现、API网关、 配置中心、负载均衡、服务调用、服务熔断、链路追踪、消息队列、服务安全、分布式事务)

微服务架构已成为大型系统设计中不可忽视的趋势&#xff0c;它通过将单一系统拆分为多个自治的服务&#xff0c;解决了传统单体架构难以应对的复杂性和扩展性问题。然而&#xff0c;微服务架构的成功依赖于多个核心组件的协同工作&#xff0c;从服务发现到API网关&#xff0c;从…

Spring Cloud Netflix Eureka 注册中心讲解和案例示范

在微服务架构中&#xff0c;服务的发现和注册是至关重要的一环。Netflix Eureka 是一个在云端设计的服务注册与发现系统。它允许各个微服务将自身注册到注册中心&#xff0c;并在需要时发现其他服务&#xff0c;从而实现客户端负载均衡、服务容错以及动态扩展。本文将深入分析 …

【Golang】Go 语言中的 time 包详解:全面掌握时间处理与应用

在 Go 语言中&#xff0c;time 包提供了强大的时间处理功能&#xff0c;适用于各种场景&#xff1a;获取当前时间、格式化和解析时间、计算时间间隔、设置定时器、处理超时等。在开发过程中&#xff0c;熟练掌握 time 包能够帮助我们轻松处理时间相关的操作&#xff0c;尤其是定…

田渊栋​:求道之人,不问寒暑

好久没写这个系列了&#xff0c;一方面是因为我最近实在比较忙&#xff0c;另一方面也是想要等一等&#xff0c;分享自己觉得挺重要的结果。这次正好在去COLM的飞机上有点空&#xff0c;写一点。 从第一篇田渊栋&#xff1a;求道之人&#xff0c;不问寒暑开始&#xff0c;这个…