当前位置: 首页 > news >正文

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;}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 24年最新版pycharm找不到conda可执行文件解决办法(简单完美解决)
  • 你的代码真的安全吗?经验告诉你怎么做
  • stm32之SPI通信协议
  • fpga入门名词(1)
  • 分布式共识(一致性)算法(协议) Paxos 简介
  • TS中的装饰器
  • Win电脑使用Ollama与Open Web UI搭建本地大语言模型运行工具
  • 第三届828 B2B企业节开幕,大腾智能携手华为云共谱数字化新篇章
  • 硬件工程师笔试面试知识器件篇——电感
  • springboot 的共享session方案?
  • 【AcWing】860. 染色法判定二分图
  • MySQL系列—8.存储结构
  • 中医世家龚洪海博士:用医术和真诚赢得患者的心
  • SPIRNGBOOT+VUE实现浏览器播放音频流并合成音频
  • 【开源大模型生态6】生态大咖与产品布局
  • [笔记] php常见简单功能及函数
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • cookie和session
  • C语言笔记(第一章:C语言编程)
  • php中curl和soap方式请求服务超时问题
  • Ruby 2.x 源代码分析:扩展 概述
  • SpiderData 2019年2月25日 DApp数据排行榜
  • Spring Cloud中负载均衡器概览
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • Vue 动态创建 component
  • 技术胖1-4季视频复习— (看视频笔记)
  • 树莓派 - 使用须知
  • 说说动画卡顿的解决方案
  • 线上 python http server profile 实践
  • 由插件封装引出的一丢丢思考
  • 《天龙八部3D》Unity技术方案揭秘
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • ​第20课 在Android Native开发中加入新的C++类
  • ​如何使用QGIS制作三维建筑
  • #565. 查找之大编号
  • #Z2294. 打印树的直径
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (自适应手机端)响应式服装服饰外贸企业网站模板
  • .“空心村”成因分析及解决对策122344
  • .axf 转化 .bin文件 的方法
  • .NET Core 版本不支持的问题
  • .Net 垃圾回收机制原理(二)
  • .Net 应用中使用dot trace进行性能诊断
  • .NET 中创建支持集合初始化器的类型
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .netcore 如何获取系统中所有session_ASP.NET Core如何解决分布式Session一致性问题
  • .net访问oracle数据库性能问题
  • [ 云计算 | AWS ] 对比分析:Amazon SNS 与 SQS 消息服务的异同与选择
  • [20150707]外部表与rowid.txt