.NET Core使用Serilog自定义日志分类

        几个月前在研究了一番Serilog的时候,就想写一篇博客记录一下这个日志框架,可惜忙过了头,给忘了。那么它的优势就不多介绍了,相信你点到这篇文章之前,已经看了不少博客,且没有头绪,哈哈😄

        废话不多说,本文不介绍Serilog的基础用法,只介绍它的一个特殊用法,就是如何使用自定义分类日志,就是把日志记录到不同的业务文件夹,由于时间久远,所以就只贴关键代码啦,懒人教程。

        首先把这俩引用加了,当然不一定是这个版本哈


var builder = WebApplication.CreateBuilder(args);
//关键代码,记得加
builder.Host.UseSerilog();

SerilogHelper.InitSerilog();

   这里使用纯代码方式,非配置文件(不会用配置文件来分类,如有大佬知道,请赐教,感谢!)

请关注Log.ForContext方法和Serilog的过滤器,它是实现将日志分类的关键所在,这一点也是在官网看到的,提供了很好的灵感。

public class SerilogHelper
    {
        #region Serilog 相关设置
        internal static string LogFilePath(string fileName) => $@"Logs/{fileName}/{DateTime.Now.Year}.{DateTime.Now.Month}/log_.log";
        internal static readonly string seriCustomProperty = "seriPos";
        internal static readonly string logOutputTemplate = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}][{ThreadId}] {Message:lj}{NewLine}{Exception}";
        internal static readonly long? fileSize = 31457280L;
        /// <summary>
        /// 初始化serilog
        /// </summary>
        public static void InitSerilog()
        {
            /*
            WriteTo.File,可同步或异步,异步需要引用async包
            path:默认路径是程序的bin目录+path参数,当然也可以写绝对路径,只需要写入参数就可以了
            rollingInterval:创建文件的类别,可以是分钟,小时,天,月。 此参数可以让创建的log文件名 + 时间。例如log_20220202_001.log
            fileSizeLimitBytes:文件大小限制,类型long,1024=1KB,31457280=30MB
            rollOnFileSizeLimit:达到文件大小限制后,是否继续创建新文件,如log_20220202_001.log
            outputTemplate:日志模板,可以自定义
            retainedFileCountLimit:设置日志文件个数最大值,默认31,意思就是只保留最近的31个日志文件,等于null时永远保留文件
            restrictedToMinimumLevel:最小写入级别,接收器Sink的级别必须高于Logger的级别,比如Logger的默认级别为 Information,即便 Sink 重写日志级别为 LogEventLevel.Debug,也只能看到 Information
            */


            Serilog.Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Override("Default", LogEventLevel.Information)
                .MinimumLevel.Override("Microsoft", LogEventLevel.Error)
                .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
                .Enrich.FromLogContext()//记录相关上下文信息 
                .WriteTo.Console(restrictedToMinimumLevel: levelConsole, theme: Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme.Code, outputTemplate: logOutputTemplate)
                //利用过滤器对输出文件按文件夹分类
                //如果使用依赖注入ILogger记录日志,当需要记录到指定文件夹,需要传入seriPos,否则只记录到默认Default文件夹
                //比如:Log.Information("{seriPos}:Test", LogType.Order);//LogType自定义枚举
                .WriteTo.Logger(lg =>
                {
                    lg.Filter.ByIncludingOnly(e =>
                    {
                        try
                        {
                            if (e.Properties.TryGetValue(seriCustomProperty, out var value))
                            {
                                ScalarValue scalarValue = value as ScalarValue;
                                LogType arg = (LogType)scalarValue.Value;
                                if (arg != LogType.Default)
                                    return false;
                            }
                            return true;
                        }
                        catch (Exception)
                        {
                            return false;
                        }
                    });
                    lg.WriteTo.Async(a => a.File(LogFilePath(LogType.Default.ToString()), rollingInterval: RollingInterval.Day, fileSizeLimitBytes: fileSize, rollOnFileSizeLimit: true, outputTemplate: logOutputTemplate));
                })
                .WriteToFilePath(new List<LogType> { LogType.Order, LogType.Goods })
                .CreateLogger();
        }
        #endregion

        /*****************************日志级别*****************************/
        // FATAL(致命错误) > ERROR(一般错误) > Warning(警告) > Information(一般信息) > DEBUG(调试信息)>Verbose(详细模式,即全部)

        #region Info
        public static void Info(string msg, params object[] args)
        {
            Serilog.Log.Information(msg, args);
        }

        public static void Info(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Information(msg, args);
        }
        #endregion

        #region Debug
        public static void Debug(string msg, params object[] args)
        {
            Serilog.Log.Debug(msg, args);
        }

        public static void Debug(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Debug(msg, args);
        }

        public static void Debug(Exception err, string msg)
        {
            Serilog.Log.Debug(err, msg);
        }

        public static void Debug(LogType logType, Exception err, string msg)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Debug(err, msg);
        }
        #endregion

        #region Warning
        public static void Warning(string msg, params object[] args)
        {
            Serilog.Log.Warning(msg, args);
        }

        public static void Warning(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Warning(msg, args);
        }
        #endregion

        #region Error
        public static void Error(string msg, params object[] args)
        {
            Serilog.Log.Error(msg, args);
        }

        public static void Error(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Error(msg, args);
        }

        public static void Error(Exception err, string msg)
        {
            Serilog.Log.Error(err, msg);
        }

        public static void Error(LogType logType, Exception err, string msg)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Error(err, msg);
        }

        public static void Error(Exception err, string msg, params object[] args)
        {
            Serilog.Log.Error(err, msg, args);
        }

        public static void Error(LogType logType, Exception err, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Error(err, msg, args);
        }

        #endregion

        #region Fatal
        public static void Fatal(string msg, params object[] args)
        {
            Serilog.Log.Fatal(msg, args);
        }

        public static void Fatal(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Fatal(msg, args);
        }

        public static void Fatal(Exception err, string msg)
        {
            Serilog.Log.Fatal(err, msg);
        }

        public static void Fatal(LogType logType, Exception err, string msg)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Fatal(err, msg);
        }
        #endregion

        #region Verbose
        public static void Verbose(string msg, params object[] args)
        {
            Serilog.Log.Verbose(msg, args);
        }

        public static void Verbose(Exception err, string msg)
        {
            Serilog.Log.Verbose(err, msg);
        }
        #endregion
    }

   自定义扩展方法,用于加载不同的日志类型

