Skip to content

Commit

Permalink
Add support for logback-access v2
Browse files Browse the repository at this point in the history
  • Loading branch information
croudet committed Apr 12, 2024
1 parent 5e9c076 commit 20fd5e5
Show file tree
Hide file tree
Showing 19 changed files with 483 additions and 92 deletions.
29 changes: 23 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.6</version>
<version>3.2.4</version>
</parent>

<organization>
Expand Down Expand Up @@ -127,8 +127,25 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<groupId>ch.qos.logback.access</groupId>
<artifactId>common</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback.access</groupId>
<artifactId>tomcat</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback.access</groupId>
<artifactId>jetty12</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jakarta-server</artifactId>
<version>12.0.8</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -138,7 +155,7 @@
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<version>6.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down Expand Up @@ -282,12 +299,12 @@
<dependency>
<groupId>io.gitlab.arturbosch.detekt</groupId>
<artifactId>detekt-cli</artifactId>
<version>1.23.5</version>
<version>1.23.6</version>
</dependency>
<dependency>
<groupId>io.gitlab.arturbosch.detekt</groupId>
<artifactId>detekt-formatting</artifactId>
<version>1.23.5</version>
<version>1.23.6</version>
</dependency>
</dependencies>
<configuration>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.akkinoc.spring.boot.logback.access

import ch.qos.logback.access.spi.AccessContext
import ch.qos.logback.access.common.spi.AccessContext
import ch.qos.logback.core.spi.FilterReply
import ch.qos.logback.core.status.Status
import dev.akkinoc.spring.boot.logback.access.LogbackAccessProperties.Companion.DEFAULT_CONFIGS
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package dev.akkinoc.spring.boot.logback.access

