springboot使用切面记录接口访问日志

前言

当我们开发和维护一个复杂的应用程序时,了解应用程序的运行情况变得至关重要。特别是在生产环境中,我们需要追踪应用程序的各个方面,以确保它正常运行并能够及时发现潜在的问题。其中之一关键的方面是记录应用程序的接口访问日志。

Spring Boot是一个流行的Java框架,它使得构建强大的、可伸缩的应用程序变得更加容易。在Spring Boot中,我们可以使用切面(Aspect)来轻松地记录接口访问日志,这将帮助我们跟踪应用程序的运行状况,及时发现问题并提供必要的信息,以便更好地监控和调试我们的应用程序。

本篇博客将深入探讨如何使用Spring Boot的切面功能来记录接口访问日志。我们将介绍什么是切面以及它们在应用程序中的作用,然后逐步展示如何创建一个自定义切面来捕获接口请求和响应的信息,最终将这些信息记录到日志中。通过这个过程,我们将能够实现更好的应用程序监控和故障排除,提高开发和维护的效率。

让我们开始探索如何在Spring Boot中利用切面记录接口访问日志,为我们的应用程序增加更多的可观察性和可维护性。

实现方式

1.准备maven依赖

   		<!--切面--><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.9</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.9</version></dependency>

2.日志实体类

