.Net Core5.0使用中间件记录请求日志的注意事项

发布时间 2023-09-01 10:47:04作者: 大西瓜3721

前言:走进.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);
            }
        }
    }
复制代码