Skip to content

Commit 198eb9b

Browse files
author
Sylvain Becuwe
committed
feat: Added more routes to test more behaviors
1 parent 6965b23 commit 198eb9b

File tree

7 files changed

+101
-14
lines changed

7 files changed

+101
-14
lines changed

README.md

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,37 @@ This project is an attempt of using [Micrometer Tracing](https://github.com/micr
99
- [Zipkin Brave](https://github.com/openzipkin/brave)
1010
- [Zalando Logbook](https://github.com/zalando/logbook)
1111

12-
This might also be useful for those migrating *Spring Boot 2 & Spring Sleuth* to *Spring Boot 3* which has breaking changes about tracing.
12+
This might also be useful for those migrating *Spring Boot 2 & Spring Sleuth* to *Spring Boot 3* which has breaking changes about tracing.<br/>
13+
14+
The aim is to manage to have standard baggage (traceId and spanId) and custom baggage (myBaggageFilter and myBaggageController) logged in every single log of the application.
15+
16+
Below are a list of issues related to this mechanism not working properly:
17+
- https://github.com/openzipkin/brave/issues/1376
18+
- https://github.com/zalando/logbook/issues/1513
19+
- https://github.com/zalando/logbook/issues/1513
20+
- https://github.com/micrometer-metrics/tracing/issues/174
1321

1422
# How to test the application
15-
Run `./gradlew bootRun` then call the local server: `curl --location 'http://localhost:8080/test' --header 'Authorization: Basic dXNlcjp1c2Vy'`
23+
Run `./gradlew bootRun` then call the local server depending on the behavior you want to test:
24+
## Simple
25+
This will test the following behavior:
26+
1) Write a log in the controller
27+
2) Make a **webClient** call
28+
29+
`curl --location 'http://localhost:8080/simple' --header 'Authorization: Basic dXNlcjp1c2Vy'`
30+
31+
## Suspend
32+
This will test the following behavior:
33+
1) Simulate some delay inside the coroutine so that kotlin suspends the function then resume it
34+
2) Write a log in the controller
35+
3) Make a **webClient** call
36+
37+
`curl --location 'http://localhost:8080/suspend' --header 'Authorization: Basic dXNlcjp1c2Vy'`
38+
39+
## Coroutine
40+
This will test the following behavior:
41+
1) Start a new coroutine
42+
2) Write a log in the controller
43+
3) Make a **webClient** call
44+
45+
`curl --location 'http://localhost:8080/coroutine' --header 'Authorization: Basic dXNlcjp1c2Vy'`

app/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
22
import org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
33

44
plugins {
5-
id("org.springframework.boot") version "3.1.3"
5+
id("org.springframework.boot") version "3.1.4"
66
kotlin("jvm") version "1.9.10"
77
kotlin("plugin.spring") version "1.9.10"
88
}
@@ -27,12 +27,14 @@ dependencies {
2727
runtimeOnly("org.springframework.boot:spring-boot-starter-actuator")
2828
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor:1.7.3")
2929
implementation("org.jetbrains.kotlin:kotlin-reflect")
30+
implementation("io.micrometer:micrometer-core")
3031
implementation("io.micrometer:micrometer-tracing-bridge-brave") {
3132
exclude(group = "io.zipkin.reporter2", module = "zipkin-reporter-brave")
3233
}
3334
implementation("io.github.microutils:kotlin-logging:3.0.5")
3435
implementation("org.zalando:logbook-spring-boot-starter")
3536
implementation("org.zalando:logbook-spring-boot-webflux-autoconfigure")
37+
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-slf4j:1.7.3")
3638
compileOnly("org.springframework.boot:spring-boot-devtools")
3739
testImplementation("org.springframework.boot:spring-boot-starter-test")
3840
}

app/src/main/kotlin/com/grassehh/app/configuration/SecurityConfiguration.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.grassehh.app.configuration
22

33
import com.grassehh.app.filter.AppWebFilter
4+
import io.micrometer.observation.ObservationRegistry
45
import io.micrometer.tracing.Tracer
56
import org.springframework.context.annotation.Bean
67
import org.springframework.context.annotation.Configuration
@@ -28,14 +29,15 @@ class SecurityConfiguration {
2829
)
2930

3031
@Bean
31-
fun springSecurityFilterChain(http: ServerHttpSecurity, tracer: Tracer) =
32+
fun springSecurityFilterChain(http: ServerHttpSecurity, tracer: Tracer, observationRegistry: ObservationRegistry) =
3233
http
3334
.authorizeExchange { exchanges: AuthorizeExchangeSpec ->
3435
exchanges
3536
.anyExchange().authenticated()
3637
}
3738
.httpBasic(withDefaults())
3839
.formLogin(withDefaults())
40+
// .addFilterBefore(CoroutineWebFilter(observationRegistry), FIRST)
3941
.addFilterBefore(AppWebFilter(tracer), HTTP_BASIC)
4042
.build()
4143
}

app/src/main/kotlin/com/grassehh/app/configuration/TracingConfiguration.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package com.grassehh.app.configuration
22

