Skip to content

Instantly share code, notes, and snippets.

@vegaasen
Last active November 1, 2023 15:08
Show Gist options
  • Save vegaasen/df4037d335fb25fc42f652e78984d027 to your computer and use it in GitHub Desktop.
Save vegaasen/df4037d335fb25fc42f652e78984d027 to your computer and use it in GitHub Desktop.
Spring boot webflux and Micronaut netty access log config

Micronaut

application.conf-file

micronaut {
...
  server {
...
    netty {
      access-logger {
        logger-name = "access-log"
        enabled = true
        log-format = %a - - %t '%r' %s %b "%{referer}i" "%{user-agent}i" "CID:%{x-request-id}o" %D
      }
    }
  }
...
}

log4j2.xml-file

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="console" target="SYSTEM_OUT">
      <JSONLayout compact="true" eventEol="true" properties="true" locationInfo="true" stacktraceAsString="true">
        <KeyValuePair key="time" value="$${date:yyyy-MM-dd'T'HH:mm:ss.SSSZ}"/>
      </JSONLayout>
    </Console>
    <Console name="accessLog" target="SYSTEM_OUT">
      <PatternLayout pattern="%m%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="warn">
    </Root>
    <!--👇 the important entry right here, yo 👇-->
    <logger name="access-log" level="info">
      <AppenderRef ref="accessLog"/>
    </logger>
    <Logger name="io.micronaut" level="info">
      <AppenderRef ref="console"/>
    </Logger>
    <Logger name="io.netty" level="info">
      <AppenderRef ref="console"/>
    </Logger>
    <Logger name="io.github" level="info">
      <AppenderRef ref="console"/>
    </Logger>
    <Logger name="kotlinx.coroutines" level="info">
      <AppenderRef ref="console"/>
    </Logger>
    <Logger name="no.vegaasen" level="info">
      <AppenderRef ref="console"/>
    </Logger>
  </Loggers>
</Configuration>

Spring Boot Webflux (Netty reactive)

Wut?

Got a Spring Boot application based on WebFlux, Netty and Reactive? Require to change some logging format stuff? Just use a variation of this logger 🔥 💥.

Note: This example assumes Log4j2.

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <Console name="console-json" target="SYSTEM_OUT">
      <JSONLayout compact="true" eventEol="true" properties="true" locationInfo="true" stacktraceAsString="true">
        <KeyValuePair key="time" value="$${date:yyyy-MM-dd'T'HH:mm:ss.SSSZ}"/>
      </JSONLayout>
    </Console>
    <Console name="console-plain" target="SYSTEM_OUT">
      <PatternLayout pattern="%m%n"/>
    </Console>
  </Appenders>
  <Loggers>
    <Root level="INFO"/>
    <Logger name="org.springframework.web.servlet.PageNotFound" level="ERROR">
      <AppenderRef ref="console-json"/>
    </Logger>
    <Logger name="org.springframework" level="WARN">
      <AppenderRef ref="console-json"/>
    </Logger>
    <Logger name="reactor.netty.http.server.AccessLog" level="INFO">
      <AppenderRef ref="console-plain"/>
    </Logger>
    <Logger name="no.whatever" level="INFO">
      <AppenderRef ref="console-json"/>
    </Logger>
    <Logger name="liquibase" level="INFO">
      <AppenderRef ref="console-json"/>
    </Logger>
  </Loggers>
</Configuration>

AccessLogConfiguration.kt

package no.whatever.config

import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.context.annotation.Configuration
import reactor.netty.http.server.logging.AccessLog
import java.net.InetSocketAddress
import java.net.SocketAddress
import java.time.format.DateTimeFormatter

private const val unknown = "-"
private val accessTimeFormatter = DateTimeFormatter.ofPattern("dd/MMM/yyyy:HH:mm:ss Z")
private fun SocketAddress?.ipAddress() = if (this is InetSocketAddress) hostString else unknown
private fun AccessLogArgProvider.correlationId() = requestHeader(header_x_request_id)?.toString()?.also(::correlationIdDefine)

@Configuration
class AccessLogConfiguration : WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
  override fun customize(factory: NettyReactiveWebServerFactory) {
    factory.addServerCustomizers({ server ->
      server.accessLog(true) { provider ->
        AccessLog.create(
          "{} - {} [{}] \"{} {} {}\" {} {} {} \"{}\" \"{}\" {}",
          provider.remoteAddress().ipAddress(),
          provider.user(),
          provider.accessDateTime()?.format(accessTimeFormatter).orEmpty(),
          provider.method(),
          provider.uri(),
          provider.protocol(),
          provider.status(),
          if (provider.contentLength() > -1) provider.contentLength() else unknown,
          unknown,
          provider.requestHeader("User-Agent") ?: unknown,
          provider.correlationId()?.let { "CID:$it" } ?: unknown,
          provider.duration()
        )
      }
    })
  }
}

Notes

You do no longer need to enable the access log explicitly either, as it is already configured by your configuration.

// remove this one, if you have it 👇
private fun SpringApplicationBuilder.useNettyLogging() = also { setProperty(ACCESS_LOG_ENABLED, "false") }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment