当前位置: 首页 > news >正文

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

文章目录

  • 目的
  • 实现案例:
    • 一.首先我们新建一个WebApi项目
    • 二.配置 appsettings.json 文件,配置日志存放路径
    • 三.创建 Model 文件夹,创建AppConfig类和ErrorLog类
      • 1.在AppConfig类中编写一个GetConfigInfo方法获取配置文件中的值
      • 2.在ErrorLog类中,实现往日志文件中书写接口日志的操作
    • 四.创建Filter文件夹,创建ActionFilter实现行动过滤器,实现记录接口日志
    • 五.在 Program 中配置行动过滤器 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>(); // 注册 ActionFilterbuilder.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>>();
#endregionvar 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行动过滤器搭建成功,之后这个框架的接口请求就都会携带日志了

相关文章:

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

文章目录 目的实现案例&#xff1a;一.首先我们新建一个WebApi项目二.配置 appsettings.json 文件&#xff0c;配置日志存放路径三.创建 Model 文件夹&#xff0c;创建AppConfig类和ErrorLog类1.在AppConfig类中编写一个GetConfigInfo方法获取配置文件中的值2.在ErrorLog类中&a…...

代码详解:2024美团春招实习笔试第一场0309,是难还是简单?

前言: 1.第一题&#xff08;模拟&#xff09; 2.第二题&#xff08;模拟&#xff09; 3.第三题&#xff08;二维前缀和&#xff09; 4.第四题的思维&#xff08;双指针&#xff09; 5.第五题难度比较大&#xff08;并查集删边离散化&#xff09; 一.小美的MT MT 是美团的…...

平衡二叉树

前言 在关键字排列随机的情况下&#xff0c;二叉排序树的平均查找长度和 l o g n log n logn是等数量级的。在某些情况下&#xff0c;尚需在构成二叉排序树的过程中进行“平衡化”处理&#xff0c;使其成为平衡二叉树。 如果任何初始化序列构成的二叉排序树都是平衡二叉树&…...

脚本自动化 设置快捷方式并设置为管理员运行

自动化创建快捷方式并设置为始终以管理员权限运行&#xff0c;可以通过编写批处理脚本来实现。以下是一个创建.bat批处理文件快捷方式并设置为管理员运行的示例脚本&#xff1a; batch echo off set SCRIPT_PATH"C:\Scripts\myScript.bat" set SHORTCUT_PATH"%…...

TypeScript学习笔记(上):TypeScript的介绍、安装及常用类型

我对TypeScript的理解就是&#xff0c;TypeScript是增加了类型校验的JavaScript&#xff0c;能够把运行期错误提升至编译期 目录 TypeScript是什么&#xff1f; 安装编译 TS 的工具包 运行 TS 的步骤 TypeScript 常用类型 JS 已有类型 TS 新增类型 简单数据类型 数组类…...

Vue3学习记录(六)--- 组合式API之依赖注入和异步组件

一、依赖注入 1、简介 ​ 在前面的笔记中&#xff0c;我们学习过父组件向子组件传递数据时&#xff0c;需要借助props来实现。但如果父组件想要向孙子组件传递数据&#xff0c;那就要连续使用两层props逐级向下传递&#xff0c;如果要接收数据的是更深层的后代组件&#xff0…...

JZ76 删除链表中重复的结点

/*public class ListNode {int val;ListNode next null;ListNode(int val) {this.val val;} } */import java.util.*; public class Solution {public ListNode deleteDuplication(ListNode pHead) {//初步想想法&#xff1a; 弄一个hashmap 然后进行key存储起来。然后 如果存…...

20.2 nginx

20.2 nginx 1. 学习目标2. 介绍2.1 正向代理2.2 反向代理2.3 动态静态资源分离2.4 nginx优缺点3. 安装3.1 Linux安装****************************************************************************************************************************************************…...

MySQL学习Day26——事务基础知识

一、数据库事务概述: 事务是数据库区别于文件系统的重要特性之一,事务会让数据始终保持一致性,能通过事务机制恢复到某个时间点,可以保证提交到数据库的修改不会因为系统崩溃而丢失 1.查看引擎支持事务的情况:只有InnoDB存储引擎支持事务 SHOW ENGINES; 2.基本概念: 事…...

three.js 射线Ray,三维空间中绘制线框

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs"></div> <div>{{ res1 }}</div> <div>{{ res2 }}</div><…...

【Demo】游戏小地图

简介 该Demo基于2D关卡随机生成项目进行实现&#xff0c;旨在初步探索游戏小地图的制作。 演示 MiniMapDemo 资源下载 百度网盘&#xff08;提取码&#xff1a;1314&#xff09; 如果这篇文章对你有帮助&#xff0c;请给作者点个赞吧&#xff01;...

代码随想录算法训练营Day39 || leetCode 762.不同路径 || 63. 不同路径 II

62.不同路径 每一位的结果等于上方与左侧结果和 class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m,vector(n,0));for (int i 0; i < m; i) dp[i][0] 1;for (int j 0; j < n; j) dp[0][j] 1;for (int i 1; i < m; …...

Qt中parent()函数的使用

情景(需求)抽象&#xff1a; A类对象是B类对象的成员变量。 B类对象是A类对象的父亲。 A类对象中包含按钮&#xff0c;点击按钮&#xff0c;调用B类的成员函数。 示例&#xff1a; A类&#xff1a; #pragma once#include <QWidget> #include "ui_QtWidgetsCla…...

Python基础学习(5)流程控制

文章目录 一. 程序三大执行流程二. 分支结构1.单分支结构(if)2.双分支结构(if..else)3.多分支结构(if..elif..else) 二,缩进(tab键)三,循环结构1.while循环2.for循环①遍历字典 五.break&#xff0c;continue和pass语句1.break&#xff0c;continue2.pass Python基础学习(1)基本…...

代码随想录刷题笔记 DAY 42 | 最后一块石头的重量 II No.1049 | 目标和 No.494 | 一和零 No.474

文章目录 Day 4301. 最后一块石头的重量 II&#xff08;No. 1049&#xff09;<1> 题目<2> 笔记<3> 代码 02. 目标和&#xff08;No. 494&#xff09;<1> 题目<2> 笔记<3> 代码 03. 一和零&#xff08;No. 474&#xff09;<1> 题目&l…...

793.高精度乘法(acwing)

文章目录 793.高精度乘法题目描述高精度乘法 793.高精度乘法 题目描述 给定两个正整数A和B&#xff0c;请你计算A * B的值。 输入格式 共两行&#xff0c;第一行包含整数A&#xff0c;第二行包含整数B。 输出格式 共一行&#xff0c;包含A * B的值。 数据范围 1≤A的长度≤…...

考研经验|如何从考研失败中走出来?

对我来说&#xff0c;太丢人了 其实我在本科的时候在同学眼中&#xff0c;一直很优秀&#xff0c;每年奖学金必有我的&#xff0c;国家励志奖学金&#xff0c;国家奖学金&#xff0c;这种非常难拿的奖学金&#xff0c;我也拿过&#xff0c;本科期间学校有一个公费去新西兰留学的…...

SpringBoot如何修改pom依赖的默认版本号

1、找到SpringBoot父工程依赖。 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.5.RELEASE</version> </parent>2、ctrl 鼠标左键点击<artifact…...

UDP与TCP:了解这两种网络协议的不同之处

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…...

String、StringBuffer基本用法

一、StringBuffer基本用法 1.append():字符串连接操作 StringBuffer sb new StringBuffer();sb.append("a");sb.append("b");sb.append("c");sb.append("哈哈").append("d");System.out.println(sb);2.insert():在任意位…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作&#xff1a;验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化&#xff1a;测试aof和aof持久化机制&#xff0c;确保数据在开启后正确恢复。 事务&#xff1a;检查事务的原子性和回滚机制。 发布订阅&#xff1a;确保消息正确传递。 2、性…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

若依登录用户名和密码加密

/*** 获取公钥&#xff1a;前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...

RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上

一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema&#xff0c;不需要复杂的查询&#xff0c;只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 &#xff1a;在几秒钟…...