L'application doit consigner les informations suivantes sans impact sur un client, de manière asynchrone (dans un thread séparé).
Si nous consommons inputstream
dans le filtre, alors il ne peut pas être consommé à nouveau au printemps pour le mappage d'objet json. Quelque part pendant le flux d'entrée vers le mappage d'objet, pouvons-nous brancher notre enregistreur?
Mise à jour:
Nous pouvons écraser le code de journalisation dans un MessageConverter , mais cela ne semble pas être une bonne idée.
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));
}
}
Une réponse de baeldung.com :
Spring fournit une solution intégrée pour enregistrer les charges utiles. Nous pouvons utiliser des filtres prêts à l'emploi en nous connectant à l'application Spring à l'aide de la configuration. AbstractRequestLoggingFilter est un filtre qui fournit les fonctions de base de la journalisation. Les sous-classes doivent remplacer les méthodes
beforeRequest()
etafterRequest()
pour effectuer la journalisation réelle autour de la demande. Le framework Spring fournit les classes d'implémentation concrètes suivantes qui peuvent être utilisées pour enregistrer la demande entrante. Ceux-ci sont:L'application Spring Boot peut être configurée en ajoutant une définition de bean pour activer la journalisation des demandes:
@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; } }
En outre, ce filtre de journalisation nécessite que le niveau de journalisation soit défini sur DEBUG. Dans
application.properties
mettrelogging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=DEBUG
Pour rendre la journalisation asynchrone, nous pouvons utiliser appender asynchronous . Malheureusement, il ne prend pas en charge la journalisation des charges utiles de réponse. :(
Vous pouvez y parvenir en utilisant l'aspect ressort. Il vous fournit des annotations comme: @Before , @AfterReturning, @AfterThrowing
etc. Vous n'avez peut-être pas besoin de tous les journaux de noeuds finaux, alors voici quelques filtres basés sur le package. Voici quelques exemples:
Pour la demande:
@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.
Pour la réponse:
@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.
Par exception:
@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.
Voici le code complet:
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());
}
}
}
Plus d'informations sur la visite AOP ici:
J'utiliserais 2 éléments: A LoggingFilter et le support Async de Spring. Pour le premier, j'utiliserais un CommonsRequestLoggingFilter qui sait déjà comment intercepter les requêtes HTTP, créer la configuration pour cela et l'Async. Vous pouvez faire quelque chose comme:
Activez d'abord la prise en charge asynchrone
@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }
Ensuite, créez le 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
}
}
Créez ensuite une configuration de journalisation personnalisée pour le filtre que vous avez créé:
@Configuration
public class LoggingConfiguration {
@Bean
public LoggingConfiguration logFilter() {
LoggingFilter filter
= new LoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setIncludeHeaders(true);
return filter;
}
}
Pour extraire les données de la requête, vous pouvez utiliser le paramètre message
ou traiter le HttpServletRequest
. Prenons comme exemple:
Je suppose que votre meilleure option est de faire la journalisation dans une méthode Async.
@Async
public void asyncMethodWithVoidReturnType() {
System.out.println("Execute method asynchronously. "
+ Thread.currentThread().getName());
}
Prière de se référer à: