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

news2024/11/29 16:10:50

前言

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

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);
	@Resource
	protected SLogOperatorallService operatorLogAllService;
	@Resource
	protected AppLogsConfiguration appLogsConfiguration;
	@Resource
	private 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();
			// 请求的IP
			String 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.coloradmin.cn/o/986686.html

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

相关文章

Linux系统离线安装RabbitMQ

安装rabbitmq 1、下载安装包 首先进入官网进行安装包的下载&#xff0c;在下载时一定要注意erlong版本和rabbitmq-server版本匹配 rabbitmq版本对应关系&#xff1a;传送门 Erlong下载地址:传送门 rabbitmq-server下载地址:传送门 socat 不同版本 centos7:传送门 cent…

理解项目开发(寺庙小程序)

转载自&#xff1a;历经一年&#xff0c;开发一个寺庙小程序&#xff01; (qq.com) 破防了&#xff01;为方丈开发一款纪念小程序&#xff01; (qq.com) 下面内容转载自&#xff1a;程序员5K为青岛啤酒节开发个点餐系统&#xff01; (qq.com) 看一个人如何完成一个项目的开发…

SpringBoot集成Swagger的使用

Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。目标是使客户端和文件系统作为服务器以同样的速度来更新文件的方法,参数和模型紧密集成到服务器。 Swagger能够在线自动生成 RESTFul接口的文档&#xff0c;同时具备测试接口的功能。 简单…

UDP的可靠性传输2

系列文章目录 第一章 UDP的可靠性传输-理论篇&#xff08;一&#xff09; 第二章 UDP的可靠性传输-理论篇&#xff08;二&#xff09; 文章目录 系列文章目录三、流量控制RTORTT流量控制1.如何控制流量2. 发送方何时在发送数据3.流程图 拥塞控制1.慢启动 总结1.拥塞控制和流量…

基于Streamlit的应用如何通过streamlit-authenticator组件实现用户验证与隔离

Streamlit框架中默认是没有提供用户验证组件的&#xff0c;大家在基于streamlit快速实现web应用服务过程中&#xff0c;不可避免的需要配置该应用的访问范围和权限&#xff0c;即用户群体&#xff0c;一般的做法有两种&#xff0c;一种是通过用户密码验证机制&#xff0c;要求只…

Matlab图像处理-灰度直方图

一幅含有一个与背景明显对比的物体的灰度图像&#xff0c;如下图所示。 有包含双峰的灰度直方图&#xff0c;如下图所示。 两个尖峰对应于物体内部和外部较多数目的点&#xff0c;两峰尖的谷对应于物体边缘附近相对较少数目的点。在这样的情况下&#xff0c;通常使用直方图来确…

点亮LED——第一个IAR工程

文章目录 说明创建工作区创建新工程向工程添加文件设置工程Options编译工程下载程序EWPtool 插件之前的"测试开发板"章节 测试开发板——第一个AutoSAR程序,使用了一个 demo 工程,不管是裸机程序还是AutoSAR程序,那都是别人已经创建好的工程。本节来介绍如何来创建…

gismo程序示例:边长为 8 16 32 的长方体 受均布载荷

文章目录 前言一、一、8*32面 受均布载荷 二、最小的面&#xff08;8*16&#xff09;受均布载荷三、最大的面受均布载荷 前言 只是为方便学习&#xff0c;不做其他用途&#xff0c; 一、 一、8*32面 受均布载荷 /// This is an example of using the linear elasticity solver…

9月7日扒面经

redis缓存用在哪里&#xff0c;用本地缓存行不行? 数据库查询缓存&#xff0c;减小数据源压力&#xff0c;提高响应速度 页面缓存&#xff1a;将页面的渲染结果缓存在Redis中&#xff0c;以减少页面生成的时间和服务器负载。 频繁计算结果缓存&#xff1a;将频繁计算的结果…

配电房能源监测系统

配电房能源监测系统是一种能够实时监测和管理配电房能源消耗的系统&#xff0c;有助于企业更好地管理能源使用&#xff0c;降低能源成本&#xff0c;提高能源利用效率。本文将详细介绍配电房能源监测系统的组成、功能和优点。 一、配电房能源监测系统的组成 配电房能源监测系统…

2023年8大在线渗透测试工具介绍与分析

随着企业参与数字化运动&#xff0c;网络安全已成为大多数董事会讨论的一个重要方面。事实上&#xff0c;最近的一份报告显示&#xff0c;2022 年网络犯罪造成的损失总额达到惊人的 103 亿美元。 这就是在线渗透测试工具在网络安全中受到关注的地方。 今天&#xff0c;我们希…

【LeetCode-中等题】40. 组合总和 II

文章目录 题目方法一&#xff1a;递归回溯去重 题目 本题需要注意的就是去重操作因为nums数组里面的元素可能存在重复&#xff1a; 不重复的版本&#xff1a;【LeetCode-中等题】39. 组合总和 不去重版 方法一&#xff1a;递归回溯去重 参考讲解视频—回溯算法中的去重&#…

从KOOM看Java内存泄漏检测

前面我们了解了LeakCanary和Matrix Resource Canary中内存泄漏的监控和解析&#xff0c;不难看出LeakCanary是只能在线下部署的&#xff0c;主要原因是因为Debug.dumpHprofData执行会冻结整个应用进程&#xff0c;造成应用进程几秒乃至十多秒不能响应的情况&#xff0c;而dump时…

异步编程 - 10 Web Servlet的异步非阻塞处理

文章目录 OverViewServlet概述Servlet 3.0提供的异步处理能力Servlet 3.1提供的非阻塞IO能力Spring Web MVC的异步处理能力基于DeferredResult的异步处理基于Callable实现异步处理 小结 OverView 我们这里主要讨论Servlet3.0规范前的同步处理模型和缺点&#xff0c;Servlet3.0…

Amazon Aurora MySQL 和 Amazon RDS for MySQL 集群故障转移和只读实例扩容时间测试

01 测试背景 Amazon Aurora MySQL 是与 MySQL 兼容的关系数据库&#xff0c;专为云而打造&#xff0c;性能和可用性与商用数据库相当&#xff0c;成本只有其 1/10。 Amazon RDS for MySQL 让您能够在云中更轻松设置、操作和扩展 MySQL 部署。借助 Amazon RDS&#xff0c;您可以…

小白备战大厂算法笔试(三)——栈、队列、双向队列

文章目录 栈栈常用操作栈的实现基于链表的实现基于数组的实现 两种实现对比栈典型应用 队列队列常用操作队列实现基于链表的实现基于数组的实现 队列典型应用 双向队列双向队列常用操作双向队列实现基于双向链表的实现基于数组的实现 双向队列应用 栈 栈是一种遵循先入后出的逻…

Java从入门到精通-流程控制(二)

习题讲解&#xff1a; 上次我们给大家留了一些流程控制的问题&#xff0c;这次给大家分析讲解一下&#xff1a; 条件语句练习&#xff1a; 1.编写Java程序&#xff0c;用于接受用户输入的数字&#xff0c;然后判断它是偶数还是奇数&#xff0c;并输出相应的消息。 import ja…

电表采集器是如何接线的?

随着社会的进步和科技的发展&#xff0c;智能化和自动化已经成为了各个行业的发展趋势。在电力系统领域&#xff0c;电表采集器的应用越来越广泛&#xff0c;它实现了电能数据的远程采集、传输和分析&#xff0c;为电力系统的稳定运行提供了重要的数据支持。那么&#xff0c;电…

广东成人高考报名将于9月14日开始!

截图来自广东省教育考试院官网* 今年的广东成人高考正式报名时间终于确定了&#xff01; 报名时间&#xff1a;2023年 9 月14—20日 准考证打印时间&#xff1a;考前一周左右 考试时间&#xff1a;2023年10月21—22日 录取时间&#xff1a;2023年12 月中上旬 报名条件: …

恒运资本:存储市场有望触底反弹 电子竞技迎催化

昨日&#xff0c;沪指早盘震动下探&#xff0c;午后拉升翻红&#xff1b;深成指、创业板指跌幅收窄&#xff1b;到收盘&#xff0c;沪指涨0.12%报3158.08点&#xff0c;深成指跌0.24%报10515.21点&#xff0c;创业板指跌0.47%报2101.4点&#xff0c;科创50指数涨0.43%&#xff…