前言:走進.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); } } }