怪物奇妙物语

宇宙无敌超级美少男的怪物奇妙物语

首页 新随笔 联系 管理
  819 随笔 :: 0 文章 :: 2 评论 :: 16万 阅读

1、添加审核日志实体

1.1 实体定义

在项目 Electric.Entity,添加文件夹:AuditLogs,并添加类:EleAuditLog

EleAuditLog 完整代码如下:

namespace Electric.Entity.AuditLogs;
/// <summary>
/// 审核日志
/// </summary>
[Index(nameof(AuditLogType))]
public class EleAuditLog : EleEntity
{
/// <summary>
/// API接口地址
/// </summary>
[MaxLength(256)]
[Comment("API接口地址")]
public string ApiUrl { get; set; } = string.Empty;
/// <summary>
/// 接口的方法
/// </summary>
[MaxLength(256)]
[Comment("接口的方法")]
public string Method { get; set; } = string.Empty;
/// <summary>
/// 参数
/// </summary>
[MaxLength(1024)]
[Comment("参数")]
public string Parameters { get; set; } = string.Empty;
/// <summary>
/// 返回结果
/// </summary>
[Comment("返回结果")]
public string ReturnValue { get; set; } = string.Empty;
/// <summary>
/// 执行时间
/// </summary>
[Comment("执行时间")]
public int ExecutionDuration { get; set; }
/// <summary>
/// 客户端IP
/// </summary>
[MaxLength(64)]
[Comment("客户端IP")]
public string ClientIpAddress { get; set; } = string.Empty;
/// <summary>
/// 浏览器信息
/// </summary>
[MaxLength(512)]
[Comment("浏览器信息")]
public string BrowserInfo { get; set; } = string.Empty;
/// <summary>
/// 日志类型
/// </summary>
[Comment("日志类型 0:正常日志记录,99:异常日志")]
public AuditLogType AuditLogType { get; set; }
/// <summary>
/// 异常信息
/// </summary>
[MaxLength(1024)]
[Comment("异常信息")]
public string ExceptionMessage { get; set; } = string.Empty;
/// <summary>
/// 详细异常
/// </summary>
[MaxLength(2000)]
[Comment("详细异常")]
public string Exception { get; set; } = string.Empty;
}
/// <summary>
/// 审核日志类型
/// </summary>
public enum AuditLogType
{
/// <summary>
/// 正常日志记录
/// </summary>
Info,
/// <summary>
/// 异常日志
/// </summary>
Exception = 99
}

1.2 添加至上下文

在项目 Electric.EntityFrameworkCore,添加至 ApplicationDbContext

public DbSet<EleAuditLog> EleAuditLog { get; set; }

1.3 添加数据库迁移

打开:程序包管理器控制台,操作步骤:菜单栏找到:工具 =》NuGet 包管理器 =》程序包管理器控制台。

默认项目:选择 Electric.DbMigrator,并把 Electric.DbMigrator 设置为默认启动项目,输入:Add-Migration InitDB,并回车。其中:Add-Migration 为迁移命令,InitDB:这个是迁移的名称,可以根据自己自定义。

程序包管理器控制台,输入命令:Update-Database,并回车。

在数据库就可以看到对应的表:

2、添加审核日志服务

2.1 添加接口

在项目 Electric.Service,添加文件夹:AuditLogs,并添加接口:IAuditLogService

IAuditLogService 完整代码:

namespace Electric.Service.AuditLogs;
public interface IAuditLogService
{
/// <summary>
/// 插入审核日志
/// </summary>
/// <param name="eleAuditLog"></param>
/// <returns></returns>
EleAuditLog Add(EleAuditLog eleAuditLog);
}

接口说明:

定义添加日志方法,用于记录日志。

2.2 添加实现类

在项目 Electric.Service,文件夹 AuditLogs 下添加 AuditLogService。

AuditLogService 完整代码如下:

namespace Electric.Service.AuditLogs;
/// <summary>
/// 审核日志
/// </summary>
public class AuditLogService : BaseService, IAuditLogService
{
private readonly IBaseRepository<EleAuditLog> _auditLogRepository;
private readonly ILogger<AuditLogService> _logger;
public AuditLogService(IBaseRepository<EleAuditLog> auditLogRepository, ILogger<AuditLogService> logger)
{
_auditLogRepository = auditLogRepository;
_logger = logger;
}
/// <summary>
/// 插入审核日志
/// </summary>
/// <param name="eleAuditLog"></param>
/// <returns></returns>
public EleAuditLog Add(EleAuditLog eleAuditLog)
{
try
{
return _auditLogRepository.Add(eleAuditLog);
}
catch (Exception ex)
{
_logger.LogError(ex, "保存日志至数据库异常,接口:{0}\r\nMethod:{1}\r\n参数:{2}\r\nIP:{3}\r\n花费时长:{4}",
eleAuditLog.ApiUrl, eleAuditLog.Method, eleAuditLog.Parameters, eleAuditLog.ClientIpAddress, eleAuditLog.ExecutionDuration);
return eleAuditLog;
}
}
}

代码说明:

  1. 继承:BaseService, IAuditLogService;
  2. 实现接口:调用仓储对象完成日志记录;这边添加了 try...catch,避免记录日志发生异常影响正常业务的功能。

3、API 接口方法过滤器

3.1 添加方法过滤器

在项目 Electric.API,添加文件夹 AuditLog,并添加类 EletricActionFilterAttribute

EletricActionFilterAttribute 完整代码如下:

/// <summary>
/// 方法过滤器
/// </summary>
public class EletricActionFilterAttribute : ActionFilterAttribute
{
private EleAuditLog _auditLog;
private readonly IAuditLogService _auditLogService;
private readonly UserManager<EleUser> _userManager;
private readonly ILogger<ElectricExceptionFilterAttribute> _logger;
/// <summary>
/// 执行前的时间
/// </summary>
private DateTime _executionBefore;
/// <summary>
/// 执行后的世界
/// </summary>
private DateTime _executionAfter;
/// <summary>
/// 依赖注入日志对象
/// </summary>
/// <param name="auditLogService"></param>
/// <param name="userManager"></param>
public EletricActionFilterAttribute(IAuditLogService auditLogService, UserManager<EleUser> userManager, ILogger<ElectricExceptionFilterAttribute> logger)
{
_auditLog = new EleAuditLog();
_auditLogService = auditLogService;
_userManager = userManager;
_logger = logger;
}
/// <summary>
/// 方法执行前
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuting(ActionExecutingContext context)
{
_executionBefore = DateTime.Now;
_auditLog = AuditLogHelper.GenerateRequestAuditLog(context.HttpContext, _userManager);
}
/// <summary>
/// 方法执行完毕
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuted(ActionExecutedContext context)
{
_executionAfter = DateTime.Now;
_auditLog.ExecutionDuration = (int)(_executionAfter - _executionBefore).TotalMilliseconds;
_auditLog.ReturnValue = context.Result == null ? string.Empty : JsonConvert.SerializeObject(context.Result);
//异常信息
if (context.Exception != null)
{
_auditLog.ExceptionMessage = context.Exception.Message;
if (_auditLog.ExceptionMessage.Length > 1024)
{
_auditLog.ExceptionMessage = _auditLog.ExceptionMessage.Substring(0, 1024);
}
_auditLog.Exception = context.Exception.ToString();
if (_auditLog.Exception.Length > 2000)
{
_auditLog.Exception = _auditLog.Exception.Substring(0, 2000);
}
_auditLog.AuditLogType = AuditLogType.Exception;
//记录文件异常日志
_logger.LogError(context.Exception, "接口:{0}\r\nMethod:{1}\r\n参数:{2}\r\nIP:{3}\r\n花费时长:{4}",
_auditLog.ApiUrl, _auditLog.Method, _auditLog.Parameters, _auditLog.ClientIpAddress, _auditLog.ExecutionDuration);
}
_auditLogService.Add(_auditLog);
}
}

代码说明:

  1. 继承 ActionFilterAttribute,并重载方法 OnActionExecuting、OnActionExecuted,这两个分别为:方法执行前、方法执行后;
  2. _executionBefore 记录执行前的时间,_executionAfter 记录执行后的时间,并由此计算出接口的执行时间;
  3. OnActionExecuted,判断 context.Exception 是否为 null,由此判断接口是否发生异常;
  4. OnActionExecuting,通过调用 AuditLogHelper.GenerateRequestAuditLog,获取接口的请求参数;AuditLogHelper 是我们自定义封装的,便于代码复用。

