Forår - Log indgående anmodninger

1. Introduktion

I denne hurtige vejledning skal vi vise det grundlæggende ved at logge indgående anmodninger ved hjælp af Spring's logningsfilter. Hvis du lige er kommet i gang med logning, skal du tjekke denne artikel om logning samt SLF4J-artiklen.

2. Maven-afhængigheder

Logningsafhængighederne vil simpelthen være de samme som dem i introduktionsartiklen; lad os blot tilføje foråret her:

 org.springframework spring-core 5.2.2.RELEASE 

Den seneste version kan findes her for spring-core.

3. Grundlæggende webcontroller

Lad os først definere en controller, der vil blive brugt i vores eksempel:

@RestController offentlig klasse TaxiFareController {@GetMapping ("/ taxifare / get /") offentlig RateCard getTaxiFare () {returner nyt RateCard (); } @PostMapping ("/ taxifare / beregne /") offentlig streng beregneTaxiFare (@RequestBody @Valid TaxiRide taxiRide) {// returnere den beregnede billetpris}}

4. Tilpasset anmodningslogning

Spring giver en mekanisme til konfiguration af brugerdefinerede interceptors til at udføre handlinger før og efter webanmodninger.

Blandt Spring Interceptors er en af ​​de bemærkelsesværdige grænseflader HandlerInterceptor, som kan bruges til at logge den indgående anmodning ved at implementere følgende metoder:

  1. preHandle () - denne metode udføres før den aktuelle controller-servicemetode
  2. afterCompletion () - denne metode udføres, når controlleren er klar til at sende svaret

Desuden leverer Spring standardimplementeringen af HandlerInterceptor interface i form af HandlerInterceptorAdaptor klasse, som kan udvides af brugeren.

Lad os oprette vores egen interceptor - ved at udvide HandlerInterceptorAdaptor som:

@Komponent offentlig klasse TaxiFareRequestInterceptor udvider HandlerInterceptorAdapter {@Override public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) {return true; } @Override offentligt ugyldigt efterCompletion (HttpServletRequest anmodning, HttpServletResponse svar, Objektbehandler, Undtagelse ex) {//}}

Endelig konfigurerer vi TaxiRideRequestInterceptor inde i MVC-livscyklus for at registrere præ- og efterbehandling af påkald af controller-metoden, der er kortlagt til stien / taxifare defineret i TaxiFareController klasse.

@Configuration offentlig klasse TaxiFareMVCConfig implementerer WebMvcConfigurer {@Autowired private TaxiFareRequestInterceptor taxiFareRequestInterceptor; @Override public void addInterceptors (InterceptorRegistry registry) {registry.addInterceptor (taxiFareRequestInterceptor) .addPathPatterns ("/ ** / taxifare / ** /"); }}

Afslutningsvis er WebMvcConfigurer tilføjer TaxiFareRequestInterceptor inde i forårets MVC-livscyklus ved at påberåbe sig addInterceptors () metode.

Den største udfordring er at få kopierne af anmodningen og svarets nyttelast til logning og stadig efterlade den ønskede nyttelast til servleten til at behandle den.

Hovedproblemet med læseanmodningen er, at så snart inputstrømmen læses for første gang, er den markeret som forbrugt og kan ikke læses igen.

Ansøgningen kaster en undtagelse efter at have læst anmodningsstrømmen:

{"tidsstempel": 1500645243383, "status": 400, "error": "Dårlig anmodning", "undtagelse": "org.springframework.http.converter. HttpMessageNotReadableException", "meddelelse": "Kunne ikke læse dokumentet: Stream lukket ; nestet undtagelse er java.io.IOException: Stream lukket "," path ":" / rest-log / taxifare / calc / /}

For at overvinde dette problem, vi kan udnytte caching til at gemme anmodningsstrømmen og bruge den til logning.

Spring giver få nyttige klasser såsom ContentCachingRequestWrapper og ContentCachingResponseWrapper, som kan bruges til at cache anmodningsdataene til logningsformål.

Lad os justere vores preHandle () af TaxiRideRequestInterceptor klasse for at cache anmodningsobjektet ved hjælp af ContentCachingRequestWrapper klasse.

@Override offentlig boolsk preHandle (HttpServletRequest anmodning, HttpServletResponse svar, Objektbehandler) {HttpServletRequest anmodningCacheWrapperObject = nyt ContentCachingRequestWrapper (anmodning); requestCacheWrapperObject.getParameterMap (); // Læs inputStream fra requestCacheWrapperObject og log det returner sandt; }

Som vi kan se, har vi cache anmodningsobjektet ved hjælp af ContentCachingRequestWrapper klasse, der kan bruges til at læse nyttelastdata til logning uden at forstyrre det faktiske anmodningsobjekt:

requestCacheWrapperObject.getContentAsByteArray ();

Begrænsning

  • ContentCachingRequestWrapper klasse understøtter kun følgende:
Content-Type: application / x-www-form-urlencoded Method-Type: POST
  • Vi skal påkalde følgende metode for at sikre, at anmodningsdata cachelagres ContentCachingRequestWrapper inden brug:
requestCacheWrapperObject.getParameterMap ();

5. Logning af forårets indbyggede anmodning

Spring giver en indbygget løsning til at logge nyttelast. Vi kan bruge færdige filtre ved at tilslutte til Spring-applikationen ved hjælp af konfiguration.

AbstractRequestLoggingFilter er et filter, der giver grundlæggende funktioner til logning. Underklasser bør tilsidesætte beforeRequest () og afterRequest () metoder til at udføre den egentlige logning omkring anmodningen.

Spring framework giver tre konkrete implementeringsklasser, som kan bruges til at logge den indgående anmodning. Disse tre klasser er:

  • CommonsRequestLoggingFilter
  • Log4jNestedDiagnosticContextFilter (forældet)
  • ServletContextRequestLoggingFilter

Lad os nu gå videre til CommonsRequestLoggingFilter og konfigurer den til at registrere indgående anmodning om logning.

5.1. Konfigurer Spring Boot Application

Spring Boot-applikation kan konfigureres ved at tilføje en bønnedefinition for at aktivere anmodningslogning:

@Configuration public class RequestLoggingFilterConfig {@Bean public CommonsRequestLoggingFilter logFilter () {CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter (); filter.setIncludeQueryString (sand); filter.setIncludePayload (true); filter.setMaxPayloadLength (10000); filter.setIncludeHeaders (false); filter.setAfterMessagePrefix ("ANMOD DATA:"); returfilter; }}

Dette logfilter kræver også, at logniveauet er indstillet til DEBUG. Vi kan aktivere DEBUG-tilstand ved at tilføje nedenstående element i logback.xml:

En anden måde at aktivere DEBUG-niveauloggen er at tilføje følgende i application.properties:

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

5.2. Konfigurer traditionel webapplikation

I standard Spring-webapplikationen Filter kan indstilles via enten XML-konfiguration eller Java-konfiguration. Lad os oprette CommonsRequestLoggingFilter ved hjælp af konventionel Java-baseret konfiguration.

Som vi ved, er includePayload attribut for CommonsRequestLoggingFilter er som standard sat til falsk. Vi har brug for en brugerdefineret klasse for at tilsidesætte værdien af ​​attributten for at aktivere includePayload inden injektion i containeren ved hjælp af Java-konfiguration:

public class CustomeRequestLoggingFilter udvider CommonsRequestLoggingFilter {public CustomeRequestLoggingFilter () {super.setIncludeQueryString (true); super.setIncludePayload (true); super.setMaxPayloadLength (10000); }}

Nu skal vi indsprøjte CustomeRequestLoggingFilter ved hjælp af Java-baseret webinitialisering:

offentlig klasse CustomWebAppInitializer implementerer WebApplicationInitializer {public void onStartup (ServletContext container) {AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext (); context.setConfigLocation ("com.baeldung"); container.addListener (ny ContextLoaderListener (kontekst)); ServletRegistration.Dynamic dispatcher = container.addServlet ("dispatcher", ny DispatcherServlet (kontekst)); dispatcher.setLoadOnStartup (1); dispatcher.addMapping ("/"); container.addFilter ("customRequestLoggingFilter", CustomeRequestLoggingFilter.class) .addMappingForServletNames (null, false, "dispatcher"); }}

6. Eksempel i aktion

Nu kan vi oprette en Spring Boot med kontekst og se i aktion, at logning af indgående anmodninger fungerer som forventet:

@Test offentlig ugyldighed givenRequest_whenFetchTaxiFareRateCard_thanOK () {TestRestTemplate testRestTemplate = ny TestRestTemplate (); TaxiRide taxiRide = ny TaxiRide (sand, 10 l); String fare = testRestTemplate.postForObject (URL + "beregne /", taxiRide, String.class); assertThat (billetpris, equalTo ("200")); }

7. Konklusion

I denne artikel viste vi, hvordan man implementerer grundlæggende logning af webanmodninger ved hjælp af interceptors; Vi viste også begrænsningerne og udfordringerne ved denne løsning.

Derefter viste vi den indbyggede filterklasse, som giver klar til brug og enkel logningsmekanisme.

Som altid er implementeringen af ​​eksemplet og kodestykker tilgængelige på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found