import ch.qos.logback.access.spi.IAccessEvent
import ch.qos.logback.access.spi.IAccessEvent.NA
import ch.qos.logback.access.spi.IAccessEvent.SENTINEL
import ch.qos.logback.access.spi.ServerAdapter
import ch.qos.logback.access.common.spi.IAccessEvent
import ch.qos.logback.access.common.spi.IAccessEvent.NA
import ch.qos.logback.access.common.spi.IAccessEvent.SENTINEL
import ch.qos.logback.access.common.spi.ServerAdapter
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import java.io.IOException
Expand All @@ -17,7 +17,7 @@ import java.util.concurrent.TimeUnit.MILLISECONDS
* The Logback-access event.
*
* @property source The Logback-access event source.
* @see ch.qos.logback.access.spi.AccessEvent
* @see ch.qos.logback.access.common.spi.AccessEvent
*/
class LogbackAccessEvent(private var source: LogbackAccessEventSource) : IAccessEvent, Serializable {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package dev.akkinoc.spring.boot.logback.access

import ch.qos.logback.access.spi.IAccessEvent
import ch.qos.logback.access.spi.ServerAdapter
import ch.qos.logback.access.common.spi.IAccessEvent
import ch.qos.logback.access.common.spi.ServerAdapter
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import java.io.Serializable
Expand All @@ -11,7 +11,7 @@ import java.io.Serializable
* Represents the attributes of [IAccessEvent] by Kotlin properties,
* which helps to implement subclasses with Kotlin delegated properties (especially [lazy]).
*
* @see ch.qos.logback.access.spi.IAccessEvent
* @see ch.qos.logback.access.common.spi.IAccessEvent
*/
abstract class LogbackAccessEventSource {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.akkinoc.spring.boot.logback.access

import ch.qos.logback.access.spi.IAccessEvent
import ch.qos.logback.access.common.spi.IAccessEvent
import dev.akkinoc.spring.boot.logback.access.value.LogbackAccessLocalPortStrategy
import io.undertow.UndertowOptions
import org.apache.catalina.valves.RemoteIpValve
Expand Down
Original file line number Diff line number Diff line change
@@ -1,87 +1,91 @@
package dev.akkinoc.spring.boot.logback.access.jetty

import ch.qos.logback.access.AccessConstants.LB_INPUT_BUFFER
import ch.qos.logback.access.AccessConstants.LB_OUTPUT_BUFFER
import ch.qos.logback.access.common.AccessConstants.LB_INPUT_BUFFER
import ch.qos.logback.access.common.AccessConstants.LB_OUTPUT_BUFFER
import ch.qos.logback.access.common.servlet.Util.isFormUrlEncoded
import ch.qos.logback.access.jetty.JettyModernServerAdapter
import ch.qos.logback.access.jetty.JettyServerAdapter
import ch.qos.logback.access.servlet.Util.isFormUrlEncoded
import ch.qos.logback.access.servlet.Util.isImageResponse
import ch.qos.logback.access.jetty.ResponseWrapper
import dev.akkinoc.spring.boot.logback.access.LogbackAccessContext
import dev.akkinoc.spring.boot.logback.access.LogbackAccessEventSource
import dev.akkinoc.spring.boot.logback.access.LogbackAccessHackyLoggingOverrides.overriddenRequestBody
import dev.akkinoc.spring.boot.logback.access.LogbackAccessHackyLoggingOverrides.overriddenResponseBody
import dev.akkinoc.spring.boot.logback.access.security.LogbackAccessSecurityServletFilter.Companion.REMOTE_USER_ATTRIBUTE
import dev.akkinoc.spring.boot.logback.access.value.LogbackAccessLocalPortStrategy
import org.eclipse.jetty.http.HttpHeader
import org.eclipse.jetty.server.Request
import org.eclipse.jetty.server.Response
import org.eclipse.jetty.session.DefaultSessionIdManager
import java.lang.System.currentTimeMillis
import java.lang.Thread.currentThread
import java.net.URLEncoder.encode
import java.util.Collections.unmodifiableList
import java.util.Collections.unmodifiableMap
import kotlin.text.Charsets.UTF_8

/**
* The Logback-access event source for the Jetty web server.
*
* @property logbackAccessContext The Logback-access context.
* @see ch.qos.logback.access.spi.AccessEvent
* @see ch.qos.logback.access.common.spi.AccessEvent
* @see ch.qos.logback.access.jetty.JettyServerAdapter
* @see ch.qos.logback.access.PatternLayout
* @see ch.qos.logback.access.common.PatternLayout
* @see org.eclipse.jetty.server.CustomRequestLog
*/
class LogbackAccessJettyEventSource(
private val logbackAccessContext: LogbackAccessContext,
override val request: Request,
override val response: Response,
private val rawRequest: Request,
private val rawResponse: Response,
override val request: RequestWrapper,
override val response: ResponseWrapper
) : LogbackAccessEventSource() {

override val serverAdapter: JettyServerAdapter = JettyServerAdapter(request, response)
override val serverAdapter: JettyServerAdapter = JettyModernServerAdapter(rawRequest, rawResponse)

override val timeStamp: Long = currentTimeMillis()

override val elapsedTime: Long = timeStamp - request.timeStamp
override val elapsedTime: Long = timeStamp - Request.getTimeStamp(rawRequest)

override val sequenceNumber: Long? = logbackAccessContext.raw.sequenceNumberGenerator?.nextSequenceNumber()

override val threadName: String = currentThread().name

override val serverName: String by lazy(LazyThreadSafetyMode.NONE) {
request.serverName
Request.getServerName(rawRequest)
}

override val localPort: Int by lazy(LazyThreadSafetyMode.NONE) {
when (logbackAccessContext.properties.localPortStrategy) {
LogbackAccessLocalPortStrategy.LOCAL -> request.localPort
LogbackAccessLocalPortStrategy.SERVER -> request.serverPort
LogbackAccessLocalPortStrategy.LOCAL -> Request.getLocalPort(rawRequest)
LogbackAccessLocalPortStrategy.SERVER -> Request.getServerPort(rawRequest)
}
}

override val remoteAddr: String by lazy(LazyThreadSafetyMode.NONE) {
request.remoteAddr
Request.getRemoteAddr(rawRequest)
}

override val remoteHost: String by lazy(LazyThreadSafetyMode.NONE) {
request.remoteHost
Request.getRemoteAddr(rawRequest)
}

override val remoteUser: String? by lazy(LazyThreadSafetyMode.NONE) {
request.getAttribute(REMOTE_USER_ATTRIBUTE) as String? ?: request.remoteUser
rawRequest.getAttribute(REMOTE_USER_ATTRIBUTE) as String?
}

override val protocol: String by lazy(LazyThreadSafetyMode.NONE) {
request.protocol
rawRequest.connectionMetaData.protocol
}

override val method: String by lazy(LazyThreadSafetyMode.NONE) {
request.method
rawRequest.method
}

override val requestURI: String? by lazy(LazyThreadSafetyMode.NONE) {
request.requestURI
rawRequest.httpURI.path
}

override val queryString: String by lazy(LazyThreadSafetyMode.NONE) {
request.queryString?.let { "?$it" }.orEmpty()
rawRequest.httpURI.query?.let { "?$it" }.orEmpty()
}

override val requestURL: String by lazy(LazyThreadSafetyMode.NONE) {
Expand All @@ -90,37 +94,37 @@ class LogbackAccessJettyEventSource(

override val requestHeaderMap: Map<String, String> by lazy(LazyThreadSafetyMode.NONE) {
val headers = sortedMapOf<String, String>(String.CASE_INSENSITIVE_ORDER)
request.headerNames.asSequence().associateWithTo(headers) { request.getHeader(it) }
rawRequest.headers.fieldNamesCollection.associateByTo(headers, { it }, { rawRequest.headers[it] })
unmodifiableMap(headers)
}

override val cookieMap: Map<String, String> by lazy(LazyThreadSafetyMode.NONE) {
val cookies = linkedMapOf<String, String>()
request.cookies.orEmpty().associateTo(cookies) { it.name to it.value }
Request.getCookies(rawRequest).associateByTo(cookies, { it.name }, { it.value })
unmodifiableMap(cookies)
}

override val requestParameterMap: Map<String, List<String>> by lazy(LazyThreadSafetyMode.NONE) {
val params = linkedMapOf<String, List<String>>()
request.parameterMap.mapValuesTo(params) { unmodifiableList(it.value.asList()) }
Request.getParameters(rawRequest).associateByTo(params, { it.name }, { it.values })
unmodifiableMap(params)
}

override val attributeMap: Map<String, String> by lazy(LazyThreadSafetyMode.NONE) {
val attrs = linkedMapOf<String, String>()
request.attributeNames.asSequence()
rawRequest.attributeNameSet.asSequence()
.filter { it !in setOf(LB_INPUT_BUFFER, LB_OUTPUT_BUFFER) }
.associateWithTo(attrs) { "${request.getAttribute(it)}" }
.associateWithTo(attrs) { "${rawRequest.getAttribute(it)}" }
unmodifiableMap(attrs)
}

override val sessionID: String? by lazy(LazyThreadSafetyMode.NONE) {
request.getSession(false)?.id
rawRequest.getAttribute(DefaultSessionIdManager.__NEW_SESSION_ID)?.toString()
}

override val requestContent: String? by lazy(LazyThreadSafetyMode.NONE) {
overriddenRequestBody(request)?.also { return@lazy it }
val bytes = request.getAttribute(LB_INPUT_BUFFER) as ByteArray?
val bytes = rawRequest.getAttribute(LB_INPUT_BUFFER) as ByteArray?
if (bytes == null && isFormUrlEncoded(request)) {
return@lazy requestParameterMap.asSequence()
.flatMap { (key, values) -> values.asSequence().map { key to it } }
Expand All @@ -131,23 +135,24 @@ class LogbackAccessJettyEventSource(
}

override val statusCode: Int by lazy(LazyThreadSafetyMode.NONE) {
response.status
rawResponse.status
}

override val responseHeaderMap: Map<String, String> by lazy(LazyThreadSafetyMode.NONE) {
val headers = sortedMapOf<String, String>(String.CASE_INSENSITIVE_ORDER)
response.headerNames.associateWithTo(headers) { response.getHeader(it) }
rawResponse.headers.fieldNamesCollection.associateByTo(headers, { it }, { rawResponse.headers[it] })
unmodifiableMap(headers)
}

override val contentLength: Long by lazy(LazyThreadSafetyMode.NONE) {
response.contentCount
Response.getContentBytesWritten(rawResponse)
}

override val responseContent: String? by lazy(LazyThreadSafetyMode.NONE) {
overriddenResponseBody(request, response)?.also { return@lazy it }
if (isImageResponse(response)) return@lazy "[IMAGE CONTENTS SUPPRESSED]"
val bytes = request.getAttribute(LB_OUTPUT_BUFFER) as ByteArray?
val contentType = rawResponse.headers[HttpHeader.CONTENT_TYPE]
if (contentType != null && contentType.startsWith("image/")) return@lazy "[IMAGE CONTENTS SUPPRESSED]"
val bytes = rawRequest.getAttribute(LB_OUTPUT_BUFFER) as ByteArray?
bytes?.let { String(it, UTF_8) }
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.akkinoc.spring.boot.logback.access.jetty

import ch.qos.logback.access.jetty.ResponseWrapper
import dev.akkinoc.spring.boot.logback.access.LogbackAccessContext
import dev.akkinoc.spring.boot.logback.access.LogbackAccessEvent
import org.eclipse.jetty.server.Request
Expand Down Expand Up @@ -30,8 +31,10 @@ class LogbackAccessJettyRequestLog(
)
val source = LogbackAccessJettyEventSource(
logbackAccessContext = logbackAccessContext,
request = request,
response = response,
rawRequest = request,
rawResponse = response,
request = RequestWrapper(request),
response = ResponseWrapper(response),
)
val event = LogbackAccessEvent(source)
logbackAccessContext.emit(event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package dev.akkinoc.spring.boot.logback.access.jetty

import dev.akkinoc.spring.boot.logback.access.LogbackAccessContext
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.handler.RequestLogHandler
import org.slf4j.Logger
import org.slf4j.LoggerFactory.getLogger
import org.springframework.boot.web.embedded.jetty.ConfigurableJettyWebServerFactory
Expand Down Expand Up @@ -34,10 +33,7 @@ class LogbackAccessJettyWebServerFactoryCustomizer(
* @param server The [Server].
*/
private fun customize(server: Server) {
val handler = RequestLogHandler().apply {
requestLog = LogbackAccessJettyRequestLog(logbackAccessContext)
}
server.insertHandler(handler)
server.setRequestLog(LogbackAccessJettyRequestLog(logbackAccessContext))
log.debug(
"Customized the {}: {} @{}",
Server::class.simpleName,
Expand Down

0 comments on commit 20fd5e5

Please sign in to comment.