Java教程:如何使用切面环绕方法对所有接口进行添加出入参日志保存功能

背景:

----在很多时候我们做开发时,往往只是提供一个对外接口来进行前后端调试,或第三方系统联调,并使用log进行日志打印,每当出现问题进行排查时,只需要查看服务器日志就可以定位到问题,从而解决问题,但当接口慢慢变多,公司开发部署方案越来越成熟时,分工明确,查看服务器日志却变得不那么随意,这个时候如果还和以前一样出现问题就打开服务器查看日志文件就会变得越来越困难,所以我们必须对重要日志信息进行数据库存储,出现问题直接在浏览器页面查看即可,非常方便,本章就来教大家如何使用切面的方式完成这一操作,方便快捷,使用简单,话不多说,教学开始~

第一步、首先我们需要创建一个注解接口:

/**
 * 接口日志记录注解
 *
 * @author wfeil211@foxmail.com
 */
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InterfaceLog {

    /**
     * 标题
     */
    public String title() default "";

}

第二步、创建接口日志保存实体类:

/**
 * 接口日志对象 interface_log
 *
 * @author wfeil211@foxmail.com
 */
@Data
public class InterfaceLog extends BaseEntity {
    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    private String id;

    /**
     * 接口说明
     */
    private String title;

    /**
     * 接口入参
     */
    private String requestParam;

    /**
     * 响应参数
     */
    private String responseParam;

    /**
     * 方法名称
     */
    private String methodName;

    /**
     * 请求方式
     */
    private String requestWay;

    /**
     * 请求耗时(ms)
     */
    private String handleTime;

    /**
     * 请求时间 yyyy-MM-dd HH:mm:ss
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date requestTime;

    /**
     * 错误信息
     */
    private String errorInfo;

    /**
     * 错误码
     */
    private String errorCode;

    /**
     * 用户ip
     */
    private String userIp;

    /**
     * 服务器ip
     */
    private String serverIp;

    /**
     * 接口状态(0正常 1异常)
     */
    private String status;
}

第三步、创建我们的切面类,将第一步接口进行切入:

/**
 * 接口日志记录处理
 *
 * @author wfeil211@foxmail.com
 */
@Aspect
@Component
public class InterfaceLogAspect {
    private static final Logger log = LoggerFactory.getLogger(InterfaceLogAspect.class);

    // 接口日志
    @Autowired
    private InterfaceLogService interfaceLogService;

    // 配置切入点
    @Pointcut("@annotation(com.feilin.InterfaceLog)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object aroundBusAsMethod(ProceedingJoinPoint joinPoint) {
        // 获得注解
        InterfaceLog controllerLog = getAnnotationLog(joinPoint);
        if (controllerLog == null) {
            return null;
        }
        Object[] args = joinPoint.getArgs();
        long time;
        // 进入方法之前的时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        InterfaceLog interfaceLog = new InterfaceLog();
        // 请求的地址
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        interfaceLog.setUserIp(ip);
        interfaceLog.setTitle(controllerLog.title());
        String hostAddress = "";
        try {
            hostAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            log.error("获取服务器ip异常,原因={}", e.toString());
        }
        interfaceLog.setServerIp(hostAddress);

        // 设置方法名称
        String methodName = joinPoint.getSignature().getName();
        interfaceLog.setMethodName(methodName);
        Object respObj = null;
        // 设置请求方式
        interfaceLog.setRequestWay(ServletUtils.getRequest().getMethod());
        try {
            respObj = joinPoint.proceed(args);
        } catch (Throwable e) {
            log.error("获取方法响应异常,原因:{}", e.toString());
        }
        stopWatch.stop();
        time = stopWatch.getTotalTimeMillis();
        // 请求时间与耗时
        interfaceLog.setHandleTime(String.valueOf(time));
        interfaceLog.setRequestTime(new Date());
        // 请求入参出参
        interfaceLog.setRequestParam(StringUtils.substring(argsArrayToString(args), 0, 2048));
        if (!ObjectUtils.isEmpty(respObj)) {
            interfaceLog.setResponseParam(StringUtils.substring(JSON.toJSONString(respObj), 0, 2048));
            JSONObject parse = (JSONObject) JSONObject.parse(interfaceLog.getResponseParam());
            String code = parse.getString("code");
            String msg = parse.getString("msg");
            if (!"0".equals(code)) {
                interfaceLog.setErrorCode(code);
                interfaceLog.setErrorInfo(msg);
                interfaceLog.setStatus("1");
            } else {
                interfaceLog.setStatus("0");
            }
        }
        try {
            interfaceLogService.insertInterfaceLog(interfaceLog);
        } catch (Throwable e) {
            log.error("接口日志保存失败,interfaceLog={},原因={}", interfaceLog.toString(), e.toString());
        }
        return respObj;
    }