3+
import io.micrometer.context.ContextRegistry
34
import io.micrometer.context.ContextSnapshotFactory
45
import io.micrometer.observation.ObservationRegistry
56
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor
67
import io.micrometer.tracing.Tracer
8+
import io.micrometer.tracing.contextpropagation.ObservationAwareSpanThreadLocalAccessor
79
import io.netty.channel.ChannelDuplexHandler
810
import io.netty.channel.ChannelHandlerContext
911
import io.netty.channel.ChannelPromise
@@ -21,7 +23,7 @@ class TracingConfiguration(private val observationRegistry: ObservationRegistry,
2123
@PostConstruct
2224
fun postConstruct() {
2325
Hooks.enableAutomaticContextPropagation()
24-
// ContextRegistry.getInstance().registerThreadLocalAccessor(ObservationAwareSpanThreadLocalAccessor(tracer));
26+
ContextRegistry.getInstance().registerThreadLocalAccessor(ObservationAwareSpanThreadLocalAccessor(tracer));
2527
ObservationThreadLocalAccessor.getInstance().observationRegistry = observationRegistry
2628
Metrics.observationRegistry(observationRegistry)
2729
}
Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,65 @@
11
package com.grassehh.app.controller
22

3+
import io.micrometer.core.instrument.kotlin.asContextElement
4+
import io.micrometer.observation.ObservationRegistry
35
import io.micrometer.tracing.Tracer
6+
import kotlinx.coroutines.*
47
import mu.KotlinLogging.logger
58
import org.springframework.http.HttpStatus.OK
69
import org.springframework.security.access.prepost.PreAuthorize
710
import org.springframework.web.bind.annotation.GetMapping
8-
import org.springframework.web.bind.annotation.RequestMapping
911
import org.springframework.web.bind.annotation.ResponseStatus
1012
import org.springframework.web.bind.annotation.RestController
1113
import org.springframework.web.reactive.function.client.WebClient
1214
import org.springframework.web.reactive.function.client.awaitExchange
1315
import java.util.*
1416

1517
@RestController
16-
@RequestMapping("/test")
17-
class SampleController(private val webClient: WebClient, private val tracer: Tracer) {
18+
class SampleController(
19+
private val webClient: WebClient,
20+
private val tracer: Tracer,
21+
private val observationRegistry: ObservationRegistry
22+
) {
1823
companion object {
1924
private val logger = logger {}
2025
}
2126

22-
@GetMapping
27+
@GetMapping("simple")
2328
@ResponseStatus(OK)
2429
@PreAuthorize("hasRole('USER')")
25-
suspend fun test() {
30+
suspend fun simple() {
2631
tracer.createBaggageInScope("myBaggageController", UUID.randomUUID().toString())
27-
logger.debug { "Log from inside the controller. This should contain the baggage called myBaggageController" }
28-
webClient.get().uri { it.scheme("https").host("google.com").build() }
32+
logger.debug { "CONTROLLER LOG" }
33+
webClient.get()
34+
.uri("https://www.google.fr")
2935
.awaitExchange { }
3036
}
31-
}
37+
38+
@GetMapping("suspend")
39+
@ResponseStatus(OK)
40+
@PreAuthorize("hasRole('USER')")
41+
suspend fun suspend() {
42+
tracer.createBaggageInScope("myBaggageController", UUID.randomUUID().toString())
43+
withContext(observationRegistry.asContextElement()) {
44+
delay(500)
45+
logger.debug { "CONTROLLER LOG INSIDE withContext()" }
46+
}
47+
logger.debug { "CONTROLLER LOG OUTSIDE withContext()" }
48+
webClient.get()
49+
.uri("https://www.google.fr")
50+
.awaitExchange { }
51+
}
52+
53+
@GetMapping("coroutine")
54+
@ResponseStatus(OK)
55+
@PreAuthorize("hasRole('USER')")
56+
suspend fun coroutine() {
57+
CoroutineScope(SupervisorJob()).launch(observationRegistry.asContextElement()) {
58+
tracer.createBaggageInScope("myBaggageController", UUID.randomUUID().toString())
59+
logger.debug { "CONTROLLER LOG" }
60+
webClient.get()
61+
.uri("https://www.google.fr")
62+
.awaitExchange { }
63+
}
64+
}
65+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.grassehh.app.filter
2+
3+
import io.micrometer.core.instrument.kotlin.asContextElement
4+
import io.micrometer.observation.ObservationRegistry
5+
import kotlinx.coroutines.withContext
6+
import org.springframework.web.server.CoWebFilter
7+
import org.springframework.web.server.CoWebFilterChain
8+
import org.springframework.web.server.ServerWebExchange
9+
10+
11+
class CoroutineWebFilter(private val observationRegistry: ObservationRegistry) : CoWebFilter() {
12+
override suspend fun filter(exchange: ServerWebExchange, chain: CoWebFilterChain) {
13+
withContext(observationRegistry.asContextElement()) {
14+
chain.filter(exchange)
15+
}
16+
}
17+
}

app/src/main/resources/application.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ logging:
1717
com.grassehh: debug
1818
org.zalando.logbook: trace
1919
pattern:
20-
console: "MDC Context: [%mdc] - %clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"
20+
console: "MDC:[%mdc] %clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"
2121

2222
logbook:
2323
strategy: status-at-least

0 commit comments

Comments
 (0)