Spring Cloud Sleuth i en monolittapplikasjon

1. Oversikt

I denne artikkelen introduserer vi Spring Cloud Sleuth - et kraftig verktøy for å forbedre logger i alle applikasjoner, men spesielt i et system bygget opp av flere tjenester.

Og for denne oppskriften vil vi fokusere på å bruke Sleuth i en monolit-applikasjon, ikke på tvers av mikrotjenester.

Vi har alle hatt den uheldige opplevelsen av å prøve å diagnostisere et problem med en planlagt oppgave, en operasjon med flere tråder eller en kompleks nettforespørsel. Ofte, selv når det er logging, er det vanskelig å fortelle hvilke handlinger som må korreleres sammen for å lage en enkelt forespørsel.

Dette kan gjøre diagnostisere en kompleks handling veldig vanskelig eller til og med umulig. Ofte resulterer det i løsninger som å sende en unik id til hver metode i forespørselen om å identifisere loggene.

Inn kommer Sleuth. Dette biblioteket gjør det mulig å identifisere logger knyttet til en bestemt jobb, tråd eller forespørsel. Sleuth integreres uanstrengt med loggerammer som Tilbakekobling og SLF4J for å legge til unike identifikatorer som hjelper med å spore og diagnostisere problemer ved hjelp av logger.

La oss ta en titt på hvordan det fungerer.

2. Oppsett

Vi begynner med å lage en Vårstøvel webprosjekt i vår favoritt IDE og legge denne avhengigheten til vår pom.xml fil:

 org.springframework.cloud spring-cloud-starter-sleuth 

Søknaden vår kjører med Vårstøvel og foreldrepompen gir versjoner for hver oppføring. Den siste versjonen av denne avhengigheten finner du her: spring-cloud-starter-sleuth. For å se hele POM, sjekk ut prosjektet på Github.

I tillegg, la oss legge til et applikasjonsnavn for å instruere Sleuth for å identifisere programmets logger.

I vår application.properties fil legg til denne linjen:

spring.application.name = Veiledning for Baeldung Sleuth

3. Sleuth-konfigurasjoner

Sleuth er i stand til å forbedre logger i mange situasjoner. Fra og med versjon 2.0.0 bruker Spring Cloud Sleuth Brave som sporingsbiblioteket som legger til unike ID-er til hver nettforespørsel som kommer inn i applikasjonen vår. Videre har Spring-teamet lagt til støtte for å dele disse ID-ene på tvers av trådgrenser.

Spor kan tenkes på som en enkelt forespørsel eller jobb som utløses i et program. Alle de forskjellige trinnene i forespørselen, selv på tvers av applikasjons- og trådgrenser, vil ha samme traceId.

Omfang kan derimot betraktes som deler av en jobb eller forespørsel. Et enkelt spor kan bestå av flere spenn som hver korrelerer til et bestemt trinn eller en del av forespørselen. Ved hjelp av sporings- og spenn-ID kan vi presisere når og hvor applikasjonen vår er når den behandler en forespørsel. Å gjøre det enklere å lese loggene våre.

I eksemplene våre vil vi utforske disse funksjonene i en enkelt applikasjon.

3.1. Enkel nettforespørsel

La oss først lage en kontrollerklasse som skal være et inngangspunkt å jobbe med:

@RestController public class SleuthController {@GetMapping ("/") public String helloSleuth () {logger.info ("Hello Sleuth"); returnere "suksess"; }}

La oss kjøre applikasjonen vår og navigere til “// localhost: 8080”. Se på loggene for utdata som ser ut som:

2017-01-10 22: 36: 38.254 INFO [Baeldung Sleuth Tutorial, 4e30f7340b3fb631,4e30f7340b3fb631, false] 12516 --- [nio-8080-exec-1] c.b.spring.session.SleuthController: Hello Sleuth

Dette ser ut som en vanlig logg, bortsett fra delen i begynnelsen mellom parentesene. Dette er kjerneinformasjonen som Spring Sleuth har lagt til. Disse dataene følger formatet av:

[applikasjonsnavn, traceId, spanId, eksport]

  • Programnavn - Dette er navnet vi setter i egenskapsfilen og kan brukes til å samle logger fra flere forekomster av samme applikasjon.
  • TraceId - Dette er en id som er tilordnet en enkelt forespørsel, jobb eller handling. Noe som hver unike brukerinitiert nettforespørsel vil ha sin egen sporId.
  • SpanId - Sporer en arbeidsenhet. Tenk på en forespørsel som består av flere trinn. Hvert trinn kan ha sitt eget spanId og spores individuelt. Som standard vil enhver applikasjonsflyt starte med samme TraceId og SpanId.
  • Eksport - Denne egenskapen er en boolsk som indikerer om denne loggen ble eksportert til en aggregator som Zipkin. Zipkin ligger utenfor omfanget av denne artikkelen, men spiller en viktig rolle i analysen av logger opprettet av Sleuth.

Nå skal du ha en ide om kraften til dette biblioteket. La oss se på et annet eksempel for ytterligere å demonstrere hvor integrert dette biblioteket er for loggføring.

3.2. Enkel nettforespørsel med tjenestetilgang

La oss starte med å lage en tjeneste med en enkelt metode:

@Service public class SleuthService {public void doSomeWorkSameSpan () {Thread.sleep (1000L); logger.info ("Gjør noe arbeid"); }}

La oss nå injisere tjenesten vår i kontrolleren vår og legge til en metode for kartlegging av forespørsler som får tilgang til den:

@Autowired private SleuthService sleuthService; @GetMapping ("/ same-span") public String helloSleuthSameSpan () kaster InterruptedException {logger.info ("Same Span"); sleuthService.doSomeWorkSameSpan (); returnere "suksess"; }

Til slutt, start applikasjonen på nytt og naviger til “// localhost: 8080 / same-span”. Se etter loggutdata som ser ut som:

2017-01-10 22: 51: 47.664 INFO [Baeldung Sleuth Tutorial, b77a5ea79036d5b9, b77a5ea79036d5b9, false] 12516 --- [nio-8080-exec-3] cbspring.session.SleuthController: Same Span 2017-01-10 22 : 51: 48.664 INFO [Baeldung Sleuth Tutorial, b77a5ea79036d5b9, b77a5ea79036d5b9, false] 12516 --- [nio-8080-exec-3] c.baeldung.spring.session.SleuthService: Gjør noe arbeid

Vær oppmerksom på at sporings- og spenn-ID-ene er de samme mellom de to loggene, selv om meldingene kommer fra to forskjellige klasser. Dette gjør det trivielt å identifisere hver logg under en forespørsel ved å søke etter sporId av den forespørselen.

Dette er standard oppførsel, en forespørsel får en enkelt sporId og spanId. Men vi kan legge til spennvidder manuelt slik vi ønsker det. La oss se på et eksempel som bruker denne funksjonen.

3.3. Manuelt legge til et spenn

For å starte, la oss legge til en ny kontroller:

@GetMapping ("/ new-span") public String helloSleuthNewSpan () {logger.info ("New Span"); sleuthService.doSomeWorkNewSpan (); returnere "suksess"; }

Og la oss nå legge til den nye metoden i tjenesten vår:

@Autowired private Tracer tracer; // ... offentlig ugyldig doSomeWorkNewSpan () kaster InterruptedException {logger.info ("Jeg er i det opprinnelige spennet"); Span newSpan = tracer.nextSpan (). Navn ("newSpan"). Start (); prøv (SpanInScope ws = tracer.withSpanInScope (newSpan.start ())) {Thread.sleep (1000L); logger.info ("Jeg er i det nye spennet og gjør noe kult arbeid som trenger sitt eget spenn"); } til slutt {newSpan.finish (); } logger.info ("Jeg er i det opprinnelige spennet"); }

Merk at vi også har lagt til et nytt objekt, Spor. De sporvogn forekomst er opprettet av Spring Sleuth under oppstart og blir gjort tilgjengelig for klassen vår gjennom avhengighetsinjeksjon.

Spor må startes og stoppes manuelt. For å oppnå dette, kode som kjører i en manuelt opprettet span er plassert inne i en prøv-endelig blokkere for å sikre span er stengt uavhengig av operasjonens suksess. Legg også merke til at nytt spennvidde må plasseres i omfang.

Start applikasjonen på nytt og naviger til “// localhost: 8080 / new-span”. Se etter loggutgangen som ser ut som:

2017-01-11 21: 07: 54.924 INFO [Baeldung Sleuth Tutorial, 9cdebbffe8bbbade, 9cdebbffe8bbbade, false] 12516 --- [nio-8080-exec-6] cbspring.session.SleuthController: New Span 2017-01-11 21 : 07: 54.924 INFO [Baeldung Sleuth Tutorial, 9cdebbffe8bbbade, 9cdebbffe8bbbade, false] 12516 --- [nio-8080-exec-6] c.baeldung.spring.session.SleuthService: Jeg er i den opprinnelige perioden 2017-01- 11 21: 07: 55.924 INFO [Baeldung Sleuth Tutorial, 9cdebbffe8bbbade, 1e706f252a0ee9c2, false] 12516 --- [nio-8080-exec-6] c.baeldung.spring.session.SleuthService: Jeg er i det nye spennet og gjør noe kult arbeid som trenger sitt eget spenn 2017-01-11 21: 07: 55.924 INFO [Baeldung Sleuth Tutorial, 9cdebbffe8bbbade, 9cdebbffe8bbbade, false] 12516 --- [nio-8080-exec-6] c.baeldung.spring.session. SleuthService: Jeg er i det opprinnelige spennet

