spring boot 之 结合aop整合日志

news2024/11/14 22:05:00

AOP

该切面仅用于请求日志记录,若有其他需求,在此基础上扩展即可,不多逼逼,直接上代码。

引入切面依赖

<!-- 切面 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

日志切面类

import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Slf4j
@Aspect
@Component
public class RequestAop {

    private static final String START_TIME = "request-start";

    // 按需修改需要扫描的controller层
    @Pointcut("execution(* com.example.controller..*.*(..))")
    public void pointCut() {
        //该方法仅用于扫描controller包下类中的方法,而不做任何特殊的处理。
    }

    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint) {
        HttpServletRequest request =
                ((ServletRequestAttributes) Objects
                        .requireNonNull(RequestContextHolder.getRequestAttributes()))
                        .getRequest();
        Long start = System.currentTimeMillis();
        request.setAttribute(START_TIME, start);
    }

    @Around("pointCut()")
    @SneakyThrows
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object result = joinPoint.proceed();
        try {
            // 获取方法名称
            String method = joinPoint.getSignature().getName();
            // 获取类名称
            String className = joinPoint.getSignature().getDeclaringTypeName();
            // 获取请求
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            // 请求路径
            String requestUrl = request.getRequestURL().toString();
            // 获取请求参数进行打印
            Signature signature = joinPoint.getSignature();
            // 参数名数组
            String[] parameterNames = ((MethodSignature) signature).getParameterNames();
            // 构造参数组集合
            List<Object> argList = new ArrayList<>();
            for (Object arg : joinPoint.getArgs()) {
                // request/response无法使用toJSON
                if (arg instanceof HttpServletRequest) {
                    argList.add("request");
                } else if (arg instanceof HttpServletResponse) {
                    argList.add("response");
                } else {
                    argList.add(JSON.toJSON(arg));
                }
            }
            log.info("类名:[{}] 方法名:[{}] 请求URL:[{}] 请求参数:{} -> {} 请求结果:{}", className, method, requestUrl, JSON.toJSON(parameterNames), JSON.toJSON(argList), JSON.toJSON(result));
        } catch (Exception e) {
            log.error("切面类参数获取失败: {}", e.getMessage());
        }
        return result;
    }

    @After("pointCut()")
    public void doAfter(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        Long start = (Long) request.getAttribute(START_TIME);
        Long end = System.currentTimeMillis();
        // 耗时
        long costTime = end - start;
        // 方法名
        String method = joinPoint.getSignature().getName();
        log.info("方法名:[{}] 请求耗时:[{}ms]", method, costTime);
    }
}

日志

级别

日志级别(Log Levels)是指日志消息的优先级或者重要程度,它用于对日志的不同类型和重要程度进行分类和过滤。
不同的日志框架可能使用不同的命名和数量的日志级别,但基本概念是相似的。以下是常见的几个标准日志级别:

1,TRACE(追踪):最低级别的日志,包含详细的调试信息,用于追踪代码的执行流程,如方法的输入参数、内部状态等。
2,DEBUG(调试):用于输出调试信息,在开发和调试阶段使用,帮助排查问题和跟踪代码执行情况以及验证程序的行为。
3,INFO(信息):提供程序运行过程中的重要信息,用于向用户提供一些关键的操作状态和进度,如程序启动关闭、配置项变更等。
4,WARN(警告):表示潜在的问题或异常情况,不会阻止程序继续执行,但可能会影响程序的正常运行,需要开发人员注意。
5,ERROR(错误):表示错误情况,通常表示某个功能或步骤无法正常完成,但程序仍然可以继续运行,需要开发人员关注和解决。
6,FATAL(致命):最高级别的日志,表示最严重的错误,表示程序无法继续运行,会导致应用程序的中断或崩溃,如系统崩溃。

特别说明:以上日志级别由上往下依次增强,而日志级别越高,控制台打印出的日志信息就越少,但打印出的日志信息越重要。

引入lombok依赖

引入lombok后,在需要记录日志的类上添加@Slf4j注解即可。

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.32</version>
    <scope>provided</scope>
</dependency>

日志配置文件

