大家好,欢迎来到IT知识分享网。
目录
一、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将日志滚动到底部;
注意:MouseEnter与MouseLeave两事件的响应原因:查看日志时,防止日志自动滚动到底部;
<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; }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/118522.html