AOP实现操作日志记录

news2025/1/12 13:31:50

文章目录

    • 1.common-log4j2-starter
        • 1.目录
        • 2.pom.xml 引入依赖
        • 3.LogAspect.java
        • 4.Log4j2AutoConfiguration.java Log4j2自动配置类条件注入切面类
    • 2.common-log4j2-starter-demo 测试
        • 1.目录
        • 2.application.yml 启用日志切面
        • 3.TraceController.java
        • 4.结果

1.common-log4j2-starter

1.目录

CleanShot 2024-10-24 at 22.06.38@2x

2.pom.xml 引入依赖
<!-- aop -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- gson -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>
3.LogAspect.java
package com.sunxiansheng.log4j2.aspectj;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;

@Aspect
@Slf4j
public class LogAspect {

    // 使用 Gson 序列化对象,启用 PrettyPrinting,输出格式化的 JSON
    private static final Gson GSON = new GsonBuilder()
            .setPrettyPrinting() // 启用格式化输出
            .disableHtmlEscaping() // 禁用 HTML 转义,保留字符原始形式
            .create();

    // ANSI 颜色代码
    private static final String ANSI_RESET = "\u001B[0m";     // 重置颜色
    private static final String ANSI_CYAN = "\u001B[92m";     // 绿色

    /**
     * 配置切点,匹配 Controller 和 Service 层的所有方法
     */
    @Pointcut("execution(* com.sunxiansheng..controller..*(..)) || execution(* com.sunxiansheng..service..*(..))")
    public void applicationPackagePointcut() {
        // 方法为空,这是一个切点定义
    }

    /**
     * 环绕通知,记录方法执行的详细信息
     *
     * @param joinPoint 切入点
     * @return 方法执行结果
     * @throws Throwable 异常
     */
    @Around("applicationPackagePointcut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取方法签名和详细信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String className = signature.getDeclaringTypeName();
        String methodName = signature.getName();

        // 获取方法参数
        Object[] args = joinPoint.getArgs();
        String requestParams = GSON.toJson(args);

        // 记录方法进入日志,参数从下一行开始输出,参数部分以黄色显示
        log.info("==> 进入方法: {}.{}()", className, methodName);
        log.info("参数:\n" + ANSI_CYAN + "{}" + ANSI_RESET, requestParams);

        // 记录方法执行开始时间
        long startTime = System.currentTimeMillis();

        Object result;
        try {
            // 执行目标方法
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            // 捕获并记录异常信息,堆栈信息从下一行开始输出
            String exceptionStackTrace = getStackTraceAsString(throwable);
            log.error("<== 方法异常: {}.{}() | 异常信息: {}", className, methodName, throwable.getMessage());
            log.error("堆栈信息:\n{}", exceptionStackTrace);
            throw throwable;  // 重新抛出异常,保持原有异常机制
        }

        // 记录方法执行结束时间
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;

        // 获取方法返回值
        String response = GSON.toJson(result);

        // 记录方法退出日志,返回值从下一行开始输出,返回值部分以黄色显示
        log.info("<== 退出方法: {}.{}() | 耗时: {} ms", className, methodName, executionTime);
        log.info("返回值:\n" + ANSI_CYAN + "{}" + ANSI_RESET, response);

        return result;
    }

    /**
     * 将异常的堆栈信息转换为字符串
     *
     * @param throwable 异常
     * @return 堆栈信息字符串
     */
    private String getStackTraceAsString(Throwable throwable) {
        StringBuilder sb = new StringBuilder();
        for (StackTraceElement element : throwable.getStackTrace()) {
            sb.append("\tat ").append(element.toString()).append("\n");
        }
        return sb.toString();
    }
}
4.Log4j2AutoConfiguration.java Log4j2自动配置类条件注入切面类
package com.sunxiansheng.log4j2.config;

import com.sunxiansheng.log4j2.aspectj.LogAspect;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Description: Log4j2自动配置类
 *
 * @Author sun
 * @Create 2024/10/24 10:36
 * @Version 1.0
 */
@Configuration
public class Log4j2AutoConfiguration {

