在asp.net web api 2 使用 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;
}
在这个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项目也是如此,但是在控制台应用却没有问题,今天晚些时候我将继续研究并解决这个问题。
在asp.net web api 2 使用 Serilog 记录日志的更多相关文章
- 在asp.net web api 2 (ioc autofac) 使用 Serilog 记录日志
Serilog是.net里面非常不错的记录日志的库,另外一个我认为比较好的Log库是NLog. 在我个人的asp.net web api 2 基础框架(Github地址)里,我原来使用的是NLog,但 ...
- 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用
由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...
- ASP.NET Web API Model-ActionBinding
ASP.NET Web API Model-ActionBinding 前言 前面的几个篇幅把Model部分的知识点划分成一个个的模块来讲解,而在控制器执行过程中分为好多个过程,对于控制器执行过程(一 ...
- ASP.NET Web API Model-ParameterBinding
ASP.NET Web API Model-ParameterBinding 前言 通过上个篇幅的学习了解Model绑定的基础知识,然而在ASP.NET Web API中Model绑定功能模块并不是被 ...
- ASP.NET Web API Model-ModelBinder
ASP.NET Web API Model-ModelBinder 前言 本篇中会为大家介绍在ASP.NET Web API中ModelBinder的绑定原理以及涉及到的一些对象模型,还有简单的Mod ...
- ASP.NET Web API Model-ValueProvider
ASP.NET Web API Model-ValueProvider 前言 前面一篇讲解了Model元数据,Model元数据是在Model绑定中很重要的一部分,只是Model绑定中涉及的知识点比较多 ...
- ASP.NET Web API Model-ModelMetadata
ASP.NET Web API Model-ModelMetadata 前言 前面的几个篇幅主要围绕控制器的执行过程,奈何执行过程中包含的知识点太庞大了,只能一部分一部分的去讲解,在上两篇中我们看到在 ...
- ASP.NET Web API 过滤器创建、执行过程(二)
ASP.NET Web API 过滤器创建.执行过程(二) 前言 前面一篇中讲解了过滤器执行之前的创建,通过实现IFilterProvider注册到当前的HttpConfiguration里的服务容器 ...
- ASP.NET Web API 过滤器创建、执行过程(一)
ASP.NET Web API 过滤器创建.执行过程(一) 前言 在上一篇中我们讲到控制器的执行过程系列,这个系列要搁置一段时间了,因为在控制器执行的过程中包含的信息都是要单独的用一个系列来描述的,就 ...
随机推荐
- 每次启动懂maven项目都必须关闭javaw.exe进程
最近几天一致在搞配置问题,今天总于解决了.下面说问题: 我发现我每次配置完maven build的goals运行,总是会出现TOMCAT绑定接口问题,我做的方法就是不断地关掉JAVAw.exe进程.但 ...
- [Caffe]使用经验积累
Caffe使用经验积累 本贴记录Caffe编译好了,使用过程的常用命令与常见错误解决方式.如果对编译过程还存在问题,请参考史上最全的caffe安装过程配置Caffe环境. 1 使用方法 训练网络 xx ...
- Spring详解(三)------DI依赖注入
上一篇博客我们主要讲解了IOC控制反转,也就是说IOC 让程序员不在关注怎么去创建对象,而是关注与对象创建之后的操作,把对象的创建.初始化.销毁等工作交给spring容器来做.那么创建对象的时候,有可 ...
- Ext.grid.EditorGridPanel点击单元格添加菜单栏
1.定义菜单栏需要的全局变量 var khbm; var type; 2.新建一个菜单栏 var smenu = new Ext.menu.Menu({ id:"sMenu", i ...
- IBM与麻省理工学院联合建立AI实验室 承诺投资2.4亿美元
IBM和麻省理工学院将通过今天宣布的一个新的联合实验室共同努力,对人工智能进行广泛的研究.麻省理工学院IBM沃森AI实验室将重点关注四个研究支柱:开发AI算法,使用物理学来创建AI计算的新硬 ...
- shell变量$(CURDIR),$0,$1,$2,$#含义解释
$(CURDIR): CURDIR是make的内嵌变量, 为当前目录 实例 SRCTREE := $(CURDIR) *$(CURDIR)为当前目录,相当于SRCTREE=./ MKCONFIG ...
- linux模拟实现主机跨路由通信
p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-bottom: .0001pt; text-align: justify; f ...
- tkinter第二章(添加图片,背景图片)
#插入文件图片import tkinter as tk root = tk.Tk() #创建一个标签类, [justify]:对齐方式textLabel = tk.Label(root,text=&q ...
- MySQL (八)-- 事务、变量、触发器
1 事务 需求:有一张银行账户表,A用户给B用户转账,A账户先减少,B账户增加,但是A操作完之后断电了. 解决方案:A减少钱,但是不要立即修改数据表,B收到钱之后,同时修改数据表. 事务:一系列要发生 ...
- 201521123082 《Java程序设计》第1周学习总结
201521123082 <Java程序设计>第1周学习总结 标签(空格分隔): Java 1. 本周学习总结 0.初步了解Java语言及其发展历史和过程,同时也初步了解了Java具有跨平 ...