spring boot aop打印http请求回复日志包含请求体
一.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
二.代码
AopLoggingApplication
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan
public class AopLoggingApplication {
public static void main(String[] args) {
SpringApplication.run(AopLoggingApplication.class, args);
}
}
SomeFilter
package com.example.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebFilter
@Slf4j
public class SomeFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
log.debug("进入SomeFilter");
/**
* 因为请求体的流一般在进入aop时就被关闭了,这里这样写可以在aop里面拿到请求体,具体参考org.springframework.web.filter.AbstractRequestLoggingFilter.
*/
if(log.isDebugEnabled()){
httpServletRequest = new ContentCachingRequestWrapper(httpServletRequest, 1024);
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
HttpLoggingAspect
package com.example.aspect;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.WebUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
@Component
@Aspect
@Slf4j
@ConditionalOnProperty(name = "web-logging", havingValue = "debug")
public class HttpLoggingAspect {
@Autowired
private ObjectMapper mapper;
@Pointcut("execution(public * com.example..controller.*.*(..))")//两个..代表所有子目录,最后括号里的两个..代表所有参数
public void logPointCut() {
}
@Around("logPointCut()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
log.debug("┌──────────请求──────────────────");
log.debug("┃控制器{}->方法{}-->参数{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(), mapper.writeValueAsString(Arrays.toString(joinPoint.getArgs())));
log.debug("┃{}-->{}-->{}", request.getRemoteAddr(), request.getMethod(), request.getRequestURL());
log.debug("┃parameter{}", mapper.writeValueAsString(request.getParameterMap()));// 格式化输出的json mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
log.debug("┃body{}",getPayload(request));
log.debug("└──────────────────────────────");
long startTime = System.currentTimeMillis();
Object ob = pjp.proceed();// ob 为方法的返回值
log.debug("┌──────────回复──────────────────");
log.debug("┃耗时{}ms" ,(System.currentTimeMillis() - startTime));
log.debug("┃返回" + mapper.writeValueAsString(ob));
log.debug("└──────────────────────────────");
return ob;
}
private String getPayload(HttpServletRequest request) {
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
) {
try {
);//最大只打印1024字节
, length, wrapper.getCharacterEncoding());
} catch (UnsupportedEncodingException var6) {
return "[unknown]";
}
}
}
return "";
}
}
测试用的TestController
package com.example.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class TestController {
@GetMapping("/")
public String hello(String name) {
return "Spring boot " + name;
}
@PostMapping("/")
public Map<String, Object> hello(@RequestBody Map<String, Object> map){
return map;
}
}
配置文件application.properties
web-logging=debug #作为启动web日志的配置, 方便本地开发和上线 logging.level.com.example=debug
三.效果
1.=====================================================================
请求 GET http://localhost:8080?name=abc
回复Spring boot abc
日志
-- :: --- [nio--exec-] com.example.filter.SomeFilter : 进入SomeFilter
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┌──────────请求──────────────────
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃控制器com.example.controller.TestController->方法hello-->参数"[abc]"
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃127.0.0.1-->GET-->http://localhost:8080/
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃parameter{"name":["abc"]}
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃body
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : └──────────────────────────────
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┌──────────回复──────────────────
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃耗时181ms
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃返回Spring boot abc
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : └──────────────────────────────
2.=====================================================================
请求 POST http://localhost:8080 参数 json类型 {"name":"spring boot"}
回复
{"name":"spring boot"}
日志
-- :: --- [nio--exec-] com.example.filter.SomeFilter : 进入SomeFilter
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┌──────────请求──────────────────
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃控制器com.example.controller.TestController->方法hello-->参数"[{name=spring boot}]"
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃127.0.0.1-->POST-->http://localhost:8080/
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃parameter{}
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃body{"name":"spring boot"}
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : └──────────────────────────────
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┌──────────回复──────────────────
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃耗时44ms
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : ┃返回{name=spring boot}
-- :: --- [nio--exec-] com.example.aspect.HttpLoggingAspect : └──────────────────────────────
github: https://github.com/tungss/aop-logging.git
spring boot aop打印http请求回复日志包含请求体的更多相关文章
- Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理
Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理 本文链接:https://blog.csdn.net/puhaiyang/article/details/78146620 ...
- Spring Boot 2.X(十四):日志功能 Logback
Logback 简介 Logback 是由 SLF4J 作者开发的新一代日志框架,用于替代 log4j. 主要特点是效率更高,架构设计够通用,适用于不同的环境. Logback 分为三个模块:logb ...
- Spring Boot AOP解析
Spring Boot AOP 面向切面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP). OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面. AOP(Aspec ...
- Spring boot AOP 记录请求日志
如何将所有的通过url的请求参数以及返回结果都输出到日志中? 如果在controller的类中每个方法名都写一个log输出肯定是不明智的选择. 使用spring的AOP功能即可完成. 1. 在pom. ...
- Spring Boot AOP 扫盲,实现接口访问的统一日志记录
AOP 是 Spring 体系中非常重要的两个概念之一(另外一个是 IoC),今天这篇文章就来带大家通过实战的方式,在编程猫 SpringBoot 项目中使用 AOP 技术为 controller 层 ...
- 玩转spring boot——AOP与表单验证
AOP在大多数的情况下的应用场景是:日志和验证.至于AOP的理论知识我就不做赘述.而AOP的通知类型有好几种,今天的例子我只选一个有代表意义的“环绕通知”来演示. 一.AOP入门 修改“pom.xml ...
- Spring Boot AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...
- redis分布式锁-spring boot aop+自定义注解实现分布式锁
接这这一篇redis分布式锁-java实现末尾,实现aop+自定义注解 实现分布式锁 1.为什么需要 声明式的分布式锁 编程式分布式锁每次实现都要单独实现,但业务量大功能复杂时,使用编程式分布式锁无疑 ...
- Spring Boot - AOP(面向切面)
AOP 全称 Aspect Oriented Programming(面向切面),AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分 ...
随机推荐
- 剑指offer第六章
剑指offer第六章 1.数字在排序数组中出现的次数 统计一个数字在排序数组中出现的次数.例如输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于3在数组中出现了4次,所以输出4 分析:思路1 ...
- python笔记-9(subprocess模块、面向对象、socket入门)
一.subprocess 模块 1.了解os.system()与os.popen的区别及不足 1.1 os.system()可以执行系统指令,将结果直接输出到屏幕,同时可以将指令是否执行成功的状态赋值 ...
- 【angularJS】Route路由
介绍 AngularJS 路由允许我们通过不同的 URL 访问不同的内容. 通过 AngularJS 可以实现多视图的单页Web应用(single page web application,SPA). ...
- CSU1632Repeated Substrings(后缀数组/最长公共前缀)
题意就是求一个字符串的重复出现(出现次数>=2)的不同子串的个数. 标准解法是后缀数组.最长公共前缀的应用,对于样例aabaab,先将所有后缀排序: aab 3 aabaab 1 a ...
- VS2013下的64位与32位程序配置
VS2013下的64位与32位程序配置 在Windows 7 64bit和Visual Studio 2013下生成64位程序. 新建一个Visual Studio Win32 Console项目 ...
- cacti监控linux主机时iptables阻碍了udp161端口造成无法监控解
由于在添加linux主机时,有防火墙启用,造成无法监控linux主机,现有两种方法解决无法监控linux主机问题:可以通过在监控主机执行该命令: snmpwalk -c public -v 2c 19 ...
- bzoj3258秘密任务
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3258 因为只走最短路,所以先正反两遍djkstra,新建边. 这里的边是单向边.所以要用原来 ...
- SharePoint2013集成Exchange之任务同步
SharePoint可以将任务列表到outlook中,但在sharepoint 2013 上这个功能似乎不是很好用,如下图所示,点击任务列表的"同步到Outlook"按钮: 在弹出 ...
- rtesseract的例子
这个是用来验证码识别的 需要先装一下这些 brew install Tesseract brew install imagemagick gem install rmagick 测试代码 requir ...
- Java堆栈解析
1.RAM和ROM区别RAM-RamdomAccessMemory随机存取存储器(断电后数据会丢失),高速存取,读写时间相等,且与地址无关,如计算机内存等. ROM-Read Only Memory只 ...