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

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值