org.mpierce.gcp.logging.http.ktor.HttpRequestMetadataLogging.kt Maven / Gradle / Ivy
package org.mpierce.gcp.logging.http.ktor
import com.google.cloud.logging.HttpRequest
import io.ktor.application.Application
import io.ktor.application.ApplicationCallPipeline
import io.ktor.application.ApplicationFeature
import io.ktor.application.call
import io.ktor.features.origin
import io.ktor.http.HttpMethod
import io.ktor.request.httpMethod
import io.ktor.request.uri
import io.ktor.request.userAgent
import io.ktor.util.AttributeKey
import kotlinx.coroutines.asContextElement
import kotlinx.coroutines.withContext
import org.mpierce.gcp.logging.http.HttpRequestEnhancer
/**
* Capture request metadata so it's accessible for [HttpRequestEnhancer].
*/
class HttpRequestMetadataLogging {
companion object Feature : ApplicationFeature {
override val key: AttributeKey = AttributeKey("Http request metadata thread local")
override fun install(pipeline: Application, configure: Unit.() -> Unit): HttpRequestMetadataLogging {
val feature = HttpRequestMetadataLogging()
pipeline.intercept(ApplicationCallPipeline.Features) {
val httpReq = HttpRequest.newBuilder().run {
val method = when (call.request.httpMethod) {
HttpMethod.Get -> HttpRequest.RequestMethod.GET
HttpMethod.Head -> HttpRequest.RequestMethod.HEAD
HttpMethod.Post -> HttpRequest.RequestMethod.POST
HttpMethod.Put -> HttpRequest.RequestMethod.PUT
// RequestMethod only has prebuilt instances for a few headers
else -> HttpRequest.RequestMethod.valueOf(call.request.httpMethod.value)
}
// other request to potentially populate:
// request size
// and also a bunch of response stuff:
// status
// response seize
// latency
// cacheLookup
// cacheHit
// cacheValidated
// cacheFillBytes
// protocol
setRequestMethod(method)
setRequestUrl(call.request.uri)
setUserAgent(call.request.userAgent())
setRemoteIp(call.request.origin.remoteHost)
build()
}
withContext(HttpRequestEnhancer.HTTP_REQUEST_METADATA.asContextElement(httpReq)) {
proceed()
}
}
return feature
}
}
}