resources下新建目录logslogs下新建logback-spring.xml文件。
仅配置了常用的infoerror级别,其余按需配置即可。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <!-- 引入默认得配置文件 -->
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <!-- 模块名标识日志名称 -->
    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <!-- info日志单文件大小限制 -->
    <springProperty scope="context" name="logback.fileInfoLog.maxFileSize" source="logback.fileInfoLog.maxFileSize" defaultValue="1024MB" />
    <!-- info日志最大保留时长单位天 -->
    <springProperty scope="context" name="logback.fileInfoLog.maxHistory" source="logback.fileInfoLog.maxHistory" defaultValue="30" />
    <!-- info日志文件总大小,超过该大小,旧得即将删除 -->
    <springProperty scope="context" name="logback.fileInfoLog.totalSizeCap" source="logback.fileInfoLog.totalSizeCap" defaultValue="10GB" />

    <!-- error日志单文件大小限制 -->
    <springProperty scope="context" name="logback.fileErrorLog.maxFileSize" source="logback.fileErrorLog.maxFileSize" defaultValue="1024MB" />
    <!-- error日志最大保留时长单位天 -->
    <springProperty scope="context" name="logback.fileErrorLog.maxHistory" source="logback.fileErrorLog.maxHistory" defaultValue="30" />
    <!-- error日志文件总大小,超过该大小,旧得即将删除 -->
    <springProperty scope="context" name="logback.fileErrorLog.totalSizeCap" source="logback.fileErrorLog.totalSizeCap" defaultValue="10GB" />

    <!-- 日志目录 -->
    <springProperty scope="context" name="logback.rootDir" source="logback.rootDir" defaultValue="logs"/>

    <!-- 控制台输出得日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>

    <!-- 日志文件输出得日志格式 -->
    <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p %t [%c:%L]-%m%n"/>

    <!-- 控制台输出 -->
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </layout>
    </appender>

    <!-- info日志得设定 -->
    <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>DENY</onMatch>
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <file>${logback.rootDir}/${springAppName}.log</file>
        <!--滚动策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy" >
            <!--路径-->
            <fileNamePattern>${logback.rootDir}/%d{yyyy-MM,aux}/%d{yyyy-MM-dd,aux}/${springAppName}-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
            <maxFileSize>${logback.fileInfoLog.maxFileSize}</maxFileSize>
            <maxHistory>${logback.fileInfoLog.maxHistory}</maxHistory>
            <totalSizeCap>${logback.fileInfoLog.totalSizeCap}</totalSizeCap>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
    </appender>

    <!-- 错误日志 -->
    <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <file>${logback.rootDir}/${springAppName}-error.log</file>
        <!--滚动策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy" >
            <!--路径-->
            <fileNamePattern>${logback.rootDir}/%d{yyyy-MM,aux}/%d{yyyy-MM-dd,aux}/${springAppName}-error-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
            <maxFileSize>${logback.fileErrorLog.maxFileSize}</maxFileSize>
            <maxHistory>${logback.fileErrorLog.maxHistory}</maxHistory>
            <totalSizeCap>${logback.fileErrorLog.totalSizeCap}</totalSizeCap>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
    </appender>

    <appender name="ASYNC_consoleLog" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="consoleLog"/>
    </appender>
    <appender name="ASYNC_fileInfoLog" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="fileInfoLog"/>
    </appender>
    <appender name="ASYNC_fileErrorLog" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="fileErrorLog"/>
    </appender>

    <root level="info">
        <appender-ref ref="ASYNC_consoleLog" />
        <appender-ref ref="ASYNC_fileInfoLog" />
        <appender-ref ref="ASYNC_fileErrorLog" />
    </root>

</configuration>

application.yml配置

# spring.application.name 必须配置
# 因为上述日志配置文件指定了项目启动后输出的日志文件命名,即为该配置
spring:
  application:
    name: LogApplication

logging:
# 指定自定义的配置文件
  config: classpath:logs/logback-spring.xml
# 指定输出的日志级别
# trace < debug < info < warn < error
# 例如:指定输出级别为info,则trace和debug均不会输出
  level:
    root: info #该方式指定的是整个项目的日志输出级别
    # com.example.controller: debug #也可以指定具体某个包下的日志输出级别

结果展示

在这里插入图片描述
以上述配置为例,项目启动后会在项目下生成logs目录,该目录下会有两个日志文件:LogApplication.logLogApplication-error.log,项目中所有log.error()日志都会输出到LogApplication-error.log,其余日志则输出到LogApplication.log.

拓展

将指定的类产生的日志输出到指定的文件中。
示例:RequestAop切面中产生的是所有的请求记录,将该类的日志放入指定的文件。

logback-spring.xml新增配置,未添加请求日志文件的大小限制、存放时间等配置,若有需求,按infoerror配置仿写即可。

    <!-- 接口请求日志 -->
    <appender name="requestLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>DENY</onMatch>
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
        <!--此处配置输出文件名称为 应用名-request.log -->
        <file>${logback.rootDir}/${springAppName}-request.log</file>
        <!--滚动策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy" >
            <!--路径-->
            <fileNamePattern>${logback.rootDir}/%d{yyyy-MM,aux}/%d{yyyy-MM-dd,aux}/${springAppName}-request-%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
            <maxFileSize>${logback.fileInfoLog.maxFileSize}</maxFileSize>
            <maxHistory>${logback.fileInfoLog.maxHistory}</maxHistory>
            <totalSizeCap>${logback.fileInfoLog.totalSizeCap}</totalSizeCap>
            <cleanHistoryOnStart>true</cleanHistoryOnStart>
        </rollingPolicy>
    </appender>
    
	<appender name="ASYNC_requestLog" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="requestLog"/>
    </appender>

	<!--将切面类所在包的位置配置上-->
    <logger name="com.example.aop.RequestAop" additivity="false" level="INFO">
        <appender-ref ref="ASYNC_requestLog"/>
    </logger>

在这里插入图片描述
以上述配置为例,项目启动后会在项目下生成logs目录,该目录下会有三个日志文件:LogApplication.logLogApplication-error.logLogApplication-request.log,项目中所有log.error()日志都会输出到LogApplication-error.logRequestAop切面类的日志会输出到LogApplication-request.log,其余日志则输出到LogApplication.log.

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

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

相关文章

云主机选购指南:如何选择适合自己的云主机

一、认识移动云 移动云是中国移动提供的专业云服务品牌&#xff0c;基于移动云计算技术构建。它实现了云网一体化&#xff0c;确保客户享有安全可控的服务。通过充分利用移动云计算能力&#xff0c;打造了N31X资源布局&#xff0c;结合各省级数据中心&#xff0c;通过专线互联…

基于Vue的前端加载中页面动画——弹跳动画Loading组件的设计与实现

基于Vue的前端加载中页面动画——弹跳动画Loading组件的设计与实现 摘要 随着技术的飞速进步&#xff0c;前端开发的复杂性日益提升。传统的开发方式通常将整个系统构建为一个整体&#xff0c;导致即使是微小的改动或功能的增加也可能引起整体逻辑的变动。为了解决这个问题&a…

【蓝桥杯】国赛普及-

题目列表 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) P9420 [蓝桥杯 2023 国 B] 子 2023 / 双子数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include<bits/stdc.h> using llunsigned long long; #define int ll const int N2e510; int k0; std::string s; int…

嵌入式UI开发-lvgl+wsl2+vscode系列:1、资料收集以及Windows下WSL2模拟环境运行示例demo

文章目录 一、前言二、资料收集三、Windows下WSL2上编译运行lvgl的demo程序1、lvgl简介2、lvgl特性3、配置要求4、Windows下vscodewsl2模拟环境搭建4.1、安装vscodewsl24.2、下载获取项目&#xff1a;4.3、安装显卡驱动4.4、下载lvgl并编译运行示例demo 四、最后 一、前言 UI界…

Python数据分析-心脏病(随机森林预测分析)

本次案例分析用心脏病数据集来做随机森林模型预测 导入基本的数据分析包 import pandas as pd import numpy as np from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score…

云计算-关系型数据库(Relational Database)

关系数据库服务&#xff08;RDS&#xff09;&#xff08;Relational Database Service (RDS)&#xff09; Amazon RDS 可用于在云中设置和运行关系数据库。它支持多种数据库实例类型以及多个数据库引擎&#xff0c;如 Amazon Aurora、PostgreSQL、MySQL、MariaDB、Oracle 数据库…

基于Vue的应届毕业生财务管理系统-计算机毕业设计源码82886

摘 要 随着互联网大趋势的到来&#xff0c;社会的方方面面&#xff0c;各行各业都在考虑利用互联网作为媒介将自己的信息更及时有效地推广出去&#xff0c;而其中最好的方式就是建立网络管理系统&#xff0c;并对其进行信息管理。由于现在网络的发达&#xff0c;应届毕业生财务…

c# sqlite使用

