.Net Core5.0使用中间件记录请求日志的注意事项
前言:走进.net core5.0以后,我们会接触到中间件,中间件类似于程序的通道的一部分,也是进出程序所必须进过的一个环节。那么我们就可以利用中间件去记录程序所有相关的操作记录。
1-Startup.cs配置中间件(注意中间件的放置位置,位置不同会影响日志数据的读取,也可能读不到想要的数据或抛出异常):
我这里将中间件放到了路由中间件的下方(app.UseRouting())
//启用日志中间件 app.UseMiddleware<LogMiddleware>();
2-创建日志分类特性:
/// <summary> /// 日志特性类 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class LogAttribute : Attribute { /// <summary> /// 日志类型 /// </summary> public string LogType { get; set; } /// <summary> /// 指定创建人 /// </summary> public string Creator { get; set; } /// <summary> /// 日志构造器 /// </summary> /// <param name="logType">日志类型</param> public LogAttribute(string logType) : this(logType, null) { } /// <summary> /// 日志构造器 /// </summary> /// <param name="logType">日志类型</param> public LogAttribute(string logType, string creator) { this.LogType = logType; this.Creator = creator; } }
3-为方法添加合适的日志分类:
[HttpPost] [Log(CrmLogType.ACTIVITY_COUPON_ADD)] public ActionResult AddCouponSave(VipCoupon vipCoupon, [FromServices] VipCouponRepository couponRepository) { }
4-中间件核心代码(记录了request和response的结果响应,下面的代码并不完整,使用请调整):
/// <summary> /// Log日志中间件 /// </summary> public class LogMiddleware { private readonly RequestDelegate _next; private SystemOperationLogRepository _systemOperationLogRepository; private ILogger<LogMiddleware> _logger; public LogMiddleware(RequestDelegate next, SystemOperationLogRepository systemOperationLogRepository, ILogger<LogMiddleware> logger) { _next = next; this._systemOperationLogRepository = systemOperationLogRepository; this._logger = logger; } public async Task Invoke(HttpContext httpContext) { httpContext.Request.EnableBuffering(); //这里会记录当前平台下所有方法上比较Log特性的操作日志 Endpoint endpoint = httpContext.GetEndpoint(); LogAttribute actionLogAttribute = endpoint?.Metadata.GetMetadata<LogAttribute>(); if (actionLogAttribute != null) { HttpRequest request = httpContext.Request; HttpResponse response = httpContext.Response; var originBody = response.Body; using (var memoryStream = new MemoryStream()) { response.Body = memoryStream; await _next.Invoke(httpContext); StreamReader requestStreamReader = null; StreamReader responseStreamReader = null; try { #region 获取request请求内容 string requestUrl = $"{request.Path.Value}{request.QueryString.Value}"; string requestContent = null; if (request.HasFormContentType) { IFormCollection fromColl = request.Form; requestContent += "{"; foreach (var item in fromColl) { requestContent += $"{item.Key}:{item.Value},"; } requestContent += requestContent.TrimEnd(',') + "}"; } else if (request.HasJsonContentType()) { request.Body.Seek(0, SeekOrigin.Begin); requestStreamReader = new StreamReader(request.Body); requestContent = await requestStreamReader.ReadToEndAsync(); } #endregion #region 获取response结果 JsonModel responseJsonModel = null; memoryStream.Seek(0, SeekOrigin.Begin); responseStreamReader = new StreamReader(memoryStream); string responseBody = await responseStreamReader.ReadToEndAsync(); try { responseJsonModel = JsonHelper.ConvertStrToJson<JsonModel>(responseBody); } catch (Exception) { } #endregion #region 打印监控日志 ClaimsPrincipal user = httpContext.User; string userName = actionLogAttribute.Creator; if (userName.IsNullOrEmpty()) { userName = user.FindFirst(ClaimTypes.Name)?.Value ?? ""; } string logType = actionLogAttribute.LogType; string logStatus = (responseJsonModel?.Success ?? false) ? "成功" : "失败"; string logContent = $"操作{logStatus}!操作内容:{requestContent}"; this._systemOperationLogRepository.AddSystemOperationLog(logType, logContent, requestUrl, userName); #endregion } catch (Exception e) { this._logger.LogWarning(e, $"日志中间件出现异常,请及时处理:{e.Message}"); } finally { #region 还原response流内容 && 释放资源 memoryStream.Seek(0, SeekOrigin.Begin); await memoryStream.CopyToAsync(originBody); response.Body = originBody; response.Body.Seek(0, SeekOrigin.Begin); if (responseStreamReader != null) { responseStreamReader.Close(); responseStreamReader.Dispose(); } if (requestStreamReader != null) { requestStreamReader.Close(); requestStreamReader.Dispose(); } request.Body.Close(); request.Body.Dispose(); #endregion } } } else { await _next.Invoke(httpContext); } } }
*感谢您的阅读。喜欢的、有用的就请大哥大嫂们高抬贵手“推荐一下”吧!你的精神 支持是博主强大的写作动力。欢迎转载!
*博主的文章是自己平时开发总结的经验,由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。
*我的博客: http://www.cnblogs.com/lxhbky/
*博主的文章是自己平时开发总结的经验,由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。
*我的博客: http://www.cnblogs.com/lxhbky/