public static class LoggerConfigurationExtiensions
    {
        /// <summary>
        /// 遍历集合项,将Log输出到对应LogType文件夹
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="logType">logType作为子Log文件夹名</param>
        /// <returns></returns>
        public static LoggerConfiguration WriteToFilePath(this LoggerConfiguration logger, List<LogType> logType)
        {
            logType.ForEach(
                q =>
                {
                    logger.WriteTo.Logger(lg =>
                    {
                        lg.Filter.ByIncludingOnly(Matching.WithProperty<LogType>(SerilogHelper.seriCustomProperty, p => p == q));
                        lg.WriteTo.Async(a => a.File(SerilogHelper.LogFilePath(q.ToString()), rollingInterval: RollingInterval.Day, fileSizeLimitBytes: SerilogHelper.fileSize, rollOnFileSizeLimit: true,
                            outputTemplate: SerilogHelper.logOutputTemplate));
                    });

                }
            );
            return logger;
        }
    }

  另外Serilog有个比较好玩的功能,扩展器Enricher,我们可以自由扩展一些属性,并把属性记录在日志中,像NLog的property一样,比如这里想把线程ID加到日志中。

//使用扩展器将线程ID附加到每个事件,可以在输出内容加入
    //例:outputTemplate: "{Timestamp:HH:mm} [{Level}] ({ThreadId}) {Message}{NewLine}{Exception}")
    public class ThreadIdEnricher : ILogEventEnricher
    {
        public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
        {
            logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(
                    "ThreadId", Environment.CurrentManagedThreadId));
        }
    }

可能有人会问,为什么要定义seriCustomProperty呢,这个主要是为了兼容Microsoft.Extensions.Logging.ILogger,毕竟有些时候,我们不想去使用Helper类,而是想在Controller或者其他类中,通过依赖注入拿到ILogger对象去记录Log,比如:

public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }
}

可是Microsoft.Extensions.Logging.ILogger也只是提供基础接口,真正的实现还是Serilog,那么如何将Microsoft.Extensions.Logging.ILogger记录的Log进入Serilog的过滤器呢,那就可以通过如下方式触发:

//{seriPos}可以理解为占位符,占位符的名称正是在SerilogHelper中声明的变量(用常量会更合适)
//string seriCustomProperty = "seriPos";
Log.Information("{seriPos}:Test", LogType.Order);

