当前位置: 首页 > 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():在任意位…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...