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

news/2024/5/19 22:43:38 标签: .netcore, c#

文章目录

  • 目的
  • 实现案例:
    • 一.首先我们新建一个WebApi项目
    • 二.配置 appsettings.json 文件,配置日志存放路径
    • 三.创建 Model 文件夹,创建AppConfig类和ErrorLog类
      • 1.在AppConfig类中编写一个GetConfigInfo方法获取配置文件中的值
      • 2.在ErrorLog类中,实现往日志文件中书写接口日志的操作
    • 四.创建Filter文件夹,创建ActionFilter实现行动过滤器,实现记录接口日志
    • 五.在 Program 中配置行动过滤器 ActionFilter
    • 六.创建一个接口,调试执行一下
  • 结果

目的

使用ActionFilter记录接口请求日志

实现案例:

一.首先我们新建一个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行动过滤器搭建成功,之后这个框架的接口请求就都会携带日志了


http://www.niftyadmin.cn/n/5411024.html

相关文章

「CISP习题精讲」CISP 第九章《计算环境安全》习题15道精讲

前言 本节只要对CISP考试中的15道习题进行讲解&#xff0c;都是熬夜敲得文字&#xff0c;所以可能有哪些敲错的地方没来得及校正。 习题部分 第一道 某社交网站的用户点击了该网站上的一个广告。该广告含有一个跨站脚本&#xff0c;会将他的浏览器定向到旅游网站&#xff0…

汽车中网上的logo不能改,需要到车管所备案

需要备案。 车辆改装需到车辆管理所办理登记。 机动车每年检验时&#xff0c;需要对外观进行检测。 中国在线的标志不能更改。 汽车格栅是汽车前部进气口附近相关部件的总称。 汽车的中网主要位于水箱、发动机、空调等设备的前面&#xff0c;控制进气和通风&#xff0c;防止行…

【CSS】CSS 总结 ① ( CSS 引入方式 | CSS 选择器 | 基础选择器 | 复合选择器 ) ★

文章目录 一、CSS 引入方式1、CSS 的 3 种引入方式 ( 内联 | 内嵌 | 外链 ) 二、CSS 选择器1、CSS 基础选择器2、CSS 复合选择器后代选择器子元素选择器交集选择器并集选择器链接伪类选择器 一、CSS 引入方式 CSS 引入方式相关博客 : 【CSS】CSS 层叠样式表 ① ( 简介 | CSS 引…

Java设计模式:建造者模式之经典与流式的三种实现(四)

本文将深入探讨Java中建造者模式的两种实现方式&#xff1a;经典建造者与流式建造者。建造者模式是一种创建型设计模式&#xff0c;它允许你构建复杂对象的步骤分解&#xff0c;使得对象的创建过程更加清晰和灵活。我们将通过示例代码详细解释这两种实现方式&#xff0c;并分析…

【Selenium】UI自动化|PO设计模式 关键字驱动

一、Page Object&#xff08;页面对象模型&#xff09; 是UI自动化测试项目开发实践的最佳设计模式之一&#xff0c;通过对界面元素和功能模块的封装减少冗余代码&#xff0c;同时在后期维护中&#xff0c;若元素定位或功能模块发生变化&#xff0c;只需要调整页面元素或功能模…

基于单片机的轴承售卖系统设计

目 录 摘 要 I Abstract II 引 言 3 1总体方案设计及选择 5 1.1设计方案与选择 5 1.2总体方案设计 5 1.2.1系统总体设计 5 2 硬件电路的设计 8 2.1电源电路 8 2.2 控制核心STC89C52单片机 8 2.3 时钟电路 8 2.4 复位电路 8 2.5 按键模块 9 2.6 NFR24L01无线传输模块 10 2.7 LC…

在vue3中使用el-tree-select做一个树形下拉选择器

el-tree-select是一个含有下拉菜单的树形选择器&#xff0c;结合了 el-tree 和 el-select 两个组件的功能。 因为包含了el-tree的功能&#xff0c;我们可以自定义tree的节点&#xff0c;创造出想要的组件 使用default插槽可以自定义节点内容&#xff0c;它的default插槽相当于…

React 第七章 Hooks

Hooks 基本介绍 Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 Hooks 的出现&#xff0c;首先能解决如下的一些问题&#xff1a; 告别令人疑惑的生命周期 例如下面的例子&#xff0c;相同的代码在不同的生命周期中存在…