How to log requests and responses in micronaut
Micronaut allows you to customize the Netty pipeline by using a bean event listener. You can therefore add custom channel handlers to the pipeline. Using this knowledge together with Logbook library you can easily log a request and the corresponding response.
1. Add logbook dependencies
Add the Logbook dependencies logbook-core
and logbook-netty
to your build.gradle
.
implementation("org.zalando:logbook-core:2.3.0") implementation("org.zalando:logbook-netty:2.3.0")
2. Add the Logbook bean
Create a bean of type Logbook
via an @Factory
annotated config class to be used by the pipeline customizer.
package mn.zalando.logbook
import io.micronaut.context.annotation.Factory
import org.zalando.logbook.Logbook
import javax.inject.Singleton
@Factory
class BeanFactory {
@Singleton
Logbook logbook() {
Logbook.create()
}
}
3. Create the ChannelPipelineCustomizer
package mn.zalando.logbook
import io.micronaut.context.annotation.Requires
import io.micronaut.context.event.BeanCreatedEvent
import io.micronaut.context.event.BeanCreatedEventListener
import io.micronaut.http.netty.channel.ChannelPipelineCustomizer
import io.netty.channel.ChannelPipeline
import org.zalando.logbook.Logbook
import org.zalando.logbook.netty.LogbookClientHandler
import org.zalando.logbook.netty.LogbookServerHandler
import javax.inject.Singleton
@Requires(beans = Logbook.class)
@Singleton
class LogbookPipelineCustomizer implements BeanCreatedEventListener<ChannelPipelineCustomizer> {
private final Logbook logbook
LogbookPipelineCustomizer(Logbook logbook) {
this.logbook = logbook
}
@Override
ChannelPipelineCustomizer onCreated(BeanCreatedEvent<ChannelPipelineCustomizer> event) {
ChannelPipelineCustomizer customizer = event.getBean()
if (customizer.isServerChannel()) {
customizer.doOnConnect({ ChannelPipeline pipeline ->
pipeline.addAfter(
ChannelPipelineCustomizer.HANDLER\_HTTP\_SERVER\_CODEC,
"logbook",
new LogbookServerHandler(logbook)
)
return pipeline
})
} else {
customizer.doOnConnect({ ChannelPipeline pipeline ->
pipeline.addAfter(
ChannelPipelineCustomizer.HANDLER\_HTTP\_SERVER\_CODEC,
"logbook",
new LogbookClientHandler(logbook))
return pipeline
})
}
return customizer
}
}
The class above implements the BeanCreatedEventListener
interface enabling you to listen for the creation of the ChannelPipelineCustomizer
bean. With ChannelPipelineCustomizer
at hand you can add custom channel handlers to the pipeline whenever a new channel is established. In this case we add LogbookServerHandler
from the Logbook
library which takes care of the logging.
If clients are sending requests with the Accept-Encoding
header, then for the server you might want to add the logbook handler after the compression handler HANDLER_HTTP_COMPRESSOR
instead of HANDLER_HTTP_SERVER_CODEC
. This ensures that the logging of the response happens before the data is compressed.
if (customizer.isServerChannel()) {
customizer.doOnConnect({ ChannelPipeline pipeline ->
pipeline.addAfter( ChannelPipelineCustomizer.HANDLER\_HTTP\_COMPRESSOR, "logbook", new LogbookServerHandler(logbook) )
return pipeline
})
}
4. Set Logbook log level
The logbook logger needs to be configured to trace level in order to log the requests and responses.
logger name="org.zalando.logbook" level="TRACE"/>
A working example using Micronaut 2.1.1 can be found on github.