当前位置: 首页 > 知识库问答 >
问题:

如何在SpringREST中记录所有请求响应?

颛孙正卿
2023-03-14

应用程序应异步(在单独的线程中)记录以下信息,而不会影响客户端。

  • 请求HTTP方法和URI

如果我们在过滤器中使用inputstream,那么spring将无法再次使用它进行json到对象的映射。在输入流到对象映射期间的某个地方,我们可以插入记录器吗?

更新:

我们可以在MessageConverter中编写日志代码,但这似乎不是一个好主意。

public class MyMappingJackson2MessageConverter extends AbstractHttpMessageConverter<Object> {
    ...
    protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        InputStream inputStream = inputMessage.getBody();
        String requestBody = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
        String method = request.getMethod();
        String uri = request.getRequestURI();
        LOGGER.debug("{} {}", method, uri);
        LOGGER.debug("{}", requestBody);
        return objectMapper.readValue(requestBody, clazz);
    }

    protected void writeInternal(Object o, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        String responseBody = objectMapper.writeValueAsString(o);
        LOGGER.debug("{}", responseBody);
        outputMessage.getBody().write(responseBody.getBytes(StandardCharsets.UTF_8));
    }
}

共有3个答案

艾正浩
2023-03-14

我想您最好的选择是使用异步方法进行日志记录。

@Async
public void asyncMethodWithVoidReturnType() {
  System.out.println("Execute method asynchronously. "
    + Thread.currentThread().getName());
}

请参阅:

异步

如何异步

傅博瀚
2023-03-14

您可以使用Spring方面实现这一点。它为您提供了一些注释,如:@前,@后返回,@后抛等。您可能不需要所有endpoint日志,所以这里有一些基于包的过滤器。下面是一些例子:

请求:

@Before("within(your.package.where.is.endpoint..*)")
    public void endpointBefore(JoinPoint p) {
        if (log.isTraceEnabled()) {
            log.trace(p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName() + " START");
            Object[] signatureArgs = p.getArgs();


            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
            try {

                if (signatureArgs[0] != null) {
                    log.trace("\nRequest object: \n" + mapper.writeValueAsString(signatureArgs[0]));
                }
            } catch (JsonProcessingException e) {
            }
        }
    }

here `@Before("within(your.package.where.is.endpoint..*)")` has the package path. All endpoints within this package will generate the log.

回应:

@AfterReturning(value = ("within(your.package.where.is.endpoint..*)"),
            returning = "returnValue")
    public void endpointAfterReturning(JoinPoint p, Object returnValue) {
        if (log.isTraceEnabled()) {
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
            try {
                log.trace("\nResponse object: \n" + mapper.writeValueAsString(returnValue));
            } catch (JsonProcessingException e) {
                System.out.println(e.getMessage());
            }
            log.trace(p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName() + " END");
        }
    }

here `@AfterReturning("within(your.package.where.is.endpoint..*)")` has the package path. All endpoints within this package will generate the log. Also Object returnValue has the response.

例外情况:

@AfterThrowing(pointcut = ("within(your.package.where.is.endpoint..*)"), throwing = "e")
public void endpointAfterThrowing(JoinPoint p, Exception e) throws DmoneyException {
    if (log.isTraceEnabled()) {
        System.out.println(e.getMessage());

        e.printStackTrace();


        log.error(p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName() + " " + e.getMessage());
    }
}
here `@AfterThrowing(pointcut = ("within(your.package.where.is.endpoint..*)"), throwing = "e")`  has the package path. All endpoints within this package will generate the log. Also Exception e has the error response.

以下是完整的代码:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Order(1)
@Component
//@ConditionalOnExpression("${endpoint.aspect.enabled:true}")
public class EndpointAspect {
    static Logger log = Logger.getLogger(EndpointAspect.class);

    @Before("within(your.package.where.is.endpoint..*)")
    public void endpointBefore(JoinPoint p) {
        if (log.isTraceEnabled()) {
            log.trace(p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName() + " START");
            Object[] signatureArgs = p.getArgs();


            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
            try {

                if (signatureArgs[0] != null) {
                    log.trace("\nRequest object: \n" + mapper.writeValueAsString(signatureArgs[0]));
                }
            } catch (JsonProcessingException e) {
            }
        }
    }

