.Net Core3.0 WebApi 十五:使用Serilog替换掉Log4j
.Net Core3.0 WebApi 目录
为什么使用Serilog
Serilog 是一个用于.NET应用程序的日志记录开源库,配置简单,接口干净,并可运行在最新的.NET平台上,与其他日志库不同, Serilog 是以功能强大的结构化事件数据为基础构建的, 支持将日志输出到控制台、文件、数据库和其它更多的方式,支持参数化日志模板,非常灵活。
之前我们项目使用的是Log4j来记录用户日志的,在开发的过程中,慢慢的发现Log4j好像并不能满足我们的需求,比如结构化,日志分析等,于是决定使用serilog来替换掉Log4j,在使用的过程中发现Serilog还是很强大的。
删除原有的Log4j
1.卸载log4j包
点击卸载
2.删除log4j文件夹
3.删除startup中关于log4net的代码
4.将log4j相关的代码删除
安装Serilog包
nuget安装以下几个包
配置Serilog
编辑Appsetting.json
"Serilog": { "MinimumLevel": { "Default": "Debug", //最小日志记录级别 "Override": { //系统日志最小记录级别 "Default": "Warning", "System": "Warning", "Microsoft": "Warning" } }, "WriteTo": [ { "Name": "Console" }//输出到控制台 ] },
program.cs将系统的logger替换为serilog
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>() .UseSerilog((context, logger) =>//注册Serilog { logger.ReadFrom.Configuration(context.Configuration); logger.Enrich.FromLogContext(); }); });
Test控制器注入logger,并修改LogTest方法。
private readonly ILogger<TestController> _logger;//using Microsoft.Extensions.Logging; public TestController(ILogger<TestController> logger) { _logger = logger; }
/// <summary> /// 测试日志 /// </summary> /// <returns></returns> [HttpGet] public IActionResult LogTest() { //_logger.Error(typeof(TestController), "这是错误日志", new Exception("123")); //_logger.Debug(typeof(TestController), "这是bug日志"); //throw new System.IO.IOException(); _logger.LogInformation("info 日志"); _logger.LogDebug("debug 日志"); _logger.LogError(new System.IO.IOException(), "io 错误"); return Ok(); }
运行调用接口,可以看到日志输出到控制台了:
配置Serilog输出到文件
appsetting.json增加配置
"WriteTo": [ { "Name": "Console" }, //输出到控制台 { "Name": "Async", //Serilog.Sinks.Async "Args": { "configure": [ { "Name": "File", //输出文件 "Args": { "path": "log/log.txt", "outputTemplate": "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}", "rollingInterval": "3" //按天记录 } } ] } } ]
启动项目测试,就有了日志文件:
这里边,乱码的地方,其实是 : ,至于为什么乱码,等到找到解决方案再更新。有知道的小伙伴也可以评论下。
【补充】将配置中的:(中文状态)修改成英文下的:(英文状态)就不会出现乱码的问题了。
配置Serilog输出到数据库
{"Name": "Async", //Serilog.Sinks.Async "Args": { "configure": [ { "Name": "File", //输出文件 "Args": { "path": "log/log.txt", "outputTemplate": "{NewLine}Date:{Timestamp:yyyy-MM-dd HH:mm:ss.fff}{NewLine}LogLevel:{Level}{NewLine}Message:{Message}{NewLine}{Exception}", "rollingInterval": "3" //按天记录 } }, { "Name": "MSSqlServer", //输出到sqlserver "Args": { "connectionString": "数据库", "schemaName": "dbo", //数据库所有者,默认dbo "tableName": "Logs", // 记录日志的表名 "autoCreateSqlTable": true, // 是否自动创建表 "restrictedToMinimumLevel": "Information", // 记录日志的最小level "batchPostingLimit": 100, //单次批量处理中提交的最大日志数量 "period": "0.00:00:30", //进行批量提交的间隔 "columnOptionsSection": { "disableTriggers": true, "clusteredColumnstoreIndex": false, "primaryKeyColumnName": "Id", "addStandardColumns": [ "LogEvent" ], "removeStandardColumns": [ "MessageTemplate" ], "additionalColumns": [ //自定义列 { "ColumnName": "Ip", "DataType": "varchar", "DataLength": 20 }, { "ColumnName": "UserName", "DataType": "varchar", "DataLength": 30 }, { "ColumnName": "UserId", "DataType": "varchar", "DataLength": 50 }, { "ColumnName": "LogType", "DataType": "tinyint" }, { "ColumnName": "Parameter" }, { "ColumnName": "Result" } ], "id": { "nonClusteredIndex": true }, "properties": { "columnName": "Properties", "excludeAdditionalProperties": true, "dictionaryElementName": "dict", "itemElementName": "item", "omitDictionaryContainerElement": false, "omitSequenceContainerElement": false, "omitStructureContainerElement": false, "omitElementIfEmpty": true, "propertyElementName": "prop", "rootElementName": "root", "sequenceElementName": "seq", "structureElementName": "struct", "usePropertyKeyAsElementName": false }, "timeStamp": { "columnName": "Timestamp", "convertToUtc": true }, "logEvent": { "excludeAdditionalProperties": true, "excludeStandardColumns": true }, "message": { "columnName": "message" }, "exception": { "columnName": "exception" } } } } ] } }
Test控制器修改Aoptest方法,添加以下语句
_logger.LogInformation("ip:{IP},username{UserName},userid:{UserId}","127.0.0.1","admin","1");
启动项目,测试接口,数据库已经插入数据。
配置Serilog输出到Seq
Seq组件,通过网页UI的形式将日志展现出来,内容更加多样化,并赋予了更多功能日志搜索。
首先,安装Seq组件,Seq下载地址:https://getseq.net/Download
本地开发情形下是免费使用的,如果需要在生产环境中使用,需要商业许可(你懂的,money).在目前的版本中,4.2是只能够在windows下跑,也就是说我们如果是在windows下开发,在测试时可以借助这个方便的查看日志信息,按照给定的安装步骤完成安装。
安装完成之后,浏览器输出localhost:5341,会看到以下页面
appsetting.json配置输出到seq
{ "Name": "Seq", //输出到seq "Args": { "serverUrl": "http://127.0.0.1:5341" } },
启动项目,测试接口,可以通过页面看到数据。
全局使用Serilog记录日志
在CustomExceptionMiddleware.cs直接这样
namespace WebApi.Core.Api.Middleware { public class CustomExceptionMiddleware { private readonly RequestDelegate _next; private readonly ILogger<CustomExceptionMiddleware> _logger; public CustomExceptionMiddleware(RequestDelegate next, ILogger<CustomExceptionMiddleware> logger) { _next = next; _logger = logger; } public async Task Invoke(HttpContext httpContext) { try { await _next(httpContext); } catch (Exception ex) { _logger.LogError(ex.Message, ex); // 日志记录 await HandleExceptionAsync(httpContext, ex.Message); } finally
GlobalExceptionsFilter中也一样
namespace WebApi.Core.Api.Filters { public class GlobalExceptionsFilter : IExceptionFilter { private readonly IHostEnvironment _env; private readonly ILogger<GlobalExceptionsFilter> _logger; public GlobalExceptionsFilter(IHostEnvironment env, ILogger<GlobalExceptionsFilter> logger) { _env = env; _logger = logger; } public void OnException(ExceptionContext context) { var json = new JsonErrorResponse(); json.Message = context.Exception.Message;//错误信息 if (_env.IsDevelopment()) { json.DevelopmentMessage = context.Exception.StackTrace;//堆栈信息 } context.Result = new InternalServerErrorObjectResult(json); _logger.LogError(context.Exception, context.Exception.Message); //采用log4net 进行错误日志记录 //_loggerHelper.Error(json.Message, "出现未知异常", context.Exception); }
如果想在服务层或仓储层用的话,安装这个包 Microsoft.Extensions.Logging.Abstractions,引用下 using Microsoft.Extensions.Logging;
然后直接代码里用就行了
[Caching(AbsoluteExpiration = 1)] public async Task<UserResponse> GetUserDetails(int id) { //var userinfo = await userDal.QueryByID(id); _logger.LogError("错误日志!"); var userinfo = new UserNew { UserId = id, UserName = "bingle", Age = 18 }; if (userinfo != null) { //UserResponse model = new UserResponse() //{ // UserId = userinfo.UserId, // UserName = userinfo.UserName, // Address = "xx市xx区xx小区", // Age = userinfo.Age, // Birthday = "1996-06-26", // Phone = "13888888888" //}; UserResponse model = iMapper.Map<UserResponse>(userinfo); model.Address = "xx市xx区xx小区"; model.Birthday = "1996-06-26"; model.Phone = "13888888888"; return model; } else { return null; } }
然后调试接口,这样就有了日志: