基于aop的日志记录

aop实现日志记录

    • 记录工具
    • 切面
    • logback配置
    • 测试

记录工具

目标:
统计rest接口请求参数,请求地址,请求IP,响应数据,响应时间。方便后续问题排查,以及性能的总体分析。

  1. 基于springboot
  2. 会使用面向切面编程
  3. 基于logback,使用slf4j

切面

package top.lel.ft.config.aop;import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import top.lel.ft.common.utils.IpUtils;
import top.lel.ft.framework.BaseController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;/*** @author echo lovely* @description web url 日志记录* @since 2022/6/14 09:22*/@Slf4j
@Aspect
@Component
public class UrlLogAspect {// 使用ThreadLocal记录第一次的请求时间, 统计接口响应时间// 也可用环绕通知统计接口响应时间final ThreadLocal<Long> tl = new ThreadLocal<>();// 作用于controller的切点@Pointcut("execution(public * top.lel.ft.controller.*.*(..))" +"|| execution(public * top.lel.ft.web.*.*(..))")public void controllerAspect() {}// 记录IP,日志,请求参数@Before(value = "controllerAspect()")public void methodBefore(JoinPoint joinPoint) {List<String> paramList = new ArrayList<>();Object[] args = joinPoint.getArgs();if (args != null) {for (Object arg : args) {if (!isOriginType(arg)) {paramList.add(JSON.toJSONString(arg));}}}String methodName = joinPoint.getSignature().getName();HttpServletRequest httpReq = BaseController.getServletReq();String ipAddress = IpUtils.getIpAddr(httpReq);// 日志增强实现MDC.put("ip", ipAddress);MDC.put("traceId", UUID.randomUUID().toString().replaceAll("-", ""));log.debug("请求路径: {}, 请求方法: {}, 请求参数: {}, 请求IP: {}", httpReq.getRequestURI(), methodName, String.join(", ", paramList), ipAddress);tl.set(System.currentTimeMillis());}@AfterReturning(pointcut = "controllerAspect()", returning = "retVal")public void afterRet(Object retVal) {log.info("响应时间: {}毫秒, 响应内容: {}", System.currentTimeMillis() - tl.get(), JSON.toJSONString(retVal));tl.remove();MDC.clear();}/*** 判断是原生servlet容器*/private boolean isOriginType(Object e) {return e instanceof HttpServletRequest || e instanceof HttpSession;}
}

logback配置

application.yaml 配置logback.logPath

<configuration><!-- 日志存放路径 --><springProperty scope="context" name="logPath" source="logback.logPath"/><conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /><!-- 日志输出格式 --><!--  traceId为日志跟踪id,ip为请求ip,见切面MDC工具  --><property name="log.pattern" value="%red(%d{yyyy-MM-dd HH:mm:ss.SSS}) [%X{traceId}] %boldMagenta([%X{ip}] [%thread]) %highlight(%-5level) %clr(%logger{50}){pink} - %cyan(%msg%n)" /><!-- <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%X{ip}] [%thread] %-5level %logger{20} - %msg%n" />--><springProperty scope="context" name="appName" source="spring.application.name"/><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.PatternLayout"><Pattern>${log.pattern}</Pattern></layout></encoder></appender><!-- 系统日志输出 --><appender name="server" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logPath}/${appName}_server.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${logPath}/%d{yyyyMMdd}/${appName}_server.%i.log</fileNamePattern><!-- 日志最大的历史 60--><maxHistory>60</maxHistory><!-- 文件最大50M --><TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><MaxFileSize>50MB</MaxFileSize></TimeBasedFileNamingAndTriggeringPolicy></rollingPolicy><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.PatternLayout"><Pattern>${log.pattern}</Pattern></layout></encoder><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><!-- 过滤的级别 --><level>DEBUG</level></filter></appender><appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${logPath}/${appName}_error.log</file><!-- 循环政策:基于时间创建日志文件 --><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!-- 日志文件名格式 --><fileNamePattern>${logPath}/%d{yyyyMMdd}/${appName}_error.%i.log</fileNamePattern><!-- 日志最大的历史 60--><maxHistory>60</maxHistory><!-- 文件最大50M --><TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><MaxFileSize>50MB</MaxFileSize></TimeBasedFileNamingAndTriggeringPolicy></rollingPolicy><encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"><layout class="ch.qos.logback.classic.PatternLayout"><Pattern>${log.pattern}</Pattern></layout></encoder><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><!-- 过滤的级别 --><level>ERROR</level></filter></appender><!--异常日志监控告警 todo: 实现WarningAppender, 如进行告警邮件通知.--><!--ch.qos.logback.core.UnsynchronizedAppenderBase(ILoggingEvent logEvent)--><!--<appender name="warningAppender" class="top.lel.ft.common.warning.WarningAppender"><filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>ERROR</level></filter></appender>--><!-- Spring日志级别控制  --><logger name="org.springframework" level="warn" /><root level="info">
<!--        <appender-ref ref="warningAppender" />--><appender-ref ref="STDOUT" /></root><root level="info"><appender-ref ref="server" /><appender-ref ref="error" /></root>
</configuration>

测试

、


or

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/420250.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

xss防御补丁_Discuz论坛最新dom xss漏洞的解决方法

无忧主机小编在日常处理客户网站问题时&#xff0c;经常遇到网站因为程序漏洞出现的问题。网站安全的发展&#xff0c;才能使得管理者放心营运。但是比较无奈的是&#xff0c;漏洞问题貌似屡见不鲜&#xff0c;就算再强大、再常用的程序&#xff0c;都会有诸如漏洞的问题&#…

前端学习(1351)模板引擎

const template require(art-template); //绝对路径 模板中显示的数据 const path require(path); const views path.join(__dirname, index.art); const html template(views, {name: 张三,age: 20 }); console.log(html); index.art <!DOCTYPE html> <html la…

内存数据库和关系数据库之间的数据同步原理

关系数据库到内存数据库同步这部分数据同步采用增量表的方式&#xff0c;系统新增或更新的数据将生成到关系数据库的增量表中&#xff0c;程序先到这些增量表中查询数据。如果能在这些增量表中查到数据就把这些数据更新到内存数据库对应表中&#xff0c;如果查不到&#xff0c;…

java 图片压缩100k_java实现图片压缩

简介我们在项目中经常会遇到图片上传的需求&#xff0c;如商品图片&#xff0c;但图片太大的话&#xff0c;在客户端加载太慢影响用户体验&#xff0c;所有一般会将图片进行压缩。实现原图添加依赖net.coobirdthumbnailator0.4.8按质量压缩import java.io.File;import java.io.…

前端学习(1352)模板语法

demo27.js const template require(art-template); //绝对路径 模板中显示的数据 const path require(path); const views path.join(__dirname, 01.art); const html template(views, {name: 张三,age: 20,content: <h1>我是歌谣</h1> }); console.log(html)…

marker 头像 高德地图_高德地图头像怎么更换 高德地图更换头像图文教程

相信绝大部分人都知道微信头像以及QQ头像怎么更换&#xff0c;而设置头像也是很多人喜欢做的一件事情。而对于经常使用高德地图的用户来说&#xff0c;头像该怎么设置呢&#xff1f;对于这群用户&#xff0c;下面百事网小编为大家带来详细的高德地图更换头像图文教程&#xff0…

UVA10763:Foreign ExchangeUVA10340: All in All(水题)

10763:水题不解释直接贴代码。 #include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> #include <math.h> #include <queue> #define eps 1e-9 typedef long long ll; using namespace std; int n; int d[500…

项目经验BigDecimal

BigDeciaml1. BigDecimal1. BigDecimal 我们知道&#xff0c;关于金钱相关的计算&#xff0c;都用BigDeciaml数据类型, 来表示金额。所有关于金额的项目中不能缺少它的使用。 而我今天说说用这个类型&#xff0c;踩到的坑。 金额比较问题 带精度不适用equals比较。使用compar…

前端学习(1353)模板语法条件判断

const template require(art-template); //绝对路径 模板中显示的数据 const path require(path); const views path.join(__dirname, 02.art); const html template(views, {name: 张三,age: 20,/* content: <h1>我是歌谣</h1> */ }); console.log(html); 0…

MySql 缓存查询原理与缓存监控 和 索引监控

MySql缓存查询原理与缓存监控 And 索引监控 by:授客 QQ&#xff1a;1033553122 查询缓存 1.查询缓存操作原理 mysql执行查询语句之前&#xff0c;把查询语句同查询缓存中的语句进行比较&#xff0c;且是按字节比较&#xff0c;仅完全一致才被认为相同。如下&#xff0c;这两…

python pandas 数据分析 DataFrame

二维组转表 import pandas as pdarr [[google, 100], [amazon, 99], [github, 233], [microsoft, 88]]df pd.DataFrame(dataarr, columns[Site, Age]) # 二元组转成表&#xff0c; 列为 Site 和 Age. print(df)col df[Age]# 获取Age列 print(col)# 获取Age > 100的所有行…

前端学习(1354):集合关联

const mongoose require(mongoose); mongoose.connect(mongodb://localhost/playground, { useUnifiedTopology: true }).then(() > console.log(数据库连接成功)).catch(err > console.log(err, 数据库连接失败)) const userSchema new mongoose.Schema({name: {type:…

ati jti jwt 和_一文搞懂JWT

Django REST framework JWT一、JWT简介二、JWT 组成headersignature三.使用手动生成jwt前端保存jwt一、JWT简介JWT(Json Web Token) 是一个开放标准(RFC 7519)&#xff0c;它定义了一种用于简洁&#xff0c;自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT…

[禅悟人生]心平气和, 慢慢修行

有一个人问投子大同禅师&#xff1a;“一个没有眼晴的人&#xff0c;走路时应该怎样选择方向呢&#xff1f;” 禅师回答说&#xff1a;“他可以朝着四面八方行走&#xff0c;周围都会留下他的脚印。” 那人又问&#xff1a;“既然他都没有眼睛&#xff0c;那么他的脚印怎么会遍…

前端学习(1355)模板语法循环

const template require(art-template); //绝对路径 模板中显示的数据 const path require(path); const views path.join(__dirname, 03.art); const html template(views, {users: [{name: geyao,age: 20,sex: 男}, {name: xiao,age: 20,sex: 男}, {name: hau,age: 20,se…

c++检测ip是否匹配子网掩码_网络工程师从入门到精通通俗易懂系列 | ARP和IP这篇文章讲的相当详细了,这么基础的知识往往也是最容易遗忘的!...

网络层负责将报文从源送到目的包括TCP建立连接&#xff0c;也需要依靠网络层&#xff0c;来将这个连接请求&#xff0c;传递到对方。为设备提供逻辑地址&#xff0c;也就是IP地址主流是IPV4地址IPV4地址&#xff0c;为32位二进制数&#xff0c;长度4个字节&#xff0c;1字节等于…

复合索引字段的排序对搜素的影响

索引是对数据库大数据的查询优化的一种有效的手段&#xff0c;索引又可分为唯一索引和复合索引 单一索引是指索引列为一列的情况&#xff0c;即新建索引的语句只实施在一列上面。 用户可以在多个列上建立索引&#xff0c;这种索引叫做复合索引(组合索引)。复合索引的创建方法与…

mysql图片jsp_mysql jsp 图片

?转个帖子给你&#xff0c;我也是用的这个&#xff0c;已经成功实现了的。我在程序代码里贴了向Mysql数据库写入image代码的程序&#xff0c;可是好多人都是Java的初学者&#xff0c;对于这段代码&#xff0c;他们无法将它转换成jsp&#xff0c;所以我在这在写一下用jsp怎样向…

递归和迭代的差别

递归的基本概念:程序调用自身的编程技巧称为递归,是函数自己调用自己. 一个函数在其定义中直接或间接调用自身的一种方法,它通常把一个大型的复杂的问题转化为一个与原问题类似的规模较小的问题来解决,能够极大的降低代码量.递归的能力在于用有限的语句来定义对象的无限集合. 使…

前端学习(1357) :模板配置

const template require(art-template); //绝对路径 模板中显示的数据 const path require(path); const views path.join(__dirname, 07.art); const dateFormat require(dateFormat) template.defaults.imports.dateFormat dateFormat; const html template(views, {ti…