前言

大型完善项目中肯定是需要一个全局日志拦截,记录每次接口访问相关信息,包括:

访问ip,访问设备,请求参数,响应结果,响应时间,开始请求时间,访问接口描述,访问的用户,接口地址,请求类型,便于项目的调试追踪

整合日志

SpringBoot已经帮我们做了日志整合,在它的父pom项中

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-parent</artifactId>
  4. <version>2.4.7</version>
  5. <relativePath/> <!-- lookup parent from repository -->
  6. </parent>

我们点进去看到还有父pom项

  1. <parent>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-dependencies</artifactId>
  4. <version>2.4.7</version>
  5. </parent>

spring-boot-dependencies帮我自动整合所有jar包版本,和内置很多启动项start,这是SpringBoot使用起来不需要配置那么多jar包依赖关系的本质,点进去我们看到了,所有spring,springmvc所需要依赖,和jar版本



最终我们找到了日志依赖所需jar包,所以我们直接使用就行,不需要额外引入

配置日志

  1. ######日志配置######
  2. # 日志文件
  3. logging.config=classpath:logback-spring.xml
  4. #日志包级别
  5. #logging.level.root=info
  6. logging.level.cn.soboys.blogapi=info
  7. #日志存储路径
  8. logging.file.path=/Users/xiangyong/selfProject/project/blog-api/log

日志文件配置

这里日志名字logback-spring.xml是SpringBoot独有,这么写spring boot可以为它添加一些spring boot特有的配置项

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration scan="true" scanPeriod="60 seconds" debug="false">
  3. <contextName>shop-api</contextName>
  4. <!--定义日志文件的存储地址 从springboot配置文件中获取路径-->
  5. <springProperty scope="context" name="LOG_PATH" source="logging.file.path"/>
  6. <!--springboot配置文件中获取日志级别-->
  7. <springProperty scope="context" name="LOG_LEVEL" source="logging.level.root"/>
  8. <!-- <property name="log.path" value="log" />-->
  9. <property name="log.maxHistory" value="15" />
  10. <property name="log.colorPattern" value="%magenta(%d{yyyy-MM-dd HH:mm:ss}) %highlight(%-5level) %yellow(%thread) %green(%logger) %msg%n"/>
  11. <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5level %thread %logger %msg%n"/>
  12. <!--输出到控制台-->
  13. <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
  14. <encoder>
  15. <pattern>${log.colorPattern}</pattern>
  16. </encoder>
  17. </appender>
  18. <!--输出到文件-->
  19. <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
  20. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  21. <fileNamePattern>${LOG_PATH}/info/info.%d{yyyy-MM-dd}.log</fileNamePattern>
  22. <MaxHistory>${log.maxHistory}</MaxHistory>
  23. </rollingPolicy>
  24. <encoder>
  25. <pattern>${log.pattern}</pattern>
  26. </encoder>
  27. <filter class="ch.qos.logback.classic.filter.LevelFilter">
  28. <level>INFO</level>
  29. <onMatch>ACCEPT</onMatch>
  30. <onMismatch>DENY</onMismatch>
  31. </filter>
  32. </appender>
  33. <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
  34. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  35. <fileNamePattern>${LOG_PATH}/error/error.%d{yyyy-MM-dd}.log</fileNamePattern>
  36. </rollingPolicy>
  37. <encoder>
  38. <pattern>${log.pattern}</pattern>
  39. </encoder>
  40. <filter class="ch.qos.logback.classic.filter.LevelFilter">
  41. <level>ERROR</level>
  42. <onMatch>ACCEPT</onMatch>
  43. <onMismatch>DENY</onMismatch>
  44. </filter>
  45. </appender>
  46. <root level="debug">
  47. <appender-ref ref="console" />
  48. </root>
  49. <root level="info">
  50. <appender-ref ref="file_info" />
  51. <appender-ref ref="file_error" />
  52. </root>
  53. </configuration>

这里我把日志分成两个日志文件,一个错误日志,一个信息日志,按照明天一个日志文件方式

全局日志记录