//关键代码
lg.Filter.ByIncludingOnly(e =>
                    {
                        try
                        {
                            if (e.Properties.TryGetValue(seriCustomProperty, out var value))
                            {
                                ScalarValue scalarValue = value as ScalarValue;
                                LogType arg = (LogType)scalarValue.Value;
                                if (arg != LogType.Default)
                                    return false;
                            }
                            return true;
                        }
                        catch (Exception)
                        {
                            return false;
                        }
                    });

总之,你可以以任意方式实现,Log.Information("{seriPos}:Test", LogType.Order);这种方式虽然可以实现,但用起来也确实不太舒适,所以最终我基本不用。

后来又做了一层封装,:

  public interface ISeriLogger
    {
        void LogVerbose(string msg, params object[] args);
        void LogVerbose(LogType logType, string msg, params object[] args);
        void LogInformation(string msg, params object[] args);
        void LogInformation(LogType logType, string msg, params object[] args);

        void LogDebug(string msg, params object[] args);
        void LogDebug(LogType logType, string msg, params object[] args);
        void LogDebug(Exception err, string msg);
        void LogDebug(LogType logType, Exception err, string msg);

        void LogWarning(string msg, params object[] args);
        void LogWarning(LogType logType, string msg, params object[] args);

        void LogError(string msg, params object[] args);
        void LogError(LogType logType, string msg, params object[] args);
        void LogError(Exception err, string msg);
        void LogError(LogType logType, Exception err, string msg);
        void LogError(Exception err, string msg, params object[] args);
        void LogError(LogType logType, Exception err, string msg, params object[] args);
    }

 public class SeriLogger : ISeriLogger
    {
        readonly string seriCustomProperty = SerilogHelper.seriCustomProperty;


        public void LogVerbose(string msg, params object[] args)
        {
            Serilog.Log.Verbose(msg, args);
        }

        public void LogVerbose(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Verbose(msg, args);
        }

        public void LogInformation(string msg, params object[] args)
        {
            Serilog.Log.Information(msg, args);
        }

        public void LogInformation(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Information(msg, args);
        }

        public void LogDebug(string msg, params object[] args)
        {
            Serilog.Log.Debug(msg, args);
        }

        public void LogDebug(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Debug(msg, args);
        }

        public void LogDebug(Exception err, string msg)
        {
            Serilog.Log.Debug(err, msg);
        }

        public void LogDebug(LogType logType, Exception err, string msg)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Debug(err, msg);
        }

        public void LogError(string msg, params object[] args)
        {
            Serilog.Log.Error(msg, args);
        }

        public void LogError(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Error(msg, args);
        }

        public void LogError(Exception err, string msg)
        {
            Serilog.Log.Error(err, msg);
        }

        public void LogError(LogType logType, Exception err, string msg)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Error(err, msg);
        }

        public void LogError(Exception err, string msg, params object[] args)
        {
            Serilog.Log.Error(err, msg, args);
        }

        public void LogError(LogType logType, Exception err, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Error(err, msg, args);
        }

        public void LogWarning(string msg, params object[] args)
        {
            Serilog.Log.Warning(msg, args);
        }

        public void LogWarning(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Warning(msg, args);
        }

        public void Verbose(string msg, params object[] args)
        {
            Serilog.Log.Verbose(msg, args);
        }

        public void Verbose(LogType logType, string msg, params object[] args)
        {
            Serilog.Log.ForContext(seriCustomProperty, logType).Verbose(msg, args);
        }
    }

把ISeriLogger注册到Autofac或其他容器,使用时就通过依赖注入去用即可。

总之,你可以使用SerilogHelper,也可以使用ISerilog或ILogger

public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly ISeriLogger seriLogger;

        public HomeController(ILogger<HomeController> logger, ISeriLogger seriLogger)
        {
            _logger = logger;
            this.seriLogger = seriLogger;
        }

        public IActionResult Index()
        {
            _logger.LogInformation("Hello world");//默认记录到Default文件夹
            _logger.LogInformation("{seriPos}:Hello world",LogType.Order);//记录到Order文件夹

            seriLogger.LogInformation("Hello world");//默认记录到Default文件夹
            seriLogger.LogInformation(LogType.Order,"Hello world");//记录到Order文件夹

            SerilogHelper.Info("Hello world");//默认记录到Default文件夹
            SerilogHelper.Info(LogType.Order, "Hello world");//记录到Order文件夹
            return View();
        }
}

至此结束!下一篇可能是NLog

参考:Writing Log Events · serilog/serilog Wiki · GitHub

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Boogaloo-Jer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值