    /**
     * 条件注入LogAspect
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "log.aspect.enable", havingValue = "true", matchIfMissing = true)
    LogAspect logAspect() {
        return new LogAspect();
    }
}

2.common-log4j2-starter-demo 测试

1.目录

CleanShot 2024-10-24 at 22.08.54@2x

2.application.yml 启用日志切面
log:
  aspect:
    enable: true # 开启自定义的日志切面
3.TraceController.java
/**
 * 测试的Bean,入参
 */
@Data
static class AspectBeanIn {
    private String name;
    private Integer age;
    private String phone;
}

/**
 * 测试的Bean,出参
 */
@Data
static class AspectBeanOut {
    private String name;
    private Integer age;
    private String phone;
}

@RequestMapping("/aspect")
public AspectBeanOut aspect(@RequestBody AspectBeanIn aspectBeanIn) {
    AspectBeanOut aspectBean = new AspectBeanOut();
    aspectBean.setName("sun");
    aspectBean.setAge(18);
    aspectBean.setPhone("123456789");
    // 出现异常
    int i = 1 / 0;
    return aspectBean;
}
4.结果

CleanShot 2024-10-24 at 22.10.18@2x

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

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

相关文章

分布式ID—雪花算法

背景 现在的服务基本是分布式、微服务形式的&#xff0c;而且大数据量也导致分库分表的产生&#xff0c;对于水平分表就需要保证表中 id 的全局唯一性。 对于 MySQL 而言&#xff0c;一个表中的主键 id 一般使用自增的方式&#xff0c;但是如果进行水平分表之后&#xff0c;多…

JavaEE之定时器及自我实现

在生活当中&#xff0c;有很多事情&#xff0c;我们不是立马就去做&#xff0c;而是在规定了时间之后&#xff0c;在到该时间时&#xff0c;再去执行&#xff0c;比如&#xff1a;闹钟、定时关机等等&#xff0c;在程序的世界中&#xff0c;有些代码也不是立刻执行&#xff0c;…

深入Android架构(从线程到AIDL)_23 活用IBinder接口于近程通信01

1、 在同一进程里活用IBinder接口 议题 1. myActivity对象是谁创建的呢? 2. myService对象是谁创建的呢? 3. 当myService类里有个f1()函数&#xff0c;如何去调用它呢? 4. 必须先取得myService对象的指针&#xff0c;才能调用f1()函数去存取对象的属性(Attribute)值。 …

vue3后台系统动态路由实现

动态路由的流程&#xff1a;用户登录之后拿到用户信息和token&#xff0c;再去请求后端给的动态路由表&#xff0c;前端处理路由格式为vue路由格式。 1&#xff09;拿到用户信息里面的角色之后再去请求路由表&#xff0c;返回的路由为tree格式 后端返回路由如下&#xff1a; …

如何开启苹果手机(IOS)系统的开发者模式?

如何开启开发者模式&#xff1f; 一、打开设置二、隐私与安全性三、找到开发者模式四、开启开发者模式------------------------------------------------------------如果发现没有开发者模式的选项一、电脑下载爱思助手二、连接手机三、工具箱——虚拟定位——打开虚拟定位——…

国产编辑器EverEdit - 扩展脚本:在当前文件目录下新建同类型文件

1 扩展脚本&#xff1a;在当前文件目录下新建同类型文件 1.1 应用场景 用户在进行编程语言学习时&#xff0c;比如&#xff1a;Python&#xff0c;经常做完一个小练习后&#xff0c;又需要新建一个文件&#xff0c;在新建文件的时候&#xff0c;不但要选择文件类型&#xff0c…

011:利用大津算法完成图片分割

本文为合集收录&#xff0c;欢迎查看合集/专栏链接进行全部合集的系统学习。 合集完整版请参考这里。 上一篇文章介绍了大津算法可以完成图片的前景和背景分割。 总的来说&#xff0c;大津算法的核心思想就两个&#xff1a; 数学上&#xff0c;通过确定一个像素阈值&#xf…

Jenkins触发器--在其他项目执行后构建

前言&#xff1a; jenkins中有多种触发器可用&#xff0c;可以方便的控制构建的启动 这里简单介绍下项目后构建的配置方法 1. 解释&#xff1a; Build after other projects are built Set up a trigger so that when some other projects finish building, a new build is…

PowerApps助力PowerBI实现数据写回

原文发布日期: 2019-08-01 06:03:50 0000 注&#xff1a;本文旨在介绍Power BI如何利用PowerApps实现用户在前端对数据源进行增删查改&#xff0c;关于此&#xff0c;你也可以在Google上找到更详细但较零散的资料 正文 在SSAS多维数据集中&#xff0c;开发者可以给数据开启&q…

oracle 19c安装

文章目录 一 环境配置1、更换yum源2、文件配置 二 oracle环境配置1、下载依赖包2、创建用户和用户组3、创建目录并赋予权限4、修改资源限制参数5、修改内核参数6、配置安全7、配置Oracle环境变量 三 安装Oracle数据库四 创建Oracle实例五 启动数据库 一 环境配置 1、更换yum源…

LabVIEW启动时Access Violation 0xC0000005错误

问题描述 在启动LabVIEW时&#xff0c;可能出现程序崩溃并提示以下错误&#xff1a;Error 0xC0000005 (Access Violation) ​ Access Violation错误通常是由于权限不足、文件冲突或驱动问题引起的。以下是解决此问题的全面优化方案&#xff1a; 解决步骤 1. 以管理员身份运行…

xilinx平台使用多个 FIFO 拼接

Xilinx FIFO IP 输入 的最大位宽 是 1024 bit &#xff0c;当需要缓存的数据是 1280bit 又或者是 1536等 。怎么办呢&#xff1f; 有一个办法就是拆数据&#xff0c;将1280拆5个 256bit输入&#xff0c;也就是可以使用 5个 256位宽输入的FIFO拼接起来。&#xff08;其它位宽也…

Ceph分布式存储集群,不仅仅是一个简单的对象存储解决方案

Ceph 作为 OpenStack 的存储后端 块存储&#xff08;Cinder 后端&#xff09; Ceph 的 RBD&#xff08;RADOS Block Device&#xff09;模块作为 OpenStack Cinder 服务的后端&#xff0c;为虚拟机提供块级别的存储资源。RBD 支持快照、克隆和恢复等功能&#xff0c;能够满足虚…

SD ComfyUI工作流 老照片修复上色

文章目录 老照片修复上色SD模型Node节点工作流程开发与应用效果展示老照片修复上色 该工作流专门设计用于老照片的修复和上色,通过一系列高级的图像处理技术,包括深度图预处理、面部修复、上色和图像放大等步骤,来恢复老照片的质量并增加色彩。首先,工作流加载老照片并进行…

Jmeter-压测时接口如何按照顺序执行

Jmeter-压测时接口如何按照顺序执行-临界部分控制器 在进行压力测试时&#xff0c;需要按照顺序进行压测&#xff0c;比如按照接口1、接口2、接口3、接口4 进行执行 查询结果是很混乱的&#xff0c;如果请求次数少&#xff0c;可能会按照顺序执行&#xff0c;但是随着次数增加…

Mysql--运维篇--日志管理(连接层,SQL层,存储引擎层,文件存储层)

MySQL提供了多种日志类型&#xff0c;用于记录不同的活动和事件。这些日志对于数据库的管理、故障排除、性能优化和安全审计非常重要。 一、错误日志 (Error Log) 作用&#xff1a; 记录MySQL服务器启动、运行和停止期间遇到的问题和错误信息。 查看&#xff1a; 默认情况下…

【2025 Rust学习 --- 13 闭包:Rust的Lambda】

Rust的Lambda — 闭包 对整型向量进行排序很容易&#xff1a; integers.sort(); 遗憾的是&#xff0c;当我们想对一些数据进行排序时&#xff0c;它们几乎从来都不是整型向量。例 如&#xff0c;对某种记录型数据来说&#xff0c;内置的 sort 方法一般不适用&#xff1a; st…

鸿蒙面试 2025-01-09

鸿蒙分布式理念&#xff1f;&#xff08;个人认为理解就好&#xff09; 鸿蒙操作系统的分布式理念主要体现在其独特的“流转”能力和相关的分布式操作上。在鸿蒙系统中&#xff0c;“流转”是指涉多端的分布式操作&#xff0c;它打破了设备之间的界限&#xff0c;实现了多设备…

一个基于Spring Boot的智慧养老平台

以下是一个基于Spring Boot的智慧养老平台的案例代码。这个平台包括老人信息管理、健康监测、紧急呼叫、服务预约等功能。代码结构清晰&#xff0c;适合初学者学习和参考。 1. 项目结构 src/main/java/com/example/smartelderlycare├── controller│ ├── ElderlyCon…

Taro+react 开发第一节创建 带有redux状态管理的项目

Taro 项目基于 node&#xff0c;请确保已具备较新的 node 环境&#xff08;>16.20.0&#xff09;&#xff0c;推荐使用 node 版本管理工具 nvm 来管理 node&#xff0c;这样不仅可以很方便地切换 node 版本&#xff0c;而且全局安装时候也不用加 sudo 了。 1.安装 npm inf…