这里我们基于aop切面进行请求拦截,记录所有请求相关信息

  1. package cn.soboys.core;
  2. import lombok.Data;
  3. /**
  4. * @author kenx
  5. * @version 1.0
  6. * @date 2021/6/18 18:48
  7. * 日志信息
  8. */
  9. @Data
  10. public class LogSubject {
  11. /**
  12. * 操作描述
  13. */
  14. private String description;
  15. /**
  16. * 操作用户
  17. */
  18. private String username;
  19. /**
  20. * 操作时间
  21. */
  22. private String startTime;
  23. /**
  24. * 消耗时间
  25. */
  26. private String spendTime;
  27. /**
  28. * URL
  29. */
  30. private String url;
  31. /**
  32. * 请求类型
  33. */
  34. private String method;
  35. /**
  36. * IP地址
  37. */
  38. private String ip;
  39. /**
  40. * 请求参数
  41. */
  42. private Object parameter;
  43. /**
  44. * 请求返回的结果
  45. */
  46. private Object result;
  47. /**
  48. * 城市
  49. */
  50. private String city;
  51. /**
  52. * 请求设备信息
  53. */
  54. private String device;
  55. }

这里用到了aop 所以要导入aop相关包

  1. <!--aop 切面-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-aop</artifactId>
  5. </dependency>

编写切面类拦截对应的controller请求

  1. package cn.soboys.core;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.reflect.MethodSignature;
  4. import java.lang.reflect.Method;
  5. /**
  6. * @author kenx
  7. * @version 1.0
  8. * @date 2021/6/18 14:52
  9. * 切面
  10. */
  11. public class BaseAspectSupport {
  12. public Method resolveMethod(ProceedingJoinPoint point) {
  13. MethodSignature signature = (MethodSignature)point.getSignature();
  14. Class<?> targetClass = point.getTarget().getClass();
  15. Method method = getDeclaredMethod(targetClass, signature.getName(),
  16. signature.getMethod().getParameterTypes());
  17. if (method == null) {
  18. throw new IllegalStateException("无法解析目标方法: " + signature.getMethod().getName());
  19. }
  20. return method;
  21. }
  22. private Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
  23. try {
  24. return clazz.getDeclaredMethod(name, parameterTypes);
  25. } catch (NoSuchMethodException e) {
  26. Class<?> superClass = clazz.getSuperclass();
  27. if (superClass != null) {
  28. return getDeclaredMethod(superClass, name, parameterTypes);
  29. }
  30. }
  31. return null;
  32. }
  33. }

日志记录切面GlobalLogAspect

  1. package cn.soboys.core;
  2. import cn.hutool.core.date.DateUtil;
  3. import cn.hutool.core.date.TimeInterval;
  4. import cn.hutool.json.JSONUtil;
  5. import cn.soboys.core.utils.HttpContextUtil;
  6. import io.swagger.annotations.ApiOperation;
  7. import lombok.extern.slf4j.Slf4j;
  8. import org.aspectj.lang.ProceedingJoinPoint;
  9. import org.aspectj.lang.annotation.Around;
  10. import org.aspectj.lang.annotation.Aspect;
  11. import org.aspectj.lang.annotation.Pointcut;
  12. import org.springframework.stereotype.Component;
  13. import org.springframework.web.bind.annotation.RequestBody;
  14. import org.springframework.web.bind.annotation.RequestParam;
  15. import javax.servlet.http.HttpServletRequest;
  16. import java.lang.reflect.Method;
  17. import java.lang.reflect.Parameter;
  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. /**
  23. * @author kenx
  24. * @version 1.0
  25. * @date 2021/6/18 15:22
  26. * 全局日志记录器
  27. */
  28. @Slf4j
  29. @Aspect
  30. @Component
  31. public class GlobalLogAspect extends BaseAspectSupport {
  32. /**
  33. * 定义切面Pointcut
  34. */
  35. @Pointcut("execution(public * cn.soboys.blogapi.controller.*.*(..))")
  36. public void log() {
  37. }
  38. /**
  39. * 环绕通知
  40. *
  41. * @param joinPoint
  42. * @return
  43. */
  44. @Around("log()")
  45. public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  46. LogSubject logSubject = new LogSubject();
  47. //记录时间定时器
  48. TimeInterval timer = DateUtil.timer(true);
  49. //执行结果
  50. Object result = joinPoint.proceed();
  51. logSubject.setResult(result);
  52. //执行消耗时间
  53. String endTime = timer.intervalPretty();
  54. logSubject.setSpendTime(endTime);
  55. //执行参数
  56. Method method = resolveMethod(joinPoint);
  57. logSubject.setParameter(getParameter(method, joinPoint.getArgs()));
  58. HttpServletRequest request = HttpContextUtil.getRequest();
  59. // 接口请求时间
  60. logSubject.setStartTime(DateUtil.now());
  61. //请求链接
  62. logSubject.setUrl(request.getRequestURL().toString());
  63. //请求方法GET,POST等
  64. logSubject.setMethod(request.getMethod());
  65. //请求设备信息
  66. logSubject.setDevice(HttpContextUtil.getDevice());
  67. //请求地址
  68. logSubject.setIp(HttpContextUtil.getIpAddr());
  69. //接口描述
  70. if (method.isAnnotationPresent(ApiOperation.class)) {
  71. ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
  72. logSubject.setDescription(apiOperation.value());
  73. }
  74. String a = JSONUtil.toJsonPrettyStr(logSubject);
  75. log.info(a);
  76. return result;
  77. }
  78. /**
  79. * 根据方法和传入的参数获取请求参数
  80. */
  81. private Object getParameter(Method method, Object[] args) {
  82. List<Object> argList = new ArrayList<>();
  83. Parameter[] parameters = method.getParameters();
  84. Map<String, Object> map = new HashMap<>();
  85. for (int i = 0; i < parameters.length; i++) {
  86. //将RequestBody注解修饰的参数作为请求参数
  87. RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
  88. //将RequestParam注解修饰的参数作为请求参数
  89. RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
  90. String key = parameters[i].getName();
  91. if (requestBody != null) {
  92. argList.add(args[i]);
  93. } else if (requestParam != null) {
  94. map.put(key, args[i]);
  95. } else {
  96. map.put(key, args[i]);
  97. }
  98. }
  99. if (map.size() > 0) {
  100. argList.add(map);
  101. }
  102. if (argList.size() == 0) {
  103. return null;
  104. } else if (argList.size() == 1) {
  105. return argList.get(0);
  106. } else {
  107. return argList;
  108. }
  109. }
  110. }
  111. package cn.soboys.core;
  112. import cn.hutool.core.date.DateUtil;
  113. import cn.hutool.core.date.TimeInterval;
  114. import cn.hutool.json.JSONUtil;
  115. import cn.soboys.core.utils.HttpContextUtil;
  116. import io.swagger.annotations.ApiOperation;
  117. import lombok.extern.slf4j.Slf4j;
  118. import org.aspectj.lang.ProceedingJoinPoint;
  119. import org.aspectj.lang.annotation.Around;
  120. import org.aspectj.lang.annotation.Aspect;
  121. import org.aspectj.lang.annotation.Pointcut;
  122. import org.springframework.stereotype.Component;
  123. import org.springframework.web.bind.annotation.RequestBody;
  124. import org.springframework.web.bind.annotation.RequestParam;
  125. import javax.servlet.http.HttpServletRequest;
  126. import java.lang.reflect.Method;
  127. import java.lang.reflect.Parameter;
  128. import java.util.ArrayList;
  129. import java.util.HashMap;
  130. import java.util.List;
  131. import java.util.Map;
  132. /**
  133. * @author kenx
  134. * @version 1.0
  135. * @date 2021/6/18 15:22
  136. * 全局日志记录器
  137. */
  138. @Slf4j
  139. @Aspect
  140. @Component
  141. public class GlobalLogAspect extends BaseAspectSupport {
  142. /**
  143. * 定义切面Pointcut
  144. */
  145. @Pointcut("execution(public * cn.soboys.blogapi.controller.*.*(..))")
  146. public void log() {
  147. }
  148. /**
  149. * 环绕通知
  150. *
  151. * @param joinPoint
  152. * @return
  153. */
  154. @Around("log()")
  155. public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  156. LogSubject logSubject = new LogSubject();
  157. //记录时间定时器
  158. TimeInterval timer = DateUtil.timer(true);
  159. //执行结果
  160. Object result = joinPoint.proceed();
  161. logSubject.setResult(result);
  162. //执行消耗时间
  163. String endTime = timer.intervalPretty();
  164. logSubject.setSpendTime(endTime);
  165. //执行参数
  166. Method method = resolveMethod(joinPoint);
  167. logSubject.setParameter(getParameter(method, joinPoint.getArgs()));
  168. HttpServletRequest request = HttpContextUtil.getRequest();
  169. // 接口请求时间
  170. logSubject.setStartTime(DateUtil.now());
  171. //请求链接
  172. logSubject.setUrl(request.getRequestURL().toString());
  173. //请求方法GET,POST等
  174. logSubject.setMethod(request.getMethod());
  175. //请求设备信息
  176. logSubject.setDevice(HttpContextUtil.getDevice());
  177. //请求地址
  178. logSubject.setIp(HttpContextUtil.getIpAddr());
  179. //接口描述
  180. if (method.isAnnotationPresent(ApiOperation.class)) {
  181. ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
  182. logSubject.setDescription(apiOperation.value());
  183. }
  184. String a = JSONUtil.toJsonPrettyStr(logSubject);
  185. log.info(a);
  186. return result;
  187. }
  188. /**
  189. * 根据方法和传入的参数获取请求参数
  190. */
  191. private Object getParameter(Method method, Object[] args) {
  192. List<Object> argList = new ArrayList<>();
  193. Parameter[] parameters = method.getParameters();
  194. Map<String, Object> map = new HashMap<>();
  195. for (int i = 0; i < parameters.length; i++) {
  196. //将RequestBody注解修饰的参数作为请求参数
  197. RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
  198. //将RequestParam注解修饰的参数作为请求参数
  199. RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
  200. String key = parameters[i].getName();
  201. if (requestBody != null) {
  202. argList.add(args[i]);
  203. } else if (requestParam != null) {
  204. map.put(key, args[i]);
  205. } else {
  206. map.put(key, args[i]);
  207. }
  208. }
  209. if (map.size() > 0) {
  210. argList.add(map);
  211. }
  212. if (argList.size() == 0) {
  213. return null;
  214. } else if (argList.size() == 1) {
  215. return argList.get(0);
  216. } else {
  217. return argList;
  218. }
  219. }
  220. }

这样我们在每次请求接口的时候都会记录请求的信息:

详细日志学习SpringBoot日志使用请参考我这两篇文章

SpringBoot日志详细使用和配置

JAVA 中日志的记录于使用

关注公众号猿小叔获取更多干货分享

个人博客开发之blog-api 项目全局日志拦截记录的更多相关文章

  1. 个人博客开发之xadmin 布局和后台样式

    项目源码下载:http://download.vhosts.cn 一. xadmin 后台配置注册信息 1. 在apps 的blogs 和 users 两个app中添加adminx.py文件 vim ...

  2. 个人博客开发之blog-api项目统一结果集api封装

    前言 由于返回json api 格式接口,所以我们需要通过java bean封装一个统一数据返回格式,便于和前端约定交互, 状态码枚举ResultCode package cn.soboys.core ...

  3. 个人博客开发之blog-api 项目整合JWT实现token登录认证

    前言 现在前后端分离,基于session设计到跨越问题,而且session在多台服器之前同步问题,肯能会丢失,所以倾向于使用jwt作为token认证 json web token 导入java-jwt ...

  4. SpringBoot博客开发之AOP日志处理

    日志处理: 需求分析 日志处理需要记录的是: 请求的URL 访问者IP 调用的方法 传入的参数 返回的内容 上面的内容要求在控制台和日志中输出. 在学习这部分知识的时候,真的感觉收获很多,在之前Spr ...

  5. 个人博客开发之xadmin与ueditor集成

    项目源码下载:http://download.vhosts.cn 1. xadmin 添加ueditor 插件 vim extra_apps\xadmin\plugins\ueditor.py #没有 ...

  6. 个人博客开发之 xadmin 安装

    项目源码下载:http://download.vhosts.cn xadmin 下载地址:https://github.com/sshwsfc/xadmin或 https://github.com/s ...

  7. 个人博客开发之 ueditor 安装

  8. iOS开发之MVVM在项目中的应用

    今天写这篇博客是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇博客的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  9. 文顶顶iOS开发博客链接整理及部分项目源代码下载

    文顶顶iOS开发博客链接整理及部分项目源代码下载   网上的iOS开发的教程很多,但是像cnblogs博主文顶顶的博客这样内容图文并茂,代码齐全,示例经典,原理也有阐述,覆盖面宽广,自成系统的系列教程 ...

随机推荐

  1. centOS 7-Hadoop3.3.0完全分布式部署

    本文内容不乏对各大佬的案例借鉴,侵删. 本次实验用到的有虚拟机,Xshell,Hadoop压缩包和jdk压缩包   hadoop111A:192.168.241.111 hadoop222B:192. ...

  2. 腾讯 angel 3.0:高效处理模型

    腾讯 angel 3.0:高效处理模型 紧跟华为宣布新的 AI 框架开源的消息,腾讯又带来了全新的全栈机器学习平台 angel3.0.新版本功能特性覆盖了机器学习的各个阶段,包括:特征工程.模型训练. ...

  3. 汽车HUD(Head-up Display)的技术难点

    汽车HUD(Head-up Display)的技术难点 首先解析一下HUD是什么原理吧.其实就是把车的前挡风玻璃当成反射镜,在驾驶员人眼前投射一个仪表盘的虚像.图像本身来自下方的电子发光屏,发出仪表盘 ...

  4. Kubeedge Edged概述

    Kubeedge Edged概述 Overview EdgeD是管理节点生命周期的边缘节点模块.它可以帮助用户在边缘节点上部署容器化的工作负载或应用程序.这些工作负载可以执行任何操作,从简单的远程遥测 ...

  5. VB Aspose.Pdf 字体变小方格问题处理

    宋体是这样写的:SimSun原先以为是:宋体 先定义字体,在PDF中无法设置,这个找了很久,原来是使用:FontRepository.FindFont方式,这个坑了很久,很多都说是setFont,压根 ...

  6. springboot——重定向解决刷新浏览器造成表单重复提交的问题(超详细)

    原因:造成表单重复提交的原因是当我们刷新浏览器的时候,浏览器会发送上一次提交的请求.由于上一次提交的请求方式为post,刷新浏览器就会重新发送这个post请求,造成表单重复提交. 解决办法: 将请求当 ...

  7. mybatis学习——使用注解开发

    前言: 一个语句既可以通过 XML 定义,也可以通过注解定义.不过,由于 Java 注解的一些限制以及某些 MyBatis 映射的复杂性,要使用大多数高级映射(比如:嵌套联合映射),仍然需要使用 XM ...

  8. python做。大神空闲时间能帮忙弄一串代码嘛?猜拳游戏,分很多种手的背面和正面,最后剩下的再石头剪刀布

    .每天课程结束后,老师会选择一列的同学清扫教室,人数不定(建议根据当时情况输入),在收拾完教室后,最后一步是需要从这一列的同学中选择1-2人去倒垃圾桶,垃圾桶数量根据当时情况决定,目前采取的方式是, ...

  9. yum安装时提示“尚未安装任何 GPG 公钥,请下载您希望安装的软件签名公钥并安装”

    在Linux操作系统中,安装软件依赖包时,出现了尚未安装任何 GPG 公钥,要求使用rpm --import public.gpg.key导入  问题: [root@server7 yum.repos ...

  10. 【NX二次开发】Block UI 指定位置

    属性说明 属性   类型   描述   常规           BlockID    String    控件ID    Enable    Logical    是否可操作    Group    ...