.NetCore6.0实现ActionFilter过滤器记录接口请求日志

文章目录

目的

使用ActionFilter记录接口请求日志

实现案例:

https://gitee.com/hgcjd/WebApiFilter

一.首先我们新建一个WebApi项目

开发环境,我们去掉HTTPS配置

二.配置 appsettings.json 文件,配置日志存放路径

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AppConfig": {
    "DirectoryPath": "项目本地日志存放路径"
  },
  "AllowedHosts": "*"
}

三.创建 Model 文件夹,创建AppConfig类和ErrorLog类

1.在AppConfig类中编写一个GetConfigInfo方法获取配置文件中的值

/// <summary>
/// 获取.NetCore配置文件信息
/// </summary>
public class AppConfig
{
    public static string GetConfigInfo(string Key)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json");

        IConfigurationRoot configuration = builder.Build();
        string configStr = configuration.GetSection($"{Key}").Value;
        if (!string.IsNullOrWhiteSpace(configStr))
        {
            return configStr;
        }
        return null;
    }

}

2.在ErrorLog类中,实现往日志文件中书写接口日志的操作

public class ErrorLog
{
	//获取日志文件路径
    private static string DirectoryPath = AppConfig.GetConfigInfo("AppConfig:DirectoryPath");

    /// <summary>
    /// 写入操作日志到文件中
    /// </summary>
    /// <param name="moduleName">模块名字</param>
    /// <param name="message">错误文本信息</param>
    /// <param name="ex">异常</param>
    public static void Write(string message, Exception ex)
    {
       
        string directoryPath = $@"{DirectoryPath}{DateTime.Now.ToString("yyyyMMdd")}"; // 目标目录路径

        if (!Directory.Exists(directoryPath))
        {
            // 如果目录不存在,则新建文件夹
            Directory.CreateDirectory(directoryPath);
        }

        string filePath = directoryPath + $@"\{DateTime.Now.ToString("yyyyMMddHH")}.log"; // 目标文件路径

        if (!File.Exists(filePath))
        {
            // 如果文件不存在,则创建文件
            using (File.Create(filePath))
            {
                //Console.WriteLine("文件已创建");
            }
        }
        LogToFile(filePath, message);
    }

    /// <summary>
    /// 写入操作日志到文件中
    /// </summary>
    /// <param name="moduleName">模块名字</param>
    /// <param name="ex">异常</param>
    public static void Write(string moduleName, Exception ex)
    {
        Write(moduleName, moduleName, ex);
    }

    /// <summary>
    /// 写入过程数据或说明到文件中,以便跟踪
    /// </summary>
    /// <param name="moduleName">模块名字</param>
    /// <param name="ex">异常</param>
    public static void Write(string message)
    {
        Write(String.Empty, message, null);
    }
    
    /// <summary>
    /// 文本写入
    /// </summary>
    /// <param name="logMessage"></param>
    private static void LogToFile(string logFilePath, string logMessage)
    {
        using (StreamWriter sw = File.AppendText(logFilePath))
        {
            sw.WriteLine($"{logMessage}");
        }
    }
}

四.创建Filter文件夹,创建ActionFilter实现行动过滤器,实现记录接口日志

这里我们引入Newtonsoft.Json包

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Text.Json;
using Microsoft.Extensions.Primitives;
using WebApiTest.Model;
using System.Net.Http.Json;

namespace WebApiTest.Filter
{
    /// <summary>
    /// 方法过滤器
    /// </summary>
    public class ActionFilter : IActionFilter
    {
        /// <summary>
        /// 监控日志
        /// </summary>
        public static ILogger LoggerMonitor { get; set; }

        /// <summary>
        /// 错误日志
        /// </summary>
        public static ILogger LoggerError { get; set; }

        private Stopwatch _stopwatch;

        /// <summary>
        /// 创建请求日志文本
        /// </summary>
        /// <param name="method"></param>
        /// <param name="controllerName"></param>
        /// <param name="actionName"></param>
        /// <param name="actionArgs"></param>
        /// <returns></returns>
        private static string CreateRequestLogText(string method, string controllerName, string actionName, string requestHead, string requestBody)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 请求{method}/{controllerName}/{actionName}接口,请求Head:{requestHead}\n");
            sb.Append($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 请求{method}/{controllerName}/{actionName}接口,请求Body:{requestBody}\n");
            return sb.ToString();
        }	

        /// <summary>
        /// 创建响应日志文本
        /// </summary>
        /// <param name="method"></param>
        /// <param name="controllerName"></param>
        /// <param name="actionName"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        private static string CreateResponseLogText(string method, string controllerName, string actionName, object result)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 完成请求{method}/{controllerName}/{actionName}接口,返回结果:");
            if (result != null)
            {
                sb.Append($"{JsonConvert.SerializeObject(result)}");
            }
            else
            {
                sb.Append($"无");
            }
            return sb.ToString();
        }