安装包 使用 const string strconn "Data Sourcedata.db"; using (SQLiteConnection conn new SQLiteConnection(strconn)) {conn.Open();var cmd conn.CreateCommand();//创建表cmd.CommandText "create table t1(id int,name varchar(10))";var obj…

二百三十八、Hive——Hive中为每条数据创建唯一ID

一、目的 由于Kafka的JSON中缺少唯一的ID标识字段&#xff0c;因此发现后面的需求中DWD层表需要有一个唯一ID字段&#xff0c;这样才能与数据质量表更好的关联 二、Hive版本 尚硅谷的3.1.2版本 三、Hive创建唯一ID方法 网上的创建唯一ID方法有很多&#xff0c;这里展示一些…

子网划分,交换机原理与配置

子网划分 IP地址 IPv4由32位二进制数组成&#xff0c;一般用点分十进制来表示 IPv4是由32位二进制数组成&#xff0c;分成四组,第组八位。例如:11000000.10101000.00000000.00000010 为了便于配置通常表示成点分十进制形式例如:192.168.0.2 255.255.255.0 IPv6由128位组成&…

HDR视频相关标准-HDR vivid(二)

上文介绍了HDRvivid的一些技术。今天从全局角度来看看HDR视频的处理流程&#xff0c;HDR视频系统&#xff0c;即建立一个比SDR视频更大的色彩/亮度坐标体系&#xff0c;并改变系统的传输函数&#xff0c;以再现更大的色域(WCG)和更高的亮度动态范围。 菁彩 HDR技术的专业术语 …

充电宝哪个牌子好用?充电宝品牌怎么选?充电宝最好的牌子排名

现在市面上的充电宝品牌琳琅满目&#xff0c;但并不是所有的充电宝都安全可靠。据央视的一个报道&#xff0c;市面上有35%充电宝质量是不过关的!充电宝买不对就非常容易出现爆炸的一个情况&#xff0c;所以大家对选充电宝不仅能保障设备的安全。那么&#xff0c;充电宝哪个牌子…

TypeScript学习日志-第三十二天(infer关键字)

infer关键字 一、作用与使用 infer 的作用就是推导泛型参数&#xff0c;infer 声明只能出现在 extends 子语句中&#xff0c;使用如下&#xff1a; 可以看出 已经推导出类型是 User 了 二、协变 infer 的 协变会返回联合类型&#xff0c;如图&#xff1a; 三、逆变 infer…

FusionCharts 隐藏试用图标

1、找到fusioncharts.js文件 2、搜索“raphael-group-” 3、找到此处进行替换黄线部分 将&#xff1a;"a.setAttribute("class","raphael-group-"t)" 替换成"(a.setAttribute("class","raphael-group-"t),a.setAttr…

基于 Wireshark 分析 TCP 协议

一、TCP 协议 TCP&#xff08;Transmission Control Protocol&#xff09;是一种面向连接的、可靠的传输层协议。它在网络通信中扮演着重要的角色&#xff0c;用于保证数据的可靠传输。 TCP协议的特点如下&#xff1a; 1. 面向连接&#xff1a;在通信前需要先建立连接&#x…

学习Uni-app开发小程序Day26

这一章学习的内容细节较多&#xff0c;主要是分为&#xff1a;首次加载减少网络消耗、获取图片的详细信息、图片的评分和避免重复评分、将图片下载到本地并且获取设备的授权 加载图片减少网络消耗 这里突出这个功能&#xff0c;是根据老师视频上的描述&#xff0c;个人觉得很…

如何彻底搞懂组合(Composite)设计模式?

当我们在设计系统对象关系时&#xff0c;有时候会碰到这样一种场景&#xff0c;一个对象中包含了另一组对象&#xff0c;两者构成一种”部分-整体”的关联关系。 正如上图中所展示的&#xff0c;当我们面对这样一种对象关系时&#xff0c;通常都需要分别构建单独的访问方式&…

11.Redis之zset类型

1.zset类型基本介绍 有序描述的是&#xff1a;升序/降序 Set 集合 1.唯一 2. 无序 孙行者,行者孙, 者行孙 >同一只猴~~ List有序的 孙行者,行者孙, 者行孙 >不同的猴~~ zset 中的 member 仍然要求是唯一的!!(score 则可以重复) 排序的规则是啥? 给 zset 中的 member 同…

太狠了,凌晨5点面试。。

(关注数据结构和算法&#xff0c;了解更多新知识) 网上看到一网友发文说收到面试邀请&#xff0c;面试时间竟然是早晨5点&#xff0c;这是要猝死的节奏。有的网友说应该是下午 5 点&#xff0c;如果是下午 5 点直接写下午 5 点就行了&#xff0c;或者写 17 点也行&#xff0c;直…

中医理疗元宇宙 科技赋能中医药产业走向国际市场

基于380亿参数量&#xff0c;对中医药海量文本进行数据训练&#xff0c;实现方剂优化、机制阐释和新适应症的精准发现……日前在天津召开的数智赋能大健康产业新质生产力暨第四届中医药国际发展大会上&#xff0c;由天士力医药集团与华为云共同开发的“数智本草”中医药大模型正…