在asp.net web api 2 (ioc autofac) 使用 Serilog 记录日志

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
日志服务 SLS,月写入数据量 50GB 1个月
简介: Serilog是.net里面非常不错的记录日志的库,另外一个我认为比较好的Log库是NLog。 在我个人的asp.net web api 2 基础框架(Github地址)里,我原来使用的是NLog,但是由于好奇心,我决定使用Serilog代替Nlog。

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项目也是如此,但是在控制台应用却没有问题,今天晚些时候我将继续研究并解决这个问题。

下面是我的关于ASP.NET Core Web API相关技术的公众号--草根专栏:

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
42 4
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
101 3
|
12天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
在Web开发中,前后端的高效交互是提升用户体验的关键。本文通过一个基于Flask框架的博客系统实战案例,详细介绍了如何使用AJAX和Fetch API实现不刷新页面查看评论的功能。从后端路由设置到前端请求处理,全面展示了这两种技术的应用技巧,帮助Python Web开发者提升项目质量和开发效率。
27 1
|
18天前
|
JSON API 数据格式
如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架
本文介绍了如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架,适合小型项目和微服务。文章从环境准备、创建基本Flask应用、定义资源和路由、请求和响应处理、错误处理等方面进行了详细说明,并提供了示例代码。通过这些步骤,读者可以快速上手构建自己的RESTful API。
25 2
|
28天前
|
监控 负载均衡 API
Web、RESTful API 在微服务中有哪些作用?
在微服务架构中,Web 和 RESTful API 扮演着至关重要的角色。它们帮助实现服务之间的通信、数据交换和系统的可扩展性。
47 2
|
1月前
|
人工智能 搜索推荐 API
用于企业AI搜索的Bocha Web Search API,给LLM提供联网搜索能力和长文本上下文
博查Web Search API是由博查提供的企业级互联网网页搜索API接口,允许开发者通过编程访问博查搜索引擎的搜索结果和相关信息,实现在应用程序或网站中集成搜索功能。该API支持近亿级网页内容搜索,适用于各类AI应用、RAG应用和AI Agent智能体的开发,解决数据安全、价格高昂和内容合规等问题。通过注册博查开发者账户、获取API KEY并调用API,开发者可以轻松集成搜索功能。
|
1月前
|
开发框架 .NET API
Windows Forms应用程序中集成一个ASP.NET API服务
Windows Forms应用程序中集成一个ASP.NET API服务
90 9
|
1月前
|
存储 开发框架 .NET
.NET 8 实现无实体库表 API 部署服务
【10月更文挑战第12天】在.NET 8中,可通过以下步骤实现无实体库表的API部署:首先安装.NET 8 SDK及开发工具,并选用轻量级Web API框架如ASP.NET Core;接着创建新项目并设计API,利用内存数据结构模拟数据存储;最后配置项目设置并进行测试与部署。此方法适用于小型项目或临时解决方案,但对于大规模应用仍需考虑持久化存储以确保数据可靠性与可扩展性。
|
1月前
|
前端开发 JavaScript API
惊呆了!学会AJAX与Fetch API,你的Python Web项目瞬间高大上!
在Web开发领域,AJAX与Fetch API是提升交互体验的关键技术。AJAX(Asynchronous JavaScript and XML)作为异步通信的先驱,通过XMLHttpRequest对象实现了局部页面更新,提升了应用流畅度。Fetch API则以更现代、简洁的方式处理HTTP请求,基于Promises提供了丰富的功能。当与Python Web框架(如Django、Flask)结合时,这两者能显著增强应用的响应速度和用户体验,使项目更加高效、高大上。
50 2
|
1月前
|
移动开发 前端开发 JavaScript
前端开发实战:利用Web Speech API之speechSynthesis实现文字转语音功能
前端开发实战:利用Web Speech API之speechSynthesis实现文字转语音功能
155 0