zoukankan      html  css  js  c++  java
  • 在asp.net web api 2 (ioc autofac) 使用 Serilog 记录日志

    Serilog是.net里面非常不错的记录日志的库,另外一个我认为比较好的Log库是NLog。

    在我个人的asp.net web api 2 基础框架(Github地址)里,我原来使用的是NLog,但是由于好奇心,我决定使用Serilog代替Nlog。

    安装:

    首先安装 Serilog,通过Package Manager Console或者Nuget管理窗口进行安装:

    PM> Install-Package Serilog

    然后安装 Serilog的Sinks,所谓Sink就是记录Log的途径,比如在控制台输出,在Debug窗口输出,输出到文件,输出到数据库等等。

    这里有一个列表,列出了所有的Sink:https://github.com/serilog/serilog/wiki/Provided-Sinks

    由于我使用的是asp.net web api 2.2 (.Net Framework 4.6+),所以我的项目里面暂时不需要用到Console,所以不安装官方教程的Serilog.Sinks.Literate。

    但是我需要在VS的Debug窗口显示Log,所以安装Serilog.Sinks.Debug

    通过Package Manager Console或者Nuget管理窗口进行安装:

    PM> Install-Package Serilog.Sinks.Debug

    我还需要输出到文件和Sql Server数据库,所以再安装 Serilog.Sinks.RollingFile 和 Serilog.Sinks.MSSqlServer

    通过Package Manager Console或者Nuget管理窗口进行安装:

    PM> Install-Package Serilog.Sinks.RollingFile
    PM> Install-Package Serilog.Sinks.MSSqlServer

    这些都安装完了之后,我们开始配置。

    配置:

    在Web项目里,我建立了一个配置类:

       public class SerilogConfiguration
        {
            public static void CreateLogger()
            {
            // 这一部分是配置Sql Server的Sink
    const string connectionString = AppSettings.DefaultConnection; // 数据库连接字符串 const string tableName = "Logs"; // 表名 var columnOptions = new ColumnOptions // 自定义字段 { AdditionalDataColumns = new Collection<DataColumn> { new DataColumn {DataType = typeof (string), ColumnName = "User"}, new DataColumn {DataType = typeof (string), ColumnName = "Class"}, } };
           // Sql Server的表中加入Json格式Log Event的数据字段 columnOptions.Store.Add(StandardColumn.LogEvent);
           // 输出模板,Sql Server不能用这个
    const string outputTemplate = "[{Timestamp:HH:mm:ss.FFF} {Level}] {Message} ({SourceContext:l}){NewLine}{Exception}"; Serilog.Log.Logger = new LoggerConfiguration() .MinimumLevel.Verbose() // 所有Sink的最小记录级别 .Enrich.WithProperty("SourceContext", null) //加入属性SourceContext,也就运行时是调用Logger的具体类 .Enrich.FromLogContext() //动态加入属性,主要是针对上面的自定义字段User和Class,当然也可以随时加入别的属性。 .WriteTo.Debug( outputTemplate: outputTemplate) // 写到VS Output 窗口 .WriteTo.RollingFile("logs\{Date}.log", shared: true, restrictedToMinimumLevel: LogEventLevel.Debug, outputTemplate: outputTemplate) // 写到文件,每天一个,最小记录级别是Debug,文件格式是 yyyyMMdd.log
             // 记录到Sql Server,最小级别是Information .WriteTo.MSSqlServer(connectionString, tableName, columnOptions: columnOptions, autoCreateSqlTable:
    true, restrictedToMinimumLevel: LogEventLevel.Information) .CreateLogger(); } }

    配置创建完之后赋值给Serilog.Log.Logger,它是一个静态变量。

    要在进行IOC配置之前调用这个配置类。

    注意,记录到Sql server那行配置,我设定的是自动创建表autoCreateSqlTable: true,但是如果创建后,这部分配置(Sql Server Sink)有更改,就需要把生成的表删掉,再让它重新自动建立一个,否则就无法再记录到Sql Server里面了。

    Serilog的级别一共有6个,Verbose - Debug - Information - Warning - Error - Fatal,详见其文档。

    配置IOC

    因为我的框架都是使用依赖注入模式的,所以Serilog配置完之后,我们要进行IOC的配置,我使用的是Autofac(非常好的库),它可以自动Dispose配置的类,如果这个类实现了IDisposable接口的话,例如Serilog。

    首先安装Serilog的Autofac集成库:

    PM> Install-Package AutofacSerilogIntegration

    然后到AutofacWebapiConfig.cs进行配置:

    builder.RegisterLogger(autowireProperties: true);

    非常的简单,就一句话。

    依赖注入

    配置完IOC,我们可以注入Serilog的ILogger进行使用,我们把它注入到Service层的CommonService里而不是所有的Controller里,这样就不用改太多代码。

    namespace LegacyApplication.Services.Core
    {
        public interface ICommonService
        {
            IUploadedFileRepository UploadedFileRepository { get; }
            IDepartmentRepository DepartmentRepository { get; }
            ILogger Log { get; }
        }
    
        public class CommonService : ICommonService
        {
            public IUploadedFileRepository UploadedFileRepository { get; }
            public IDepartmentRepository DepartmentRepository { get; }
            public ILogger Log { get; }
    
            public CommonService(
                IUploadedFileRepository uploadedFileRepository,
                IDepartmentRepository departmentRepository,
                ILogger log)
            {
                UploadedFileRepository = uploadedFileRepository;
                DepartmentRepository = departmentRepository;
                Log = log;
            }
        }
    }

    然后在所有Controller的父类里,就可以获取到ILogger了。

    public abstract class ApiControllerBase : ApiController
        {
            protected readonly ICommonService CommonService;
            protected readonly IUnitOfWork UnitOfWork;
            protected readonly IDepartmentRepository DepartmentRepository;
            protected readonly IUploadedFileRepository UploadedFileRepository;
            protected readonly ILogger Log;
    
            protected ApiControllerBase(
                ICommonService commonService,
                IUnitOfWork untOfWork)
            {
                CommonService = commonService;
                UnitOfWork = untOfWork;
                DepartmentRepository = commonService.DepartmentRepository;
                UploadedFileRepository = commonService.UploadedFileRepository;
                Log = commonService.Log;
            }
    View Code

    在这个Controller父类(ApiControllerBase.cs)里,继续封装一些Log的方法,以便所有的派生Controller可以简单的使用:

    #region Logging
    
            [NonAction]
            protected void LogByLevel(LogEventLevel level, string msg)
            {
                using (LogContext.PushProperty("Class", GetType().FullName)) // 对应于自定义的字段,对Sql server起作用, IDisposable
                using (LogContext.PushProperty("User", CurrentUserName))
                {
                    Log.Write(level, $"{msg} (by {CurrentUserName}, at {Now:yyyy-MM-dd HH:mm:ss.FFF})");
                }
            }
    
            [NonAction]
            protected void LogVerbose(string msg)
            {
                LogByLevel(LogEventLevel.Verbose, msg);
            }
    
            [NonAction]
            protected void LogDebug(string msg)
            {
                LogByLevel(LogEventLevel.Debug, msg);
            }
    
            [NonAction]
            protected void LogInformation(string msg)
            {
                LogByLevel(LogEventLevel.Information, msg);
            }
    
            [NonAction]
            protected void LogWarning(string msg)
            {
                LogByLevel(LogEventLevel.Warning, msg);
            }
    
            [NonAction]
            protected void LogError(string msg)
            {
                LogByLevel(LogEventLevel.Error, msg);
            }
    
            [NonAction]
            protected void LogFatal(string msg)
            {
                LogByLevel(LogEventLevel.Fatal, msg);
            }
    
            #endregion

    其中:

    using (LogContext.PushProperty("Class", GetType().FullName))
    using (LogContext.PushProperty("User", CurrentUserName))

    这部分是针对Serilog的Sql Server配置的自定义字段部分。

    全局异常记录

    针对asp.net web api 2,我使用了自定义的全局异常记录类:MyExceptionLogger.cs

    GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new MyExceptionLogger());
    GlobalConfiguration.Configuration.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());
    namespace LegacyStandalone.Web.MyConfigurations.Exceptions
    {
        public class MyExceptionLogger : ExceptionLogger
        {
            public override void Log(ExceptionLoggerContext context)
            {
    #if DEBUG
                Trace.TraceError(context.ExceptionContext.Exception.ToString());
    #endif
                using (LogContext.PushProperty("Class",
                    context.ExceptionContext.ControllerContext.ControllerDescriptor.ControllerType))
                using (LogContext.PushProperty("User",
                    context.RequestContext.Principal.Identity.Name))
                {
                    LogException(context.ExceptionContext.Exception);
                }
            }
    
            private void LogException(Exception ex)
            {
                if (ex != null)
                {
                    LogException(ex.InnerException);
                    Serilog.Log.Logger.Error(ex.ToString());
                }
            }
        }
    }

    在这里我使用的是静态版本的Serilog的Logger。

    问题

    经使用测试,输出到Debug窗口和Sql Server数据库是没有问题的,但是在asp.net web api 2项目的开发环境里一直无法输出到文件,我新建立了一个web api项目也是如此,但是在控制台应用却没有问题,今天晚些时候我将继续研究并解决这个问题。

  • 相关阅读:
    Windows Azure Web Site (19) Azure Web App链接到VSTS
    Windows Azure Virtual Machine (35) Azure VM通过Linked DB,执行SQL Job
    Azure PowerShell (16) 并行开关机Azure ARM VM
    Windows Azure Virtual Network (12) 虚拟网络之间点对点连接VNet Peering
    Azure ARM (21) Azure订阅的两种管理模式
    Windows Azure Platform Introduction (14) 申请海外的Windows Azure账户
    Azure ARM (20) 将非托管磁盘虚拟机(Unmanage Disk),迁移成托管磁盘虚拟机(Manage Disk)
    Azure ARM (19) 将传统的ASM VM迁移到ARM VM (2)
    Azure ARM (18) 将传统的ASM VM迁移到ARM VM (1)
    Azure Automation (6) 执行Azure SQL Job
  • 原文地址:https://www.cnblogs.com/cgzl/p/7616906.html
Copyright © 2011-2022 走看看