Serilog是.net里面非常不错的记录日志的库,另外一个我认为比较好的Log库是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.RollingFileSerilog.Sinks.MSSqlServer
通过Package Manager Console或者Nuget管理窗口进行安装:
PM> Install-Package Serilog.Sinks.RollingFile
PM> Install-Package Serilog.Sinks.MSSqlServer
这些都安装完了之后,我们开始配置。

配置:

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

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

配置创建完之后赋值给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里,这样就不用改太多代码。

  1. namespace LegacyApplication.Services.Core
  2. {
  3. public interface ICommonService
  4. {
  5. IUploadedFileRepository UploadedFileRepository { get; }
  6. IDepartmentRepository DepartmentRepository { get; }
  7. ILogger Log { get; }
  8. }
  9. public class CommonService : ICommonService
  10. {
  11. public IUploadedFileRepository UploadedFileRepository { get; }
  12. public IDepartmentRepository DepartmentRepository { get; }
  13. public ILogger Log { get; }
  14. public CommonService(
  15. IUploadedFileRepository uploadedFileRepository,
  16. IDepartmentRepository departmentRepository,
  17. ILogger log)
  18. {
  19. UploadedFileRepository = uploadedFileRepository;
  20. DepartmentRepository = departmentRepository;
  21. Log = log;
  22. }
  23. }
  24. }

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

  1. public abstract class ApiControllerBase : ApiController
  2. {
  3. protected readonly ICommonService CommonService;
  4. protected readonly IUnitOfWork UnitOfWork;
  5. protected readonly IDepartmentRepository DepartmentRepository;
  6. protected readonly IUploadedFileRepository UploadedFileRepository;
  7. protected readonly ILogger Log;
  8. protected ApiControllerBase(
  9. ICommonService commonService,
  10. IUnitOfWork untOfWork)
  11. {
  12. CommonService = commonService;
  13. UnitOfWork = untOfWork;
  14. DepartmentRepository = commonService.DepartmentRepository;
  15. UploadedFileRepository = commonService.UploadedFileRepository;
  16. Log = commonService.Log;
  17. }
  18. }

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

  1. #region Logging
  2. [NonAction]
  3. protected void LogByLevel(LogEventLevel level, string msg)
  4. {
  5. using (LogContext.PushProperty("Class", GetType().FullName)) // 对应于自定义的字段,对Sql server起作用, IDisposable
  6. using (LogContext.PushProperty("User", CurrentUserName))
  7. {
  8. Log.Write(level, $"{msg} (by {CurrentUserName}, at {Now:yyyy-MM-dd HH:mm:ss.FFF})");
  9. }
  10. }
  11. [NonAction]
  12. protected void LogVerbose(string msg)
  13. {
  14. LogByLevel(LogEventLevel.Verbose, msg);
  15. }
  16. [NonAction]
  17. protected void LogDebug(string msg)
  18. {
  19. LogByLevel(LogEventLevel.Debug, msg);
  20. }
  21. [NonAction]
  22. protected void LogInformation(string msg)
  23. {
  24. LogByLevel(LogEventLevel.Information, msg);
  25. }
  26. [NonAction]
  27. protected void LogWarning(string msg)
  28. {
  29. LogByLevel(LogEventLevel.Warning, msg);
  30. }
  31. [NonAction]
  32. protected void LogError(string msg)
  33. {
  34. LogByLevel(LogEventLevel.Error, msg);
  35. }
  36. [NonAction]
  37. protected void LogFatal(string msg)
  38. {
  39. LogByLevel(LogEventLevel.Fatal, msg);
  40. }
  41. #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());

  1. namespace LegacyStandalone.Web.MyConfigurations.Exceptions
  2. {
  3. public class MyExceptionLogger : ExceptionLogger
  4. {
  5. public override void Log(ExceptionLoggerContext context)
  6. {
  7. #if DEBUG
  8. Trace.TraceError(context.ExceptionContext.Exception.ToString());
  9. #endif
  10. using (LogContext.PushProperty("Class",
  11. context.ExceptionContext.ControllerContext.ControllerDescriptor.ControllerType))
  12. using (LogContext.PushProperty("User",
  13. context.RequestContext.Principal.Identity.Name))
  14. {
  15. LogException(context.ExceptionContext.Exception);
  16. }
  17. }
  18. private void LogException(Exception ex)
  19. {
  20. if (ex != null)
  21. {
  22. LogException(ex.InnerException);
  23. Serilog.Log.Logger.Error(ex.ToString());
  24. }
  25. }
  26. }
  27. }

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

问题

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