首页 > 其他分享 >simpread-课程 28:API 接口请求日志【后端】

simpread-课程 28:API 接口请求日志【后端】

时间:2024-05-03 16:33:47浏览次数:18  
标签:string 28 auditLog API context 日志 添加 public simpread

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 请求日志,效果如下:

标签:string,28,auditLog,API,context,日志,添加,public,simpread
From: https://www.cnblogs.com/zhuoss/p/18171327

相关文章

  • 283.移动零
    给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例1:输入:nums=[0,1,0,3,12]输出:[1,3,12,0,0]示例2:输入:nums=[0]输出:[0] 提示:1<=nums.length<......
  • #22 2024.4.28
    817.loj4143「CCO2019」Sirtet818.loj3364「IOI2020」植物比较819.loj3367「IOI2020」装饼干820.loj3389「NOIP2020」微信步数?计算第\(kn+i\)步还没死的人的个数。每一维是个一次函数,卷起来是个\(k\)次函数,前缀和是\(k+1\)次函数。做完了???821.uoj569【I......
  • 个人网页-测试程序-网页成功与api交互但未显示好的图片
    python:fromflaskimportFlask,render_template,request,jsonifyimportrequestsimportbase64importosfromPILimportImageimportioimportlogging#ConfigureFlaskapplicationapp=Flask(__name__,template_folder='../templates')app.c......
  • mit6.828 - lab1笔记
    安装环境编译qemu1.PC启动打开两个窗口,在第一个窗口中makeqemu-gdb,会启动内核,但在执行第一个指令之前停下;在第二个窗口中makegdb,实时观察第一个窗口中的执行过程。从这里可以观察到:IBMPC在物理地址0x000ffff0开始执行,位于为ROMBIOS保留的64KB区域的最顶部。......
  • simpread-课程 27:系统日志之 ILogger 与 Log4Net
    一、ILogger介绍1.1简介ILogger是.NET框架提供的一个接口,用于统一不同日志库的调用方式。ILogger本身并不提供具体的日志记录功能,而是通过实现它的类来执行这些操作。所以我们可以借助第三方日志库或自定义实现ILoggerProvider,将日志消息写入到文件、控制台或数据库中,也......
  • WPF CollectionViewSource ICollectionViewLiveShaping IsLiveSorting
    <Windowx:Class="WpfApp82.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.......
  • webapi创建和调用WebService
    首先需要引入soapcore包这个包提供了所需的类和soap终结点中间件。引入这个这个包之后,我们需要定义提供的服务。这里我写了一个用于查询省份面积的服务。省份信息服务///<summary>///省份信息服务接口///</summary>[ServiceContract]publicinterfaceIProvinceInfo......
  • 模拟微任务 判断是否有对应的api
    if(typeofPromise!=='undefined'&&isNative(Promise)){}functionrunMicrotask(func){if(typeofPromise==='function'){Promise.resolve().then(func)return}if(typeofMutationObserver==='functi......
  • CF628F Bear and Fair Set
    传送门网络流好题。先将所有限制按\(u_i\)排序,同时令\(u_0=0,t_0=0\)和\(u_{q+1}=b,t_{q+1}=n\)。(下面就把\(q\leftarrowq+1\)了)这些限制会把\(1\simb\)分成\(q\)段。先检查一遍,如果出现\(u_i\)更大反而\(t_i\)更小,unfair;如果出现一个段内数的个数爆了,unfair......
  • Web Audio API
    WebAudioAPI:控制Web上的音频提供了一个功能强大的通用系统,允许开发人员选择音频源,为音频添加效果,创建音频可视化,应用空间效果(如平移)等等(oscillator)振荡器播放声音<buttontype="button"class="btn-play">play</button><script>//WebAudioAPI//创建一个Au......