    @AfterReturning(value = ("within(your.package.where.is.endpoint..*)"),
            returning = "returnValue")
    public void endpointAfterReturning(JoinPoint p, Object returnValue) {
        if (log.isTraceEnabled()) {
            ObjectMapper mapper = new ObjectMapper();
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
            try {
                log.trace("\nResponse object: \n" + mapper.writeValueAsString(returnValue));
            } catch (JsonProcessingException e) {
                System.out.println(e.getMessage());
            }
            log.trace(p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName() + " END");
        }
    }


    @AfterThrowing(pointcut = ("within(your.package.where.is.endpoint..*)"), throwing = "e")
    public void endpointAfterThrowing(JoinPoint p, Exception e) throws Exception {
        if (log.isTraceEnabled()) {
            System.out.println(e.getMessage());

            e.printStackTrace();


            log.error(p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName() + " " + e.getMessage());
        }
    }
}

有关AOP的更多信息,请访问此处:

关于AOP的Spring docks

关于AOP的示例文章

萧德馨
2023-03-14

贝尔东的回答。通用域名格式:

Spring提供了记录有效载荷的内置解决方案。我们可以通过使用配置插入Spring应用程序来使用现成的过滤器。是一个提供日志记录基本功能的过滤器。子类应该重写beForeRequest()postRequest()方法,以便围绕请求执行实际的日志记录。Spring框架提供了以下具体的实现类,这些类可以用来记录传入的请求。这些是:

  • CommonRequestLoggingFilter
  • ServletContextRequestLoggingFilter

通过添加bean定义以启用请求日志记录,可以配置Spring Boot应用程序:

@Configuration
public class RequestLoggingFilterConfig {

    @Bean
    public CommonsRequestLoggingFilter logFilter() {
        CommonsRequestLoggingFilter filter
          = new CommonsRequestLoggingFilter();
        filter.setIncludeQueryString(true);
        filter.setIncludePayload(true);
        filter.setMaxPayloadLength(10000);
        filter.setIncludeHeaders(false);
        filter.setAfterMessagePrefix("REQUEST DATA : ");
        return filter;
    }
}

此外,此日志过滤器要求将日志级别设置为调试。在应用程序中。属性put

logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG

为了使日志记录异步,我们可以使用异步附加器。不幸的是,它不支持记录响应有效负载:(

 类似资料:
  • 在Vertx WebClient中记录传出http请求和响应的最简单方法是什么。我正在寻找类似于httpserver LoggerHandler的东西,但接下来是webclient的输出请求。

  • 使用Netty 4.0.0.beta1,对我来说,将传入/传出HTTP流量记录到基于Netty的服务器的最佳方式是什么?我的管道当前为: 我尝试编写一个实现的处理程序,然后在方法中进行日志记录,这似乎对传入的请求很好,但这是推荐的方式吗? 当我试图实现时,我未能在方法中看到实际的FullHttpResponse对象。 建议?谢了!

  • 问题内容: 我需要所有已登录的SOAP请求,以及持续时间(处理请求所花费的时间)。 最好的方法是什么?看起来可以为Spring WebServices配置log4j,但是它将记录所有值吗? 将以下行添加到log4j.xml 编辑:我们实际上是在使用,而不是。另外,看起来可以通过配置PayloadLoggingInterceptor来做到这一点 但是我不确定日志消息会去哪里。我将该拦截器添加到了我们

  • 我正在和spring boot一起开发rest api。我需要记录所有的请求与输入参数(与方法,如GET,POST等),请求路径,查询字符串,相应的类方法的请求,也响应这个动作,成功和错误。例如: 成功的请求: 日志应该如下所示: 或请求出错: 日志应该是这样的: 我希望请求/响应是一个单一的实体,与该实体相关的自定义信息,在成功和错误的情况下。 在spring实现这一点的最佳做法是什么,可能是与

  • 问题内容: 从技术上来说,答案都不是。根据Java虚拟机规范,用于存储字符串文字的区域位于运行时常量池中。运行时常量池内存区域是按类或每个接口分配的,因此它根本不与任何对象实例绑定。运行时常量池是方法区域的子集,其中“存储每个类的结构,例如运行时常量池,字段和方法数据以及方法和构造函数的代码,包括用于类和实例初始化以及接口的特殊方法”类型初始化”。VM规范说,尽管方法领域 从逻辑上讲,它是堆的一部

  • 我在改型API中找不到记录完整请求/响应体的相关方法。我希望在探查器中得到一些帮助(但它只提供关于响应的元数据)。我尝试在构建器中设置日志级别,但这也没有帮助: 编辑:此代码正在工作。我不知道为什么它在早些时候不起作用。可能是因为我用的是旧版本的改装。