【发布时间】:2021-06-20 14:57:42
【问题描述】:
有什么方法可以记录来自 ktor 服务器通信的请求和响应正文? 内置 CallLogging 功能仅记录呼叫的元数据。我尝试编写自己的日志记录功能,如下例所示:https://github.com/Koriit/ktor-logging/blob/master/src/main/kotlin/korrit/kotlin/ktor/features/logging/Logging.kt
class Logging(private val logger: Logger) {
class Configuration {
var logger: Logger = LoggerFactory.getLogger(Logging::class.java)
}
private suspend fun logRequest(call: ApplicationCall) {
logger.info(StringBuilder().apply {
appendLine("Received request:")
val requestURI = call.request.path()
appendLine(call.request.origin.run { "${method.value} $scheme://$host:$port$requestURI $version" })
call.request.headers.forEach { header, values ->
appendLine("$header: ${values.firstOrNull()}")
}
try {
appendLine()
appendLine(String(call.receive<ByteArray>()))
} catch (e: RequestAlreadyConsumedException) {
logger.error("Logging payloads requires DoubleReceive feature to be installed with receiveEntireContent=true", e)
}
}.toString())
}
private suspend fun logResponse(call: ApplicationCall, subject: Any) {
logger.info(StringBuilder().apply {
appendLine("Sent response:")
appendLine("${call.request.httpVersion} ${call.response.status()}")
call.response.headers.allValues().forEach { header, values ->
appendLine("$header: ${values.firstOrNull()}")
}
when (subject) {
is TextContent -> appendLine(subject.text)
is OutputStreamContent -> appendLine() // ToDo: How to get response body??
else -> appendLine("unknown body type")
}
}.toString())
}
/**
* Feature installation.
*/
fun install(pipeline: Application) {
pipeline.intercept(ApplicationCallPipeline.Monitoring) {
logRequest(call)
proceedWith(subject)
}
pipeline.sendPipeline.addPhase(responseLoggingPhase)
pipeline.sendPipeline.intercept(responseLoggingPhase) {
logResponse(call, subject)
}
}
companion object Feature : ApplicationFeature<Application, Configuration, Logging> {
override val key = AttributeKey<Logging>("Logging Feature")
val responseLoggingPhase = PipelinePhase("ResponseLogging")
override fun install(pipeline: Application, configure: Configuration.() -> Unit): Logging {
val configuration = Configuration().apply(configure)
return Logging(configuration.logger).apply { install(pipeline) }
}
}
}
它适用于使用 DoubleReceive 插件记录请求正文。如果响应是纯文本,我可以将响应记录为 sendPipeline 拦截中的主题将是 TextContent 类型或类似于示例 ByteArrayContent 中的类型。
但就我而言,我正在使用 Jackson ContentNegotiation 响应数据类实例。在这种情况下,主题是 OutputStreamContent 类型,我看不到从中获取序列化正文的选项。
知道如何在我的日志记录功能中记录序列化响应 json 吗?或者也许还有另一个使用 ktor 服务器的选项?我的意思是我可以手动序列化我的对象并响应纯文本,但这是一种丑陋的方式。
【问题讨论】:
-
你看到这个属性了吗? /** * 是否记录请求/响应负载。 * 警告:有效载荷可能包含敏感数据。 */ var logBody = false 。您还可以自定义记录器:ktor.io/docs/call-logging.html#format install(CallLogging) { format { call -> val status = call.response.status() val httpMethod = call.request.httpMethod.value val userAgent = call.request.headers[" User-Agent"] "状态:$status,HTTP 方法:$httpMethod,用户代理:$userAgent" } } 您也可以设置记录器级别。