当前位置: 首页 > news >正文

从零开始搭建Springboot项目脚手架4:保存操作日志

目的:通过AOP切面,统一记录接口的访问日志

1、加maven依赖

2、 增加日志类RequestLog

3、 配置AOP切面,把请求前的request、返回的response一起记录

package com.template.common.config;import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.template.common.domain.model.RequestLog;
import eu.bitwalker.useragentutils.UserAgent;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
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 java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;/*** 使用AOP切面记录请求日志*/
@Aspect
@Component
@Slf4j
public class RequestLogger {/*** 切入点*/@Pointcut("execution(public * com.template.api.controller.*.*Controller.*(..))")public void controllerPointcut() {// 本方法不会被执行,只是作为一个标记,与被注解的服务方法进行关联// 切入点为controller的public方法,也就是各个请求路径}/*** 环绕操作** @param joinPoint 切入点* @return 原方法返回值* @throws Throwable 异常信息*/@Around("controllerPointcut()")public Object controllerLogging(ProceedingJoinPoint joinPoint) throws Throwable {// 开始打印请求日志ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();// 计算各个日志参数long startTime = System.currentTimeMillis();Thread currentThread = Thread.currentThread();String userHost = extractUserHost(request);String userAgentString = request.getHeader("User-Agent");UserAgent userAgent = UserAgent.parseUserAgentString(userAgentString);Map<String, Object> requestParams = extractRequestParams(joinPoint);Object responseResult = joinPoint.proceed();Signature controllerMethod = joinPoint.getSignature();String classMethod = controllerMethod.getDeclaringTypeName() + "." + controllerMethod.getName();final RequestLog requestLog = RequestLog.builder().userHost(userHost).userOs(userAgent.getOperatingSystem().getName()).userBrowser(userAgent.getBrowser().getName()).userAgent(userAgentString).requestUrl(request.getRequestURL().toString()).requestMethod(request.getMethod()).requestParams(requestParams).responseResult(responseResult).classMethod(classMethod).threadId(Long.toString(currentThread.getId())).threadName(currentThread.getName()).costMillisecond(System.currentTimeMillis() - startTime).build();// 打印日志log.info("Request Log Info : {}", JSONUtil.toJsonStr(requestLog));return responseResult;}/*** 获取用户Host地址** @param request 请求对象* @return 用户Host*/private static String extractUserHost(HttpServletRequest request) {String xRealIp = request.getHeader("X-Real-IP");if (StrUtil.isNotEmpty(xRealIp) && !"unknown".equalsIgnoreCase(xRealIp)) {return xRealIp;}String xForwardedFor = request.getHeader("x-forwarded-for");if (StrUtil.isNotEmpty(xForwardedFor) && !"unknown".equalsIgnoreCase(xForwardedFor)) {return xForwardedFor;}String proxyClientIp = request.getHeader("Proxy-Client-IP");if (StrUtil.isNotEmpty(proxyClientIp) && !"unknown".equalsIgnoreCase(proxyClientIp)) {return proxyClientIp;}String wlProxyClientIp = request.getHeader("WL-Proxy-Client-IP");if (StrUtil.isNotEmpty(wlProxyClientIp) && !"unknown".equalsIgnoreCase(wlProxyClientIp)) {return wlProxyClientIp;}String httpClientIp = request.getHeader("HTTP_CLIENT_IP");if (StrUtil.isNotEmpty(httpClientIp) && !"unknown".equalsIgnoreCase(httpClientIp)) {return httpClientIp;}String httpxForwardedFor = request.getHeader("HTTP_X_FORWARDED_FOR");if (StrUtil.isNotEmpty(httpxForwardedFor) && !"unknown".equalsIgnoreCase(httpxForwardedFor)) {return httpxForwardedFor;}String remoteAddr = request.getRemoteAddr();if (!"127.0.0.1".equals(remoteAddr) && !"0:0:0:0:0:0:0:1".equals(remoteAddr)) {return remoteAddr;}//根据网卡取本机IP地址try {return InetAddress.getLocalHost().getHostAddress();} catch (UnknownHostException e) {log.error("getIpAddress exception:", e);}return xRealIp;}/*** 获取请求参数** @param joinPoint 切入点* @return 请求参数*/private Map<String, Object> extractRequestParams(ProceedingJoinPoint joinPoint) {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();final String[] parameterNames = methodSignature.getParameterNames();final Object[] parameterValues = joinPoint.getArgs();if (ArrayUtil.isEmpty(parameterNames) || ArrayUtil.isEmpty(parameterValues)) {return Collections.emptyMap();}if (parameterNames.length != parameterValues.length) {log.warn("{}方法参数名和参数值数量不一致", methodSignature.getName());return Collections.emptyMap();}Map<String, Object> requestParams = new HashMap<>();for (int i = 0; i < parameterNames.length; i++) {requestParams.put(parameterNames[i], parameterValues[i]);}return requestParams;}}

4、查看效果

 

 也可以把日志写入到数据库里面,自由发挥。

http://www.lryc.cn/news/356189.html

相关文章:

  • 持续总结中!2024年面试必问 20 道 Rocket MQ面试题(一)
  • 生成式AI的GPU网络技术架构
  • 旅游卡在哪里拿货?千益畅行旅游卡源头
  • 代码随想录算法训练营第四十一天| 509. 斐波那契数 、70. 爬楼梯 、746. 使用最小花费爬楼梯
  • Ribbon负载均衡(自己总结的)
  • Leetcode 力扣92. 反转链表 II (抖音号:708231408)
  • OSI七层模型和TCP/IP四层模型的区别
  • 在虚拟机上安装MySQL和Hive
  • Vue 2 和 Vue 3 中同步和异步
  • ssm150旅游网站的设计与实现+jsp
  • 【加密与解密(第四版)】第十四章笔记
  • 鸿蒙系统和安卓系统通过termux搭建Linux系统—Centos
  • 数据结构的希尔排序(c语言版)
  • 使用Node.js搭建服务器
  • 网络编程——多进程的服务器
  • 代码随想录算法训练营第二十一天| 530. 二叉搜索树的最小绝对差、501. 二叉搜索树中的众数、236. 二叉树的最近公共祖先
  • 【面试】JDK和JVM是什么关系?
  • 旺店通与金蝶云星空 就应该这样集成打通
  • linux开发之设备树
  • DQL(数据查询)
  • LeetCode 2951.找出峰值:模拟(遍历)
  • 软考结束。有什么要说的
  • Matlab读取Swarm球谐系数,并绘制EWH全球格网图(存在疑问)
  • Vue集成Iframe
  • Android Studio 所有历史版本下载
  • 5.27作业
  • 微服务架构下的‘黑带’安全大师:Spring Cloud Security全攻略!
  • Py列表(list)
  • 黑马es0-1实现自动补全功能
  • react通过上下文深入传递数据