Spring Cloud Sleuth i en monolit applikation

1. Oversigt

I denne artikel introducerer vi Spring Cloud Sleuth - et kraftfuldt værktøj til forbedring af logfiler i enhver applikation, men især i et system bygget op af flere tjenester.

Og til denne opskrivning vil vi fokusere på at bruge Sleuth i en monolit applikation, ikke på tværs af mikrotjenester.

Vi har alle haft den uheldige oplevelse at forsøge at diagnosticere et problem med en planlagt opgave, en operation med flere tråde eller en kompleks webanmodning. Selv når der er logget, er det ofte svært at fortælle, hvilke handlinger der skal korreleres for at skabe en enkelt anmodning.

Dette kan gøre diagnosticering af en kompleks handling meget vanskelig eller endda umulig. Ofte resulterer det i løsninger som at sende en unik id til hver metode i anmodningen om at identificere logfilerne.

Ind kommer Sleuth. Dette bibliotek gør det muligt at identificere logfiler, der vedrører et bestemt job, en tråd eller en anmodning. Sleuth integreres ubesværet med logningsrammer som f.eks Logback og SLF4J for at tilføje unikke identifikatorer, der hjælper med at spore og diagnosticere problemer ved hjælp af logfiler.

Lad os se på, hvordan det fungerer.

2. Opsætning

Vi starter med at oprette en Spring Boot webprojekt i vores foretrukne IDE og tilføje denne afhængighed til vores pom.xml fil:

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

Vores ansøgning kører med Spring Boot og den overordnede pom giver versioner til hver post. Den seneste version af denne afhængighed kan findes her: spring-cloud-starter-sleuth. For at se hele POM, se projektet på Github.

Lad os derudover tilføje et applikationsnavn for at instruere Sleuth for at identificere denne applikations logfiler.

I vores application.properties fil tilføj denne linje:

spring.application.name = Vejledning til Baeldung Sleuth

3. Sleuth-konfigurationer

Sleuth er i stand til at forbedre logfiler i mange situationer. Fra og med version 2.0.0 bruger Spring Cloud Sleuth Brave som sporingsbiblioteket, der tilføjer unikke id'er til hver webanmodning, der kommer ind i vores applikation. Desuden har Spring-teamet tilføjet støtte til at dele disse id'er på tværs af trådgrænser.

Spor kan betragtes som en enkelt anmodning eller et job, der udløses i en applikation. Alle de forskellige trin i denne anmodning, selv på tværs af applikations- og trådgrænser, har samme traceId.

Spænd kan derimod betragtes som sektioner i et job eller anmodning. Et enkelt spor kan bestå af flere spænd, der hver korrelerer med et specifikt trin eller afsnit i anmodningen. Ved hjælp af sporings- og span-id'er kan vi præcist finde ud af, hvornår og hvor vores applikation er, når den behandler en anmodning. Gør læsning af vores logfiler meget lettere.

I vores eksempler vil vi udforske disse muligheder i en enkelt applikation.

3.1. Enkel webanmodning

Lad os først oprette en controller-klasse, der skal være et indgangspunkt til at arbejde med:

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

Lad os køre vores applikation og navigere til “// localhost: 8080”. Se logfiler for output, der ligner:

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 ligner en normal log, bortset fra delen i starten mellem parenteserne. Dette er den centrale information, der Spring Sleuth har tilføjet. Disse data følger formatet af:

[applikationsnavn, traceId, spanId, eksport]

  • Ansøgningens navn - Dette er det navn, vi har angivet i egenskabsfilen og kan bruges til at samle logfiler fra flere forekomster af den samme applikation.
  • TraceId - Dette er et id, der er tildelt en enkelt anmodning, job eller handling. Noget som hver unik brugerinitieret webanmodning vil have sin egen sporId.
  • SpanId - Sporer en enhed af arbejde. Tænk på en anmodning, der består af flere trin. Hvert trin kunne have sit eget spanId og spores individuelt. Som standard starter ethvert applikationsflow med samme TraceId og SpanId.
  • Eksport - Denne egenskab er en boolesk, der angiver, om denne log blev eksporteret til en aggregator-lignende Zipkin. Zipkin er uden for denne artikels anvendelsesområde, men spiller en vigtig rolle i analysen af ​​logfiler oprettet af Sleuth.

Nu skal du have en idé om kraften i dette bibliotek. Lad os se på et andet eksempel for yderligere at demonstrere, hvor integreret dette bibliotek er ved logning.

3.2. Enkel webanmodning med serviceadgang

Lad os starte med at oprette en tjeneste med en enkelt metode:

@Service public class SleuthService {public void doSomeWorkSameSpan () {Thread.sleep (1000L); logger.info ("Gør noget arbejde"); }}

Lad os nu indsprøjte vores service i vores controller og tilføje en metode til kortlægning af anmodninger, der får adgang til den:

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

Til sidst skal du genstarte applikationen og navigere til “// localhost: 8080 / same-span”. Se efter logoutput, der ligner:

2017-01-10 22: 51: 47.664 INFO [Baeldung Sleuth Tutorial, b77a5ea79036d5b9, b77a5ea79036d5b9, false] 12516 --- [nio-8080-exec-3] cbspring.session.SleuthController: Samme 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: Gør noget arbejde

Bemærk, at sporings- og span-id'erne er de samme mellem de to logfiler, selvom meddelelserne stammer fra to forskellige klasser. Dette gør det trivielt at identificere hver logfil under en anmodning ved at søge efter sporId af denne anmodning.

Dette er standardadfærden, en anmodning får en enkelt sporId og spanId. Men vi kan manuelt tilføje spænd, som vi finder passende. Lad os se på et eksempel, der bruger denne funktion.

3.3. Manuel tilføjelse af et span

For at starte, lad os tilføje en ny controller:

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

Og lad os nu tilføje den nye metode i vores service:

@Autowired privat Tracer tracer; // ... offentlig ugyldighed doSomeWorkNewSpan () kaster InterruptedException {logger.info ("Jeg er i det oprindelige span"); 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 span og laver noget sejt arbejde, der har brug for sit eget span"); } endelig {newSpan.finish (); } logger.info ("Jeg er i det oprindelige tidsrum"); }

Bemærk, at vi også tilføjede et nyt objekt, Spor. Det sporstof forekomst er oprettet af Spring Sleuth under opstart og stilles til rådighed for vores klasse gennem afhængighedsinjektion.

Spor skal startes manuelt og stoppes. For at opnå dette, kode, der kører i en manuelt oprettet spændvidde er placeret inde i en prøv endelig blokere for at sikre spændvidde er lukket uanset operationens succes. Bemærk også, at nyt span skal placeres i omfanget.

Genstart applikationen og naviger til “// localhost: 8080 / new-span”. Hold øje med logoutput, der ligner:

2017-01-11 21: 07: 54.924 INFO [Baeldung Sleuth Tutorial, 9cdebbffe8bbbade, 9cdebbffe8bbbade, false] 12516 --- [nio-8080-exec-6] cbspring.session.SleuthController: Nyt span 21-01-2017 : 07: 54.924 INFO [Baeldung Sleuth Tutorial, 9cdebbffe8bbbade, 9cdebbffe8bbbade, false] 12516 --- [nio-8080-exec-6] c.baeldung.spring.session.SleuthService: Jeg er i den oprindelige periode 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 span og laver noget sejt arbejde, der har brug for sit eget span 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 oprindelige tidsrum

Vi kan se, at den tredje log deler sporId med de andre, men det har en unik spanId. Dette kan bruges til at lokalisere forskellige sektioner i en enkelt anmodning om mere finkornet sporing.

Lad os nu se på Sleuth er støtte til tråde.

3.4. Spanning Runnables

For at demonstrere threading-funktionerne i Sleuth lad os først tilføje en konfigurationsklasse for at oprette en trådpulje:

@Configuration public class ThreadConfig {@Autowired private BeanFactory beanFactory; @Bean offentlig eksekutor eksekutor () {ThreadPoolTaskExecutor threadPoolTaskExecutor = ny ThreadPoolTaskExecutor (); threadPoolTaskExecutor.setCorePoolSize (1); threadPoolTaskExecutor.setMaxPoolSize (1); threadPoolTaskExecutor.initialize (); returner ny LazyTraceExecutor (beanFactory, threadPoolTaskExecutor); }}

Det er vigtigt at bemærke brugen af LazyTraceExecutor. Denne klasse kommer fra Sleuth bibliotek og er en særlig form for eksekutor, der vil udbrede vores sporIds til nye tråde og opret nye spanIds i processen.

Lad os nu koble denne eksekutor til vores controller og bruge den i en ny metode til kortlægning af anmodninger:

@Autowired privat eksekutor eksekutor; @GetMapping ("/ new-thread") offentlig String helloSleuthNewThread () {logger.info ("Ny tråd"); Runnable runnable = () -> {prøv {Thread.sleep (1000L); } fange (InterruptedException e) {e.printStackTrace (); } logger.info ("Jeg er inde i den nye tråd - med et nyt interval"); }; eksekutor. udfør (kan køres); logger.info ("Jeg er færdig - med det originale span"); returnere "succes"; }

Med vores kørbare på plads, lad os genstarte vores applikation og navigere til “// localhost: 8080 / new-thread”. Se efter logoutput, der ligner:

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 færdig - med det originale span 2017-01-11 21: 18: 16.953 INFO [Baeldung Sleuth Tutorial, 96076a78343c364d, e3b6a68013ddfeea, false] 12516 --- [lTaskExecutor-1] cbspring.session.SleuthController: Jeg er inde i den nye tråd - med et nyt span

I lighed med det foregående eksempel kan vi se, at alle logfiler deler det samme sporId. Men loggen, der kommer fra den kørbare, har et unikt span, der sporer det arbejde, der er udført i den tråd. Husk, at dette sker på grund af LazyTraceExecutor, hvis vi skulle bruge en normal eksekutor, ville vi fortsat se det samme spanId brugt i den nye tråd.

Lad os nu undersøge det Sleuth er støtte for @Async metoder.

3.5. @Async Support

For at tilføje async-support, lad os først ændre vores Trådkonfiguration klasse for at aktivere denne funktion:

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

Bemærk, at vi udvider AsyncConfigurerSupport for at specificere vores async-eksekutor og brug LazyTraceExecutor for at sikre, at spor og spænd forplantes korrekt. Vi har også tilføjet @EnableAsync til toppen af ​​vores klasse.

Lad os nu tilføje en asynkroniseringsmetode til vores service:

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

Lad os nu kalde på denne metode fra vores controller:

@GetMapping ("/ async") offentlig String helloSleuthAsync () {logger.info ("Før Async-metodeopkald"); sleuthService.asyncMethod (); logger.info ("Efter Async-metodeopkald"); returnere "succes"; }

Lad os endelig genstarte vores service og navigere til “// localhost: 8080 / async”. Hold øje med logoutput, der ligner:

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: Efter 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 her se så meget som vores løbbare eksempel, Sleuth formerer sporId ind i async-metoden og tilføjer et unikt spanId.

Lad os nu gennemgå et eksempel ved hjælp af forårssupport til planlagte opgaver.

3.6. @ Planlagt Support

Lad os endelig se på hvordan Sleuth arbejder med @ Planlagt metoder. For at gøre dette, lad os opdatere vores Trådkonfiguration klasse for at aktivere planlægning:

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

Bemærk, at vi har implementeret SchedulingConfigurer interface og tilsidesat dens configureTasks-metode. Vi har også tilføjet @EnableScheduling til toppen af ​​vores klasse.

Lad os derefter tilføje en tjeneste til vores planlagte opgaver:

@Service offentlig klasse SchedulingService {privat Logger logger = LoggerFactory.getLogger (this.getClass ()); @Autowired privat SleuthService sleuthService; @Scheduled (fixedDelay = 30000) offentlig ugyldig planningWork () kaster InterruptedException {logger.info ("Start noget arbejde fra den planlagte opgave"); sleuthService.asyncMethod (); logger.info ("Afslut arbejde fra planlagt opgave"); }}

I denne klasse har vi oprettet en enkelt planlagt opgave med en fast forsinkelse på 30 sekunder.

Lad os nu genstarte vores applikation og vente på, at vores opgave udføres. Se konsollen for output som dette:

2017-01-11 21: 30: 58.866 INFO [Baeldung Sleuth Tutorial, 3605f5deaea28df2,3605f5deaea28df2, false] 10072 --- [pool-1-thread-1] cbspring.session.SchedulingService: Start noget arbejde fra den planlagte opgave 2017 -01-11 21: 30: 58.866 INFO [Baeldung Sleuth Tutorial, 3605f5deaea28df2,3605f5deaea28df2, false] 10072 --- [pool-1-thread-1] cbspring.session.SchedulingService: Afslut arbejde fra planlagt opgave

Det kan vi se her Sleuth har oprettet nye sporings- og span-id'er til vores opgave. Hver forekomst af en opgave får som standard sin egen sporing og spænding.

4. Konklusion

Afslutningsvis har vi set hvordan Spring Sleuth kan bruges i en række situationer i en enkelt webapplikation. Vi kan bruge denne teknologi til let at korrelere logfiler fra en enkelt anmodning, selv når den anmodning spænder over flere tråde.

Nu kan vi se hvordan Spring Cloud Sleuth kan hjælpe os med at bevare vores fornuft, når vi debugger et miljø med flere tråde. Ved at identificere hver operation i en sporId og hvert trin i en spanId vi kan virkelig begynde at nedbryde vores analyse af komplekse job i vores logfiler.

Selvom vi ikke går til skyen, Spring Sleuth er sandsynligvis en kritisk afhængighed i næsten ethvert projekt; det er problemfrit at integrere og er en massiv tilføjelse af værdi.

Herfra vil du måske undersøge andre funktioner i Sleuth. Det kan understøtte sporing i distribuerede systemer ved hjælp af RestTemplatepå tværs af beskedprotokoller, der bruges af RabbitMQ og Redisog gennem en gateway som Zuul.

Som altid kan du finde kildekoden på Github.


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