应用程序应该在不影响客户端的情况下(在单独的线程中)记录以下信息。
如果我们在过滤器中消耗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));
}
}
答案 0 :(得分:3)
来自baeldung.com的答案:
Spring提供了一个内置的有效负载日志解决方案。我们可以用 通过使用插入Spring应用程序的现成过滤器 组态。 AbstractRequestLoggingFilter是一个过滤器 提供日志记录的基本功能。子类应该覆盖
beforeRequest()
和afterRequest()
方法来执行实际操作 记录请求。 Spring框架提供以下内容 具体的实现类,可用于记录传入 请求。这些是:可以通过添加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; } }
此外,此日志记录筛选器要求将日志级别设置为DEBUG。在
application.properties
放logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
要使日志记录异步,我们可以使用asynchronous appenders。不幸的是,它不支持记录响应有效负载。 :(
答案 1 :(得分:1)
您可以使用弹簧方面实现此目的。它为您提供了一些注释:@Before , @AfterReturning, @AfterThrowing
等。您可能不需要所有端点日志,因此这里有一些基于包的过滤器。以下是一些例子:
对于请求:
@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的更多信息,请访问:
答案 2 :(得分:0)
我想你最好的选择是在异步方法中进行日志记录。
@Async
public void asyncMethodWithVoidReturnType() {
System.out.println("Execute method asynchronously. "
+ Thread.currentThread().getName());
}
请参阅:
答案 3 :(得分:0)
我会使用2个元素:来自Spring的LoggingFilter和Async支持。对于第一个,我将使用已经知道如何拦截HTTP请求的 CommonsRequestLoggingFilter ,为其创建配置和Async。你可以这样做:
首先启用异步支持
@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }
然后创建loggingFilter:
public class LoggingFilter extends CommonsRequestLoggingFilter {
@Override
protected void beforeRequest(final HttpServletRequest request, final String message) {
// DO something
myAsyncMethodRequest(request, message)
}
@Override
protected void afterRequest(final HttpServletRequest request, final String message) {
// Do something
myAsyncMethodResponse(request, message)
}
// -----------------------------------------
// Async Methods
// -----------------------------------------
@Async
protected void myAsyncMethodRequest(HttpServletRequest request, String message) {
// Do your thing
// You can use message that has a raw message from the properties
// defined in the logFilter() method.
// Also you can extract it from the HttpServletRequest using:
// IOUtils.toString(request.getReader());
}
@Async
protected void myAsyncMethodResponse(HttpServletRequest request, String message) {
// Do your thing
}
}
然后为您创建的过滤器创建自定义日志记录配置:
@Configuration
public class LoggingConfiguration {
@Bean
public LoggingConfiguration logFilter() {
LoggingFilter filter
= new LoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setIncludeHeaders(true);
return filter;
}
}
要从请求中提取数据,您可以使用message
参数或处理HttpServletRequest
。举个例子: