package kiso.io.http.plugin
import io.github.oshai.kotlinlogging.KLogger
import io.github.oshai.kotlinlogging.KotlinLogging
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.util.*
import kiso.io.http.engineName
import kotlin.time.Duration
import kotlin.time.TimeMark
import kotlin.time.TimeSource
public fun HttpRequestBuilder.disableCallLogging() {
attributes.put(CallLogging.Ignore, true)
}
public data class CallLogging(
private val logger: KLogger,
private val enabled: Boolean = true,
private val formatRequest: (engine: String, request: HttpRequestBuilder) -> String =
{ e, r -> "[Http/Ktor: $e] <- ${r.method.value} ${r.url.buildString()}" },
private val formatResponse: (engine: String, response: HttpResponse, took: Duration) -> String =
{ e, r, t -> "[Http/Ktor: $e] <- ${r.request.method.value} ${r.request.url} ${r.status} - $t" }
) {
public class Config {
public var log: KLogger = KotlinLogging.logger { }
public var enabled: Boolean = true
}
public fun sent(request: HttpRequestBuilder, client: HttpClient) {
request.attributes.put(StartTime, TimeSource.Monotonic.markNow())
if (request.attributes.getOrNull(Ignore) != true && enabled) {
logger.trace { formatRequest(client.engineName, request) }
}
}
public fun received(call: HttpClientCall) {
/* calculate duration */
val startTime = call.request.attributes[StartTime]
val duration = startTime.elapsedNow()
/* print out information */
if (call.request.attributes.getOrNull(Ignore) != true && enabled) {
logger.trace { formatResponse(call.client.engineName, call.response, duration) }
}
}
public companion object Plugin : HttpClientPlugin<Config, CallLogging> {
internal val StartTime = AttributeKey<TimeMark>("Call-Start-Time")
public val Ignore: AttributeKey<Boolean> = AttributeKey("Call-Logging-Ignore")
public override val key: AttributeKey<CallLogging> = AttributeKey("Call-Logging")
override fun prepare(block: Config.() -> Unit): CallLogging {
val config = Config().apply(block)
return CallLogging(config.log)
}
override fun install(plugin: CallLogging, scope: HttpClient) {
scope.requestPipeline.intercept(HttpRequestPipeline.Before) {
plugin.sent(context, scope)
}
scope.responsePipeline.intercept(HttpResponsePipeline.Receive) {
plugin.received(context)
}
}
}
}