我会通过这个实体类,在数据库中创建一张表,来存储每次访问的记录。

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="SLogOperatorall对象", description="")
public class SLogOperatorall implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;private String module;private String type;private String description;private String uri;private String method;private String sessionId;private String requestId;private String params;private String createBy;@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")private String createDate;private String beanName;private String beanMethod;@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")private String beginTime;@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")private String endTime;private String exceptionCode;private String exceptionDetail;private long requestTime;private String result;private String url;private String osInfo;private String browserInfo;private String requestParams;}
DROP TABLE IF EXISTS `s_log_operatorall`;
CREATE TABLE `s_log_operatorall`  (`id` int(32) NOT NULL AUTO_INCREMENT,`module` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`uri` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`method` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`session_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`request_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`params` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`create_by` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`create_date` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`bean_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`bean_method` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`begin_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`end_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`exception_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`exception_detail` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`request_time` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`result` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,`url` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`os_info` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`browser_info` varchar(1024) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`request_params` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 230 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;SET FOREIGN_KEY_CHECKS = 1;

3.注解

import java.lang.annotation.*;@Target({ElementType.PARAMETER, ElementType.METHOD})    
@Retention(RetentionPolicy.RUNTIME)    
@Documented    
public  @interface SystemControllerNoLog {    
}  

4.ignoringUrls

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;@Configuration
@ConfigurationProperties(prefix = "applogs")
public class AppLogsConfiguration {private String ignoringUrls = "/;/sitemids;/sitemesh;";public String getIgnoringUrls() {return ignoringUrls;}public void setIgnoringUrls(String ignoringUrls) {this.ignoringUrls = ignoringUrls;}
}

5.日志实现类

import com.alibaba.fastjson.JSON;
import com.zl.sys.controller.config.jwt.JwtConfig;
import com.zl.sys.entity.SLogOperatorall;
import com.zl.sys.service.SLogOperatorallService;
import com.zl.utils.DateUtils;
import com.zl.utils.RequestUtil;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Map;@SuppressWarnings("all")
@Aspect
@Component
public class ControllerLogAspect {private static final Logger logger = LoggerFactory.getLogger(ControllerLogAspect.class);@Resourceprotected SLogOperatorallService operatorLogAllService;@Resourceprotected AppLogsConfiguration appLogsConfiguration;@Resourceprivate JwtConfig jwtConfig;private static ThreadLocal<SLogOperatorall> tlocal = new ThreadLocal<SLogOperatorall>();@Pointcut("(@annotation(org.springframework.web.bind.annotation.RequestMapping) || @annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.PostMapping)) && !@annotation(com.zl.sys.controller.config.log.SystemControllerNoLog)")public void controllerAspect() {}/*** 前置通知 用于拦截Controller层记录用户的操作* * @param joinPoint*            切点*/@Before("controllerAspect()")public void doBefore(JoinPoint joinPoint) {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String uri = request.getRequestURI().replaceFirst(request.getContextPath(), "");if (appLogsConfiguration.getIgnoringUrls().indexOf(uri + ";") >= 0) {tlocal.set(null);return;}if (StringUtils.isNotEmpty(request.getRequestURL())){String url = request.getRequestURL().toString();String userName = jwtConfig.getUserName(request);String sessionId = request.getSession().getId();String ip = getIpAddr(request);String method = request.getMethod();// 请求的IPString params = "";if ("POST".equals(method)) {Object[] paramsArray = joinPoint.getArgs();params = argsArrayToString(paramsArray);} else {Map<?, ?> paramsMap = (Map<?, ?>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);params = paramsMap.toString();}try {SLogOperatorall log = new SLogOperatorall();log.setUrl(url);log.setUri(uri);log.setMethod(method);log.setBeanName(joinPoint.getTarget().getClass().getName());log.setBeanMethod(joinPoint.getSignature().getName() + "");log.setRequestId(ip);log.setExceptionCode(null);log.setExceptionDetail(null);log.setParams(params);log.setBeginTime(DateUtils.getISODateTime(new Date()));log.setRequestTime(System.currentTimeMillis());log.setSessionId(sessionId);log.setCreateBy(userName);log.setCreateDate(DateUtils.getDateTime());log.setOsInfo(RequestUtil.getOsInfo(request));log.setBrowserInfo(RequestUtil.getBrowserInfo(request));log.setRequestParams(JSON.toJSON(request.getParameterMap()).toString());tlocal.set(log);} catch (Exception e) {logger.error("==前置通知异常==");logger.error("异常信息:{}", e.getMessage());}}}@AfterReturning(returning = "result", pointcut = "controllerAspect()")public void doAfterReturning(Object result) {try {// 处理完请求,返回内容SLogOperatorall optLog = tlocal.get();if (optLog != null) {String resultMsg = ObjectUtils.toString(result, "");if (StringUtils.isNotEmpty(resultMsg)){String resultString = getResultString(result);if (resultString.length()>6000){optLog.setResult("结果集长度过大");}else {optLog.setResult(resultString);}long beginTime = optLog.getRequestTime();long requestTime = (System.currentTimeMillis() - beginTime);optLog.setRequestTime(requestTime);optLog.setEndTime(DateUtils.getISODateTime(new Date()));logger.info("Uri: " + optLog.getUri() + "请求耗时:" + optLog.getRequestTime());operatorLogAllService.save(optLog);}}} catch (Exception e) {logger.error("***操作请求日志记录失败doAfterReturning()***", e);}}@AfterThrowing(throwing = "ex", pointcut = "controllerAspect()")public void doAfterThrowing(Throwable ex) {try {// 处理完请求,返回内容SLogOperatorall optLog = tlocal.get();if (optLog!=null){long beginTime = optLog.getRequestTime();long requestTime = (System.currentTimeMillis() - beginTime);optLog.setRequestTime(requestTime);optLog.setExceptionCode("");optLog.setExceptionDetail(ex.getMessage());optLog.setEndTime(DateUtils.getISODateTime(new Date()));logger.info("Uri: " + optLog.getUri() + "请求异常————耗时:" + optLog.getRequestTime());operatorLogAllService.save(optLog);}} catch (Exception e) {logger.error("***操作请求日志记录失败doAfterReturning()***", e);}}/*** 获取登录用户远程主机ip地址* * @param request* @return*/private String getIpAddr(HttpServletRequest request) {return RequestUtil.getIp(request);}/*** 请求参数拼装* * @param paramsArray* @return*/private String argsArrayToString(Object[] paramsArray) {String params = "";if (paramsArray != null && paramsArray.length > 0) {for (int i = 0; i < paramsArray.length; i++) {Object obj = paramsArray[i];if (obj instanceof ServletRequest) {} else if (obj instanceof HttpServletResponse) {} else if (obj instanceof MultipartFile) {} else if (obj instanceof Model) {} else if (obj instanceof ModelAndView) {} else {try {if (obj != null && !"".equals(obj)) {Object jsonObj = JSON.toJSON(obj);params += jsonObj.toString() + ";";}} catch (Exception e) {e.printStackTrace();}}}}return params.trim();}private String getResultString(Object result) {if (result == null) {}if (result instanceof String) {return (String) result;} else {try {Object jsonObj = JSON.toJSON(result);return jsonObj.toString();} catch (Exception e) {e.printStackTrace();}}return null;}
}

6.效果展示

6.1每个接口的请求耗时
在这里插入图片描述
6.2数据展示
在这里插入图片描述

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

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

相关文章

函数相关概念

4.函数 1.函数的概念 1.什么是函数? 把特点的代码片段,抽取成为独立运行的实体 2.使用函数的好处1.重复使用,提供效率2.提高代码的可读性3.有利用程序的维护 3.函数的分类1.内置函数(系统函数)已经提高的alert(); prompt();confirm();print()document.write(),console.log()…

http和https区别,第三方证书如何保证服务器可信

目录 HTTP和HTTPS有以下区别&#xff1a; 第三方证书如何保证服务器的可信性 需要注意哪些方面 可能遇到什么问题 随着互联网技术的不断发展&#xff0c;数据传输的安全性越来越受到人们的关注。HTTP和HTTPS是两种常用的网络协议&#xff0c;它们之间的主要区别在于数据传输…

【图文并茂】c++介绍之队列

1.1队列的定义 队列&#xff08;queue&#xff09;简称队&#xff0c;它也是一种操作受限的线性表&#xff0c;其限制为仅允许在表的一端进行插入操作&#xff0c;而在表的另一端进行删除操作 一些基础概念&#xff1a; 队尾&#xff08;rear&#xff09; &#xff1a;进行插…

CloudQuery X PolarDB:让数据库管理更简单

前言&#xff1a;8 月 15 日&#xff0c;CloudQuery 数据操作管控平台与阿里云 PolarDB 数据库管理软件&#xff0c;完成产品集成认证测试。也在以下功能上完善了用户使用 PolarDB 的体验&#xff0c;使数据库的管理更加安全高效。 支持在 CloudQuery 中创建连接&#xff0c;便…

数据接口工程对接BI可视化大屏(一)

文章目录 第1章 案例概述1.1 案例目标1.2 BI最终效果1.2.1 PC端显示效果1.2.2 移动端显示效果 后记 第1章 案例概述 1.1 案例目标 此项目以常见的手机零售BI场景为例&#xff0c;介绍如何编写数据接口工程对接BI可视化大屏。 如何从当前常见的主流大数据场景中为后台程序推送…

数据结构入门-13-图

文章目录 一、图的概述1.1 图论的作用1.2 图的分类1.2.1 无向图1.2.2 有向图1.2.3 无权图1.2.4 有劝图 1.3 图的基本概念 二、树的基本表示2.1 邻接矩阵2.1.1 邻接矩阵 表示图2.1.2 邻接矩阵的复杂度 2.2 邻接表2.2.1 邻接表的复杂度2.2.2 邻接表By哈希表 三、图的深度优先遍历…

sentinel加密狗使用及规则配置

Sentinel加密狗是一种硬件加密设备&#xff0c;用于保护软件应用程序免受未经授权的访问和复制。它可以提供软件许可管理、访问控制和数据保护等功能。下面是Sentinel加密狗的使用及规则配置的相关介绍。 Sentinel加密狗的使用 插入加密狗&#xff1a;将Sentinel加密狗插入计算…

C51智能小车(循迹、跟随、避障、测速、蓝牙、wifie、4g、语音识别)总结

目录 1.电机模块开发 1.1 让小车动起来 1.2 串口控制小车方向 1.3 如何进行小车PWM调速 1.4 PWM方式实现小车转向 2.循迹小车 2.1 循迹模块使用 2.2 循迹小车原理 2.3 循迹小车核心代码 3.跟随/避障小车 3.1 红外壁障模块分析​编辑 3.2 跟随小车的原理 3.3 跟随小…

Leetcode.174 地下城游戏

题目链接 Leetcode.174 地下城游戏 hard 题目描述 恶魔们抓住了公主并将她关在了地下城 d u n g e o n dungeon dungeon 的 右下角 。地下城是由 m x n 个房间组成的二维网格。我们英勇的骑士最初被安置在 左上角 的房间里&#xff0c;他必须穿过地下城并通过对抗恶魔来拯救公…

【高阶产品策略】设计有效的AB测试

文章目录 1、A/B测试概述2、A/B测试实施过程3、A/B测试中需要注意的地方4、从一个案例中看A/B测试 1、A/B测试概述 2、A/B测试实施过程 3、A/B测试中需要注意的地方 4、从一个案例中看A/B测试

编写中间件以用于 Express 应用程序

概述 中间件函数能够访问请求对象 (req)、响应对象 (res) 以及应用程序的请求/响应循环中的下一个中间件函数。下一个中间件函数通常由名为 next 的变量来表示。 中间件函数可以执行以下任务&#xff1a; 执行任何代码。对请求和响应对象进行更改。结束请求/响应循环。调用堆…

pytorch学习——循环神经网络RNN讲解及其实现

参考书籍&#xff1a;8.6. 循环神经网络的简洁实现 — 动手学深度学习 2.0.0 documentation 参考视频&#xff1a;54 循环神经网络 RNN【动手学深度学习v2】_哔哩哔哩_bilibili 一.介绍 循环神经网络RNN&#xff08;Recurrent Neural Network &#xff09;是一类广泛应用于序列…

stm32之30.DMA

DMA&#xff08;硬件加速方法&#xff09;一般用于帮运比较大的数据&#xff08;如&#xff1a;摄像头数据图像传输&#xff09;&#xff0c;寄存器-》DMA-》RAM 或者 RAM-》DMA-》寄存器提高CPU的工作效率 源码-- #include "myhead.h" #include "adc.h"#…

javaee之黑马乐优商城2

简单分析一下商品分类表的结构 先来说一下分类表与品牌表之间的关系 再来说一下分类表和品牌表与商品表之间的关系 面我们要开始就要创建sql语句了嘛&#xff0c;这里我们分析一下字段 用到的数据库是heima->tb_category这个表 现在去数据库里面创建好这张表 下面我们再去编…

有向图和无向图的表示方式(邻接矩阵,邻接表)

目录 一.邻接矩阵 1.无向图​编辑 2.有向图 补充&#xff1a;网&#xff08;有权图&#xff09;的邻接矩阵表示法 二.邻接表 1.无向图 2.有向图 三.邻接矩阵与邻接表的关系 一.邻接矩阵 1.无向图 &#xff08;1&#xff09;对角线上是每一个顶点与自身之间的关系&…

线性空间和线性变化

目录 考点一、线性空间的基与维数 1、线性空间 2、基底 3、子空间&#xff08;线性子空间&#xff09; ​编辑4、生成子空间 &#xff08;1&#xff09;、v1 n v2 &#xff08;2&#xff09;、v1 v2 5、求和子空间的方法 6、维数定理 7、例题 &#xff08;1&#xf…

HCIA自学笔记01-冲突域

共享式网络&#xff08;用同一根同轴电缆通信&#xff09;中可能会出现信号冲突现象。 如图是一个10BASE5以太网&#xff0c;每个主机都是用同一根同轴电缆来与其它主机进行通信&#xff0c;因此&#xff0c;这里的同轴电缆又被称为共享介质&#xff0c;相应的网络被称为共享介…

MyBatis-Plus学习笔记总结

一、查询 构造器分为QueryWrapper和LambdaQueryWrapper 创建实体类User package com.system.mybatisplus.model;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.…

JDK8的 ConcurrentHashMap 源码分析

目录 1. 导读 2. ConcurrentHashMap 成员变量解读 3. ConcurrentHashMap 初始化 3.1 ConcurrentHashMap 无参构造源码解读 3.2 ConcurrentHashMap 带参构造源码解读 3.3 tableSizeFor 方法作用解读 3.4 ConcurrenthashMap初始化总结 4. ConcurrentHashMap 添加元素方法…

springboot之一:配置文件(内外部配置优先顺序+properties、xml、yaml基础语法+profile动态切换配置、激活方式)

配置的概念&#xff1a; Spring Boot是基于约定的&#xff0c;所以很多配置都有默认值&#xff0c;但如果想使用自己的配置替换默认配置的话&#xff0c;就可以使用application.properties或者application.yml(application.yaml)进行配置。 注意配置文件的命名必须是applicat…