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;
}
}
}
代码说明:
- 继承:BaseService, IAuditLogService;
- 实现接口:调用仓储对象完成日志记录;这边添加了 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);
}
}
代码说明:
- 继承 ActionFilterAttribute,并重载方法 OnActionExecuting、OnActionExecuted,这两个分别为:方法执行前、方法执行后;
- _executionBefore 记录执行前的时间,_executionAfter 记录执行后的时间,并由此计算出接口的执行时间;
- 在 OnActionExecuted,判断 context.Exception 是否为 null,由此判断接口是否发生异常;
- 在 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;
}
}
代码说明:
- 根据 Request 对象,获取接口 Url、方法、参数、浏览器信息等等;
- 根据 Identity,获取当前登录信息。
3.2 开启 Body 重用
在 AuditLogHelper 有使用了 request.Body,request.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 请求日志,效果如下:
标签:string,28,auditLog,API,context,日志,添加,public,simpread From: https://www.cnblogs.com/zhuoss/p/18171327