Micronaut Http Client and Server Logging
https://blog.pallav.dev/micronaut-and-httpclient-micronaut-declarative-client-and-filters
Micronaut Http Client and Server Logging
https://blog.pallav.dev/micronaut-and-httpclient-micronaut-declarative-client-and-filters
| json: | |
| server: | |
| url: https://jsonplaceholder.typicode.com |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <Configuration status="info"> | |
| <Appenders> | |
| <Console name="LogToConsole" target="SYSTEM_OUT"> | |
| <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} %X{X-API-ID} - %msg%n"/> | |
| </Console> | |
| </Appenders> | |
| <Loggers> | |
| <Logger name="io.micronaut.context" level="off" additivity="false"> | |
| <AppenderRef ref="LogToConsole"/> | |
| </Logger> | |
| <Logger name="io.micronaut.configuration" level="off" additivity="false"> | |
| <AppenderRef ref="LogToConsole"/> | |
| </Logger> | |
| <Logger name="io.micronaut.configuration.kafka" level="off" additivity="false"> | |
| <AppenderRef ref="LogToConsole"/> | |
| </Logger> | |
| <Logger name="com.richardagyei" level="info" additivity="false"> | |
| <AppenderRef ref="LogToConsole"/> | |
| </Logger> | |
| <Root level="error"> | |
| <AppenderRef ref="LogToConsole"/> | |
| </Root> | |
| </Loggers> | |
| </Configuration> |
| @Client("\${json.server.url}") | |
| interface PostClient { | |
| @Get("/posts") | |
| suspend fun getPost(@QueryValue("id") id: Int): List<Post>? | |
| } | |
| data class Post( | |
| val id: Int, | |
| val userId: Int, | |
| val title: String, | |
| val body: String | |
| ) | |
| data class Error( | |
| val status: String, | |
| val message: String | |
| ) | |
| import io.micronaut.context.event.ApplicationEventListener | |
| import io.micronaut.context.event.StartupEvent | |
| import io.micronaut.runtime.Micronaut.* | |
| import jakarta.inject.Singleton | |
| import kotlinx.coroutines.CoroutineScope | |
| import kotlinx.coroutines.Dispatchers | |
| import kotlinx.coroutines.launch | |
| fun main(args: Array<String>) { | |
| run(*args) | |
| } | |
| @Singleton | |
| class ApplicationStarted(private val postClient: PostClient) : ApplicationEventListener<StartupEvent> { | |
| override fun onApplicationEvent(event: StartupEvent?) { | |
| CoroutineScope(Dispatchers.IO).launch { | |
| postClient.getPost(1)?.forEach { p -> println(p) } | |
| } | |
| } | |
| } |
| import io.micronaut.http.HttpResponse | |
| import io.micronaut.http.MutableHttpRequest | |
| import io.micronaut.http.annotation.Filter | |
| import io.micronaut.http.filter.ClientFilterChain | |
| import io.micronaut.http.filter.HttpClientFilter | |
| import mu.KotlinLogging | |
| import org.reactivestreams.Publisher | |
| import org.slf4j.MDC | |
| import java.util.* | |
| @Filter("/**") | |
| class MDCLoggingClientFilter : HttpClientFilter { | |
| private val logger = KotlinLogging.logger { } | |
| override fun doFilter(request: MutableHttpRequest<*>, chain: ClientFilterChain): Publisher<out HttpResponse<*>> { | |
| val requestHeaders = request.headers | |
| val correlationId = | |
| requestHeaders.get(MDCServerLoggingFilter.CORRELATION_ID_KEY) | |
| ?: requestHeaders.get(MDCServerLoggingFilter.API_ID_KEY) ?: UUID.randomUUID().toString() | |
| MDC.put(MDCServerLoggingFilter.CORRELATION_ID_KEY, correlationId) | |
| MDC.put(MDCServerLoggingFilter.API_ID_KEY, correlationId) | |
| logger.info { "sending a request for ${request.uri} and path ${request.path}" } | |
| println("sending a request for ${request.uri} and path ${request.path}") | |
| request.setAttribute(MDCServerLoggingFilter.CORRELATION_ID_KEY, correlationId) | |
| request.headers.add(MDCServerLoggingFilter.API_ID_KEY, correlationId) | |
| request.headers.add(MDCServerLoggingFilter.CORRELATION_ID_KEY, correlationId) | |
| return chain.proceed(request) | |
| } | |
| } |
| import io.micronaut.http.annotation.Filter | |
| import io.micronaut.http.filter.HttpServerFilter | |
| import io.micronaut.http.filter.ServerFilterChain | |
| import mu.KotlinLogging | |
| import org.reactivestreams.Publisher | |
| import org.slf4j.MDC | |
| import reactor.core.publisher.Flux | |
| import java.util.* | |
| @Filter("/**") | |
| class MDCServerLoggingFilter : HttpServerFilter { | |
| // | |
| private val logger = KotlinLogging.logger { } | |
| companion object { | |
| const val CORRELATION_ID_KEY = "X-CORRELATION-ID" | |
| const val API_ID_KEY = "X-API-ID" | |
| } | |
| override fun doFilter(request: HttpRequest<*>, chain: ServerFilterChain): Publisher<MutableHttpResponse<*>> { | |
| val requestHeaders = request.headers | |
| val correlationId = | |
| requestHeaders.get(CORRELATION_ID_KEY) ?: requestHeaders.get(API_ID_KEY) ?: UUID.randomUUID().toString() | |
| MDC.put(CORRELATION_ID_KEY, correlationId) | |
| MDC.put(API_ID_KEY, correlationId) | |
| logger.info { "Got a request coming in ${request.uri} and path ${request.path}" } | |
| request.setAttribute(CORRELATION_ID_KEY, correlationId) | |
| return Flux.from(chain.proceed(request)) | |
| .map { res -> | |
| res.headers.add(CORRELATION_ID_KEY, correlationId) | |
| res.headers.add(API_ID_KEY, correlationId) | |
| res | |
| } | |
| } | |
| } |