Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for logback-access v2 #407

Merged
merged 3 commits into from
Jul 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 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.11</version>
<version>3.3.0</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>
akkinoc marked this conversation as resolved.
Show resolved Hide resolved
<dependency>
<groupId>org.eclipse.jetty.ee10.websocket</groupId>
<artifactId>jetty-ee10-websocket-jakarta-server</artifactId>
<version>12.0.9</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
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
Loading