    /**
     * 是否存在注解,如果存在就获取
     */
    private InterfaceLog getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(InterfaceLog.class);
        }
        return null;
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        String params = "";
        if (ArrayUtils.isNotEmpty(paramsArray) && paramsArray.length > 0 && !(paramsArray[0] instanceof BeanPropertyBindingResult)) {
            params = JSON.toJSON(paramsArray[0]).toString();
        } else {
            log.info("切面日志保存/打印,获取参数为空");
        }
        return params.trim();
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象,则返回true;否则返回false。
     */
    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Iterator iter = collection.iterator(); iter.hasNext(); ) {
                return iter.next() instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) {
                Map.Entry entry = (Map.Entry) iter.next();
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }
}

第四步、在我们的请求接口上加上注解:

@InterfaceLog(title = "获取参数")
@PostMapping("/test")
public Map<String, String> test(@RequestBody Map<String, String> parms){
	return parms;
}

最后一步、使用PostMan进行测试,大功告成!

在这里插入图片描述

本次教程到这里就结束了,希望大家多多关注支持(首席摸鱼师 微信同号),持续跟踪最新文章吧~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Raspberry Pi]如何用VNC遠端控制樹莓派(Ubuntu desktop 23.04)?

之前曾利用VMware探索CentOS&#xff0c;熟悉Linux操作系統的指令和配置運作方式&#xff0c;後來在樹莓派價格飛漲的時期&#xff0c;遇到貴人贈送Raspberry Pi 4 model B / 8GB&#xff0c;這下工具到位了&#xff0c;索性跳過樹莓派官方系統(Raspberry Pi OS)&#xff0c;直…

使用 Python 在 NLP 中进行文本预处理

一、说明 自然语言处理 &#xff08;NLP&#xff09; 是人工智能 &#xff08;AI&#xff09; 和计算语言学的一个子领域&#xff0c;专注于使计算机能够理解、解释和生成人类语言。它涉及计算机和自然语言之间的交互&#xff0c;允许机器以对人类有意义和有用的方式处理、分析…

智能电视与win10电脑后续无法实现DLNA屏幕共享

问题背景&#xff1a; 我用的是TCL电视&#xff0c;但是并不是最新&#xff0c;打开的方式是U盘->电脑&#xff0c;各位看自己情况&#xff0c;很多问题都大概率是智能电视问题。 情景假设&#xff1a; 假设你已经完成原先智能电视该有的步骤&#xff0c;通过DLNA&#xf…

前馈神经网络正则化例子

直接看代码&#xff1a; import torch import numpy as np import random from IPython import display from matplotlib import pyplot as plt import torchvision import torchvision.transforms as transforms mnist_train torchvision.datasets.MNIST(root…

链游再进化 Web3版CSGO来袭

过去几年&#xff0c;游戏开发者们一直希望借Web3这个价值流通网络&#xff0c;改造传统游戏的经济系统&#xff0c;将虚拟资产的掌管权交给用户&#xff0c;让资产自由地在市场流通。 Web3游戏发展史上&#xff0c;涌现过CryptoKitties、Axie Infinity两大爆款&#xff0c;但…

爬虫框架- feapder + 爬虫管理系统 - feaplat 的学习简记

文章目录 feapder 的使用feaplat 爬虫管理系统部署 feapder 的使用 feapder是一款上手简单&#xff0c;功能强大的Python爬虫框架 feapder 官方文档 文档写的很详细&#xff0c;可以直接上手。 基本命令&#xff1a; 创建爬虫项目 feapder create -p first-project创建爬虫 …

LRU算法源码实现

算法介绍&#xff1a; 最近最久未使用&#xff08;Least Recently Used LRU&#xff09;算法是⼀种缓存淘汰策略。该算法的思路是&#xff0c;将最近一段时间内最久未使用的页面置换出去。 升级版LRUK算法见 基于LRU-K算法设计本地缓存实现流量削峰https://blog.csdn.net/l…

Deep Learning With Pytorch - 最基本的感知机、贯序模型/分类、拟合

文章目录 如何利用pytorch创建一个简单的网络模型&#xff1f;Step1. 感知机&#xff0c;多层感知机&#xff08;MLP&#xff09;的基本结构Step2. 超平面 ω T ⋅ x b 0 \omega^{T}xb0 ωT⋅xb0 or ω T ⋅ x b \omega^{T}xb ωT⋅xb感知机函数 Step3. 利用感知机进行决策…