Vi kan se at den tredje loggen deler sporId med de andre, men den har en unik spanId. Dette kan brukes til å finne forskjellige seksjoner i en enkelt forespørsel om mer finkornet sporing.

La oss nå ta en titt på Sleuth er støtte for tråder.

3.4. Spanning Runnables

For å demonstrere gjengefunksjonene til Sleuth la oss først legge til en konfigurasjonsklasse for å sette opp en trådgruppe:

@Configuration public class ThreadConfig {@Autowired private BeanFactory beanFactory; @Bean public Executor executor () {ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor (); threadPoolTaskExecutor.setCorePoolSize (1); threadPoolTaskExecutor.setMaxPoolSize (1); threadPoolTaskExecutor.initialize (); returner nye LazyTraceExecutor (beanFactory, threadPoolTaskExecutor); }}

Det er viktig å merke seg bruken av LazyTraceExecutor. Denne klassen kommer fra Sleuth biblioteket og er en spesiell type eksekutor som vil forplante vårt sporIds til nye tråder og opprett nye spanIds i prosessen.

La oss nå koble denne utføreren til kontrolleren vår og bruke den i en ny metode for kartlegging av forespørsler:

@Autowired private Executor executor; @GetMapping ("/ new-thread") offentlig streng halloSleuthNewThread () {logger.info ("Ny tråd"); Runnable runnable = () -> {prøv {Thread.sleep (1000L); } fange (InterruptedException e) {e.printStackTrace (); } logger.info ("Jeg er inne i den nye tråden - med et nytt spenn"); }; executor.execute (kjørbar); logger.info ("Jeg er ferdig - med det originale spennet"); returnere "suksess"; }

Med vår kjørbare på plass, la oss starte applikasjonen på nytt og navigere til “// localhost: 8080 / new-thread”. Se etter loggutdata som ser ut som:

2017-01-11 21: 18: 15.949 INFO [Baeldung Sleuth Tutorial, 96076a78343c364d, 96076a78343c364d, false] 12516 --- [nio-8080-exec-9] cbspring.session.SleuthController: Ny tråd 2017-01-11 21 : 18: 15.950 INFO [Baeldung Sleuth Tutorial, 96076a78343c364d, 96076a78343c364d, false] 12516 --- [nio-8080-exec-9] cbspring.session.SleuthController: Jeg er ferdig - med det originale spennet 2017-01-11 21: 18: 16.953 INFO [Baeldung Sleuth Tutorial, 96076a78343c364d, e3b6a68013ddfeea, false] 12516 --- [lTaskExecutor-1] cbspring.session.SleuthController: Jeg er inne i den nye tråden - med et nytt spenn

I likhet med forrige eksempel kan vi se at alle loggene deler det samme sporId. Men stokken som kommer fra den løpbare har et unikt spenn som sporer arbeidet som er utført i den tråden. Husk at dette skjer på grunn av LazyTraceExecutor, hvis vi skulle bruke en vanlig eksekutor, ville vi fortsette å se det samme spanId brukt i den nye tråden.

La oss nå se nærmere på Sleuth er Støtte for @Async metoder.

3.5. @Async Brukerstøtte

For å legge til async-støtte, la oss først endre vår ThreadConfig klasse for å aktivere denne funksjonen:

@Configuration @EnableAsync offentlig klasse ThreadConfig utvider AsyncConfigurerSupport {// ... @Override public Executor getAsyncExecutor () {ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor (); threadPoolTaskExecutor.setCorePoolSize (1); threadPoolTaskExecutor.setMaxPoolSize (1); threadPoolTaskExecutor.initialize (); returner nye LazyTraceExecutor (beanFactory, threadPoolTaskExecutor); }}

Merk at vi utvider AsyncConfigurerSupport for å spesifisere vår asynkroniserende utfører og bruk LazyTraceExecutor for å sikre at spor og spenninger forplantes riktig. Vi har også lagt til @EnableAsync til toppen av klassen vår.

La oss nå legge til en asynkroniseringsmetode i tjenesten vår:

@Async offentlig ugyldig asyncMethod () {logger.info ("Start Async Method"); Tråd. Søvn (1000 l); logger.info ("End Async Method"); }

La oss nå kalle inn denne metoden fra kontrolleren vår:

@GetMapping ("/ async") offentlig streng halloSleuthAsync () {logger.info ("Before Async Method Call"); sleuthService.asyncMethod (); logger.info ("Etter Async Method Call"); returnere "suksess"; }

Til slutt, la oss starte tjenesten vår på nytt og gå til “// localhost: 8080 / async”. Se etter loggutgangen som ser ut som:

2017-01-11 21: 30: 40.621 INFO [Baeldung Sleuth Tutorial, c187f81915377fff, c187f81915377fff, false] 10072 --- [nio-8080-exec-2] cbspring.session.SleuthController: Before Async Method Call 2017-01- 11 21: 30: 40.622 INFO [Baeldung Sleuth Tutorial, c187f81915377fff, c187f81915377fff, false] 10072 --- [nio-8080-exec-2] cbspring.session.SleuthController: After Async Method Call 2017-01-11 21:30 : 40.622 INFO [Baeldung Sleuth Tutorial, c187f81915377fff, 8a9f3f097dca6a9e, false] 10072 --- [lTaskExecutor-1] c.baeldung.spring.session.SleuthService: Start Async Method 2017-01-11 21: 30: 41.622 INFO [Baeldung Sleuth Tutorial, c187f81915377fff, 8a9f3f097dca6a9e, false] 10072 --- [lTaskExecutor-1] c.baeldung.spring.session.SleuthService: End Async Method

Vi kan se her omtrent som vårt kjørbare eksempel, Sleuth forplanter sporId inn i async-metoden og legger til et unikt spanId.

La oss nå arbeide gjennom et eksempel ved hjelp av vårstøtte for planlagte oppgaver.

3.6. @ Planlagt Brukerstøtte

Til slutt, la oss se på hvordan Sleuth fungerer med @ Planlagt metoder. For å gjøre dette, la oss oppdatere vår ThreadConfig klasse for å aktivere planlegging:

@Configuration @EnableAsync @EnableScheduling public class ThreadConfig utvider AsyncConfigurerSupport implementerer SchedulingConfigurer {// ... @Override public void configureTasks (ScheduledTaskRegistrar scheduletaskRegistrar) {planningTaskRegistrar.setSchedulated } @Bean (destroyMethod = "shutdown") public Executor schedulingExecutor () {return Executors.newScheduledThreadPool (1); }}

Merk at vi har implementert SchedulingConfigurer grensesnitt og overstyrt configureTasks-metoden. Vi har også lagt til @EnableScheduling til toppen av klassen vår.

La oss deretter legge til en tjeneste for våre planlagte oppgaver:

@Service offentlig klasse SchedulingService {private Logger logger = LoggerFactory.getLogger (this.getClass ()); @Autowired private SleuthService sleuthService; @Scheduled (fixedDelay = 30000) offentlig ugyldig planningWork () kaster InterruptedException {logger.info ("Start litt arbeid fra den planlagte oppgaven"); sleuthService.asyncMethod (); logger.info ("Avslutt arbeid fra planlagt oppgave"); }}

I denne klassen har vi opprettet en enkelt planlagt oppgave med en fast forsinkelse på 30 sekunder.

La oss nå starte søknaden på nytt og vente på at oppgaven vår skal utføres. Se konsollen for utdata slik:

2017-01-11 21: 30: 58.866 INFO [Baeldung Sleuth Tutorial, 3605f5deaea28df2,3605f5deaea28df2, false] 10072 --- [pool-1-thread-1] cbspring.session.SchedulingService: Start litt arbeid fra den planlagte oppgaven 2017 -01-11 21: 30: 58.866 INFO [Baeldung Sleuth Tutorial, 3605f5deaea28df2,3605f5deaea28df2, false] 10072 --- [pool-1-thread-1] cbspring.session.SchedulingService: Avslutt arbeidet fra planlagt oppgave

Vi kan se her det Sleuth har opprettet nye sporings- og span-ID-er for oppgaven vår. Hver forekomst av en oppgave får som standard sitt eget spor og spenn.

4. Konklusjon

Avslutningsvis har vi sett hvordan Spring Sleuth kan brukes i en rekke situasjoner i en enkelt webapplikasjon. Vi kan bruke denne teknologien til å enkelt korrelere logger fra en enkelt forespørsel, selv når forespørselen spenner over flere tråder.

Nå kan vi se hvordan Spring Cloud Sleuth kan hjelpe oss med å holde sunn fornuft når vi feilsøker et miljø med flere tråder. Ved å identifisere hver operasjon i en sporId og hvert trinn i en spanId vi kan virkelig begynne å bryte ned analysen av komplekse jobber i loggene våre.

Selv om vi ikke går til skyen, Spring Sleuth er sannsynligvis en kritisk avhengighet i nesten ethvert prosjekt; det er sømløst å integrere og er et massivt tillegg av verdi.

Herfra vil du kanskje undersøke andre funksjoner i Sleuth. Den kan støtte sporing i distribuerte systemer ved hjelp av RestTemplate, på tvers av meldingsprotokoller som brukes av RabbitMQ og Redis, og gjennom en port som Zuul.

Som alltid kan du finne kildekoden på Github.


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