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