虚拟机问题

虚拟机无法识别USB设备 经排查为VMware USB Arbitration Service 没有启动,但是VMware USB Arbitration Service依赖于VMware Workstation Server启动 VMware USB Arbitration Service(VMUSBArbService)是由 VMware 虚拟化软件提供的一个服务,用于协调和管理主机系统上的…

Flink CDC系列之:基于 Flink CDC 构建 MySQL 和 Postgres 的 Streaming ETL

Flink CDC系列之&#xff1a;基于 Flink CDC 构建 MySQL 和 Postgres 的 Streaming ETL 一、技术路线二、MySQL数据库建表三、PostgreSQL数据库建表四、在 Flink SQL CLI 中使用 Flink DDL 创建表五、关联订单数据并且将其写入 Elasticsearch 中六、Kibana查看商品和物流信息的…

leetcode611. 有效三角形的个数(java)

有效三角形的个数 有效三角形的个数排序加二分排序 双指针 上期算法 有效三角形的个数 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 示例 1: 输入: nums [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使…

如何修复损坏的DOC和DOCX格式Word文件?

我们日常办公中&#xff0c;经常用到Word文档。但是有时会遇到word文件损坏、无法打开的情况。这时该怎么办&#xff1f;接着往下看&#xff0c;小编在这里就给大家带来最简单的Word文件修复方法&#xff01; 很多时候DOC和DOCX Word文件会无缘无故的损坏无法打开&#xff0c;一…

【C++ 记忆站】引用

文章目录 一、引用概念二、引用特性1、引用在定义时必须初始化2、一个变量可以有多个引用3、引用一旦引用一个实体&#xff0c;再不能引用其他实体 三、常引用四、使用场景1、做参数1、输出型参数2、大对象传参 2、做返回值1、传值返回2、传引用返回 五、传值、传引用效率比较六…

【C语言】每日一题(找到所有数组中消失的数字)

找到所有数组中消失的数字&#xff0c;链接奉上。 这里简单说一下&#xff0c;因为还没有接触到动态内存&#xff0c;数据结构&#xff0c;所以知识有限&#xff0c;也是尽力而为&#xff0c;结合题库的评论区找到了适合我的解法&#xff0c;以后有机会&#xff0c;会补上各种…

图数据库_Neo4j中文版_Centos7.9安装Neo4j社区版3.5.9_基于jdk1.8---Neo4j图数据库工作笔记0012

由于我们在国内使用啊,具体还是要用中文版滴,找了好久这个neo4j,原来还是有中文版的, https://we-yun.com/doc/neo4j-chs/ 中文版下载地址在这里: 所有版本都在这里了,需要哪个自己去下载就可以了,要注意下载以后,参考: https://we-yun.com/blog/prod-56.html 在这个位置下载…

画质提升+带宽优化,小红书音视频团队端云结合超分落地实践

随着视频业务和短视频播放规模不断增长&#xff0c;小红书一直致力于研究&#xff1a;如何在保证提升用户体验质量的同时降低视频带宽成本&#xff1f; 在近日结束的音视频技术大会「LiveVideoStackCon 2023」上海站中&#xff0c;小红书音视频架构视频图像处理算法负责人剑寒向…

2023.8 - java - 对象和类

public class Dog {String breed;int size;String colour;int age;void eat() {}void run() {}void sleep(){}void name(){} } 一个类可以包含以下类型变量&#xff1a; 局部变量&#xff1a;在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方…

实现Java异步调用的高效方法

文章目录 为什么需要异步调用&#xff1f;Java中的异步编程方式1. 使用多线程2. 使用Java异步框架 异步调用的关键细节结论 &#x1f389;欢迎来到Java学习路线专栏~实现Java异步调用的高效方法 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博…

LabVIEW开发最小化5G系统测试平台

LabVIEW开发最小化5G系统测试平台 由于具有大量存储能力和数据的应用程序的智能手机的激增&#xff0c;当前一代产品被迫提高其吞吐效率。正交频分复用由于其卓越的品质&#xff0c;如单抽头均衡和具有成本效益的实施&#xff0c;现在被广泛用作物理层技术。这些好处是以严格的…

Azure存储访问层

blob数据的热访问层&#xff0c;冷访问层和存档访问层 Azure Blob 存储是一种托管对象存储服务&#xff0c;可用于存储和访问大量非结构化数据&#xff0c;如文本和二进制数据。Azure Blob 存储提供了三个不同层级的访问方式&#xff0c;以适应不同数据的使用模式和成本效益需…