AuditLogHelper 完整代码如下:

namespace Electric.API.AuditLog;
/// <summary>
/// 审核日志帮助类
/// </summary>
public class AuditLogHelper
{
/// <summary>
/// 请求日志
/// </summary>
/// <param name="httpContext"></param>
/// <param name="userManager"></param>
/// <returns></returns>
public static EleAuditLog GenerateRequestAuditLog(HttpContext httpContext, UserManager<EleUser> userManager)
{
//审核日志
EleAuditLog auditLog = new EleAuditLog();
var request = httpContext.Request;
//当前用户
var userName = httpContext.User.Identity.Name;
if (!string.IsNullOrEmpty(userName))
{
var user = userManager.FindByNameAsync(userName).Result;
auditLog.CreatorId = user.Id;
}
auditLog.ApiUrl = request.Path;
auditLog.Method = request.Method;
auditLog.AuditLogType = AuditLogType.Info;
//ip
var remoteIpAddress = httpContext.Connection.RemoteIpAddress;
auditLog.ClientIpAddress = remoteIpAddress == null ? string.Empty : remoteIpAddress.MapToIPv4().ToString();
auditLog.BrowserInfo = request.Headers.ContainsKey("User-Agent") ? request.Headers["User-Agent"] : string.Empty;
if (request.Method == "GET")
{
auditLog.Parameters = request.QueryString.Value ?? string.Empty;
}
else
{
using (StreamReader reader = new StreamReader(request.Body))
{
request.Body.Position = 0;
auditLog.Parameters = reader.ReadToEndAsync().Result;
request.Body.Position = 0;
}
}
return auditLog;
}
}

代码说明:

  1. 根据 Request 对象,获取接口 Url、方法、参数、浏览器信息等等;
  2. 根据 Identity,获取当前登录信息。

3.2 开启 Body 重用

在 AuditLogHelper 有使用了 request.Bodyrequest.Body 默认是不允许重复读取的。

所以,我们要开启允许 Body 重用。

Program.cs,添加如下代码:

添加的代码:

//允许body重用
app.Use(next => context =>
{
context.Request.EnableBuffering();
return next(context);
});

开启后,我们就可以重复使用。

3.3 开启过滤器注入

开启全局过滤器注入,默认记录所有 API 请求日志。

在项目 Electric.API 中的 MVCExtension.cs 添加如下代码:

添加的代码:

configure.Filters.Add<EletricActionFilterAttribute>();
namespace Electric.API.Extensions;
/// <summary>
/// MVC相关扩展
/// </summary>
public static class MVCExtension
{
/// <summary>
/// 控制器自定义返回格式
/// </summary>
/// <param name="builder"></param>
public static void AddElectricControllers(this WebApplicationBuilder builder)
{
builder.Services.AddControllers(configure =>
{
configure.Filters.Add<UnitOfWorkFilterAttribute>();
configure.Filters.Add<ElectricExceptionFilterAttribute>();
configure.Filters.Add<EletricActionFilterAttribute>();
})
.AddNewtonsoftJson(option =>
{
option.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
});
}
}

3.4 权限过滤日志

在 Asp.net Core 优先执行 AuthorizationFilter 的,过滤器 ActionFilterAttribute 是在后面执行的,所以我们要在权限过滤器这边,添加因为无权限返回的请求日志

添加主要代码如下:

添加的代码如下:

//审核日志
var _auditLogService = context.HttpContext.RequestServices.GetService(typeof(IAuditLogService)) as IAuditLogService;
var _userManager = context.HttpContext.RequestServices.GetService(typeof(UserManager<EleUser>)) as UserManager<EleUser>;
if (hasPermission == null)
{
var result = new ForbidResult();
context.Result = result;
EleAuditLog auditLog = AuditLogHelper.GenerateRequestAuditLog(context.HttpContext, _userManager);
auditLog.ReturnValue = JsonConvert.SerializeObject(result);
_auditLogService.Add(auditLog);
}

3.5 日志效果

启动项目,操作系统功能,就能在数据库看到对应的 API 请求日志,效果如下:

posted on   超级无敌美少男战士  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示