一.引入依赖

        <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请求回复日志包含请求体的更多相关文章

  1. Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理

    Spring Boot AOP之对请求的参数入参与返回结果进行拦截处理   本文链接:https://blog.csdn.net/puhaiyang/article/details/78146620 ...

  2. Spring Boot 2.X(十四):日志功能 Logback

    Logback 简介 Logback 是由 SLF4J 作者开发的新一代日志框架,用于替代 log4j. 主要特点是效率更高,架构设计够通用,适用于不同的环境. Logback 分为三个模块:logb ...

  3. Spring Boot AOP解析

    Spring Boot AOP 面向切面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP). OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面. AOP(Aspec ...

  4. Spring boot AOP 记录请求日志

    如何将所有的通过url的请求参数以及返回结果都输出到日志中? 如果在controller的类中每个方法名都写一个log输出肯定是不明智的选择. 使用spring的AOP功能即可完成. 1. 在pom. ...

  5. Spring Boot AOP 扫盲,实现接口访问的统一日志记录

    AOP 是 Spring 体系中非常重要的两个概念之一(另外一个是 IoC),今天这篇文章就来带大家通过实战的方式,在编程猫 SpringBoot 项目中使用 AOP 技术为 controller 层 ...

  6. 玩转spring boot——AOP与表单验证

    AOP在大多数的情况下的应用场景是:日志和验证.至于AOP的理论知识我就不做赘述.而AOP的通知类型有好几种,今天的例子我只选一个有代表意义的“环绕通知”来演示. 一.AOP入门 修改“pom.xml ...

  7. Spring Boot AOP

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

  8. redis分布式锁-spring boot aop+自定义注解实现分布式锁

    接这这一篇redis分布式锁-java实现末尾,实现aop+自定义注解 实现分布式锁 1.为什么需要 声明式的分布式锁 编程式分布式锁每次实现都要单独实现,但业务量大功能复杂时,使用编程式分布式锁无疑 ...

  9. Spring Boot - AOP(面向切面)

    AOP 全称 Aspect Oriented Programming(面向切面),AOP主要实现的目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分 ...

随机推荐

  1. pat甲级 1152 Google Recruitment (20 分)

    In July 2004, Google posted on a giant billboard along Highway 101 in Silicon Valley (shown in the p ...

  2. {Notes}{Latex}{multirow}

    这个文章写的真的太牛比了! ============================================================ 在latex文件最前面用这个包\usepackag ...

  3. 关于fflush、缓冲区、scanf、EOF等问题真麻烦

    1.为什么 fflush(stdin)是错的 http://u637.springnote.com/pages/6288463.xhtml(已经bad request了) 首先请看以下程序: #inc ...

  4. 洛谷P2192HXY玩卡片

    题目大意 给你n个数,只包括5和0. 从中取一些数,要求组成的数是90的倍数. 如: 4 5 0 5 0 结果是0 题解 90=9*10,如果一个是90的倍数一定末尾有0,那么这个 数一定是10的倍数 ...

  5. volatile浅析

    volatile的意思是不稳定的.易变的,但在多线程中却跟字面意思一毛钱关系没有.作为一个变量修饰符,它主要有两个作用:一个是告诉大家,该变量是一个在多个线程之间均可见的变量:另一个是告诉java虚拟 ...

  6. Oracle修改字段值包含&字符

    昨天在更新一个表的字段时发现pl/sql弹出变量框,它认为我要改的字段值中包含未定义的变量.比如我执行一个脚本: update t_wlf_config set configvalue = 'hell ...

  7. laravel 中事务的使用

    在laravel5.3中使用事务 可以直接使用\DB::beginTransaction();使用 前提是数据库表必须支持事务,目前mysql中表类型只有InnoDb支持事务 想要在一个数据库事务中运 ...

  8. JVM内存管理之GC简介

    为何要了解GC策略与原理?        原因在上一章其实已经有所触及,就是因为在平时的工作和研究当中,不可避免的会遇到内存溢出与内存泄露的问题.如果对GC策略与原理不了解的情况下碰到了前面所说的问题 ...

  9. 窗口点击模拟a

    [问题描述] 在计算机屏幕上,有N 个窗口.窗口的边界上的点也属于该窗口.窗口之间有层次的区别,在多于一个窗口重叠的区域里,只会显示位于顶层的窗口里的内容.当你用鼠标点击屏幕上一个点的时候,若其在窗口 ...

  10. mysql索引之五:多列索引

    索引的三星原则 1.索引将相关的记录放到一起,则获得一星 2.如果索引中的数据顺序和查找中的排列顺序一致则获得二星 3.如果索引中的列包含了查询中的需要的全部列则获得三星 多列索引 1.1.多个单列索 ...