        /// <summary>
        /// 方法执行前,记录接口请求参数
        /// </summary>
        /// <param name="context"></param>
        /// <exception cref="NotImplementedException"></exception>
        public async void OnActionExecuting(ActionExecutingContext context)
        {
            ErrorLog.Write("==================================================================================================================================");
            _stopwatch = new Stopwatch();
            _stopwatch.Start();
            //throw new NotImplementedException();
            if (LoggerMonitor != null)
            {
                //记录请求参数日志
                ControllerActionDescriptor desc = context.ActionDescriptor as ControllerActionDescriptor;
                if (desc != null)
                {
                    Dictionary<string, object> headers = new Dictionary<string, object>();
                    var requestHeaders = context.HttpContext.Request.Headers;

                    // 访问请求中的 header 信息
                    foreach (var header in requestHeaders)
                    {
                        headers.Add(header.Key, header.Value);
                    }
                    var requestHead = JsonConvert.SerializeObject(headers);

                    Dictionary<string, object> bodys = new Dictionary<string, object>();
                    var actionArguments = context.ActionArguments;
                    // 访问请求中的参数
                    foreach (var argument in actionArguments)
                    {
                        //dic.Add(argument.Key, argument.Value);
                        var parameter = JsonConvert.DeserializeObject<Dictionary<string, object>>(argument.Value.ToString());
                        foreach (var item in parameter)
                        {
                            bodys.Add(item.Key, item.Value);
                        }
                    }
                    var requestBody = JsonConvert.SerializeObject(bodys);

                    var logText = CreateRequestLogText(context.HttpContext.Request.Method, desc.ControllerName, desc.ActionName, requestHead, requestBody);
                    LoggerMonitor.LogDebug(logText);
                    ErrorLog.Write(logText);
                }
            }


        }

		//方法执行后,记录接口请求结果
        public void OnActionExecuted(ActionExecutedContext context)
        {
            //throw new NotImplementedException();
            _stopwatch.Stop();
            long elaspsedMillisedconds = _stopwatch.ElapsedMilliseconds;
            string msg = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 接口执行时间:{elaspsedMillisedconds}毫秒";
            //ErrorLog.Write(msg);

            if (context.Exception != null)
            {
                // 记录异常日志
                if (LoggerError != null)
                {
                    LoggerError.LogError(context.Exception, context.Exception.Message);

                    ErrorLog.Write($@"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 接口异常:{JsonConvert.SerializeObject(context.Exception)}");
                    ErrorLog.Write($@"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff")} 异常提示信息:{JsonConvert.SerializeObject(context.Exception.Message)}");
                }
            }

            if (LoggerMonitor != null)
            {
                // 记录请求结果日志
                ControllerActionDescriptor desc = context.ActionDescriptor as ControllerActionDescriptor;
                if (desc != null)
                {
                    ObjectResult rst = context.Result as ObjectResult;
                    object rstValue = rst != null ? rst.Value : null;
                    var logText = CreateResponseLogText(
                        context.HttpContext.Request.Method,
                        desc.ControllerName,
                        desc.ActionName,
                        rstValue);
                    LoggerMonitor.LogDebug(logText);
                    ErrorLog.Write(logText);
                }
            }
            ErrorLog.Write(msg);
            ErrorLog.Write("==================================================================================================================================");
        }
    }
}

五.在 Program 中配置行动过滤器 ActionFilter

using Microsoft.AspNetCore.Mvc.Filters;
using WebApiTest.Filter;

var builder = WebApplication.CreateBuilder(args);

var configuration = builder.Configuration;
// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();


#region 接口行动过滤器
// Add services to the container.
builder.Services.AddSingleton<IActionFilter>(new ActionFilter()); // 初始化 LoggerMonitor
builder.Services.AddSingleton<IActionFilter>(new ActionFilter()); // 初始化 LoggerError
builder.Services.AddScoped<ActionFilter>(); // 注册 ActionFilter

builder.Services.AddControllers(options => {
    options.Filters.Add(new ActionFilter());
});

var serviceProvider = builder.Services.BuildServiceProvider();
ActionFilter.LoggerError = serviceProvider.GetRequiredService<ILogger<ActionFilter>>();
ActionFilter.LoggerMonitor = serviceProvider.GetRequiredService<ILogger<ActionFilter>>();
#endregion

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.Run();

六.创建一个接口,调试执行一下

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace WebApiTest.Controllers
{
    [ApiController]
    [Route("Home")]
    public class HomeController : ControllerBase
    {
        [HttpGet]
        [Route("Index")]
        public string Index()
        {
            return JsonConvert.SerializeObject(new { code = 0,data=true,msg="成功"});
        }
    }
}

接口请求成功,接着我们查看appsettings.json中配置的路径文件中,日志的记录情况

结果

.NetCore6.0项目,ActionFilter行动过滤器搭建成功,之后这个框架的接口请求就都会携带日志了

相关推荐
初九之潜龙勿用17 分钟前
C#校验画布签名图片是否为空白
开发语言·ui·c#·.net
吾与谁归in2 小时前
【C#设计模式(13)——代理模式(Proxy Pattern)】
设计模式·c#·代理模式
吾与谁归in2 小时前
【C#设计模式(14)——责任链模式( Chain-of-responsibility Pattern)】
设计模式·c#·责任链模式
神仙别闹3 小时前
基于C#和Sql Server 2008实现的(WinForm)订单生成系统
开发语言·c#
向宇it12 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
九鼎科技-Leo13 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
Heaphaestus,RC14 小时前
【Unity3D】获取 GameObject 的完整层级结构
unity·c#
baivfhpwxf202314 小时前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
直裾14 小时前
Scala全文单词统计
开发语言·c#·scala
ZwaterZ16 小时前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue