Как я могу переопределить logRequest/logResponse для регистрации пользовательского сообщения в журнале клиента Ktor?

В настоящее время реализация ведения журнала клиента ktor приведена ниже, и она работает так, как задумано, но не то, что я хотел иметь.


public class Logging(
    public val logger: Logger,
    public var level: LogLevel,
    public var filters: List<(HttpRequestBuilder) -> Boolean> = emptyList()
)
....
   private suspend fun logRequest(request: HttpRequestBuilder): OutgoingContent? {
        if (level.info) {
            logger.log("REQUEST: ${Url(request.url)}")
            logger.log("METHOD: ${request.method}")
        }

        val content = request.body as OutgoingContent

        if (level.headers) {
            logger.log("COMMON HEADERS")
            logHeaders(request.headers.entries())

            logger.log("CONTENT HEADERS")
            logHeaders(content.headers.entries())
        }

        return if (level.body) {
            logRequestBody(content)
        } else null
    }

Выше создает кошмар при просмотре журналов, потому что он регистрируется в каждой строке. Поскольку я новичок в Kotlin и Ktor, мне бы хотелось узнать, как изменить его поведение. Поскольку в Котлине все классы являются окончательными, если не открываются специально, я не знаю, как подойти к изменению logRequestповедение функции. В идеале я хотел добиться чего-то вроде примера ниже.

....
private suspend fun logRequest(request: HttpRequestBuilder): OutgoingContent? {

        ...
        if (level.body) {
            val content = request.body as OutgoingContent
        return logger.log(value("url", Url(request.url)),
                          value("method", request.method),
                          value("body", content))


    }

Любая помощь будет признательна

2 ответа

Невозможно фактически переопределить частный метод в закрытом классе, но если вы просто хотите, чтобы ваше ведение журнала работало по-другому, вам лучше использовать собственный перехватчик того же этапа в конвейере:

       val client = HttpClient(CIO) {
            install("RequestLogging") {
                sendPipeline.intercept(HttpSendPipeline.Monitoring) {
                    logger.info(
                        "Request: {} {} {} {}",
                        context.method,
                        Url(context.url),
                        context.headers.entries(),
                        context.body
                    )
                }
            }
        }
        runBlocking {
            client.get<String>("https://google.com")
        }

Это приведет к желаемой регистрации. Конечно, чтобы правильно логировать POST вам нужно будет проделать дополнительную работу.

Может кому это будет полезно:

      HttpClient() {
    install("RequestLogging") {
        responsePipeline.intercept(HttpResponsePipeline.After) {
            val request = context.request
            val response = context.response
            kermit.d(tag = "Network") {
                "${request.method} ${request.url} ${response.status}"
            }
            GlobalScope.launch(Dispatchers.Unconfined) {
                val responseBody =
                    response.content.tryReadText(response.contentType()?.charset() ?: Charsets.UTF_8)
                        ?: "[response body omitted]"
                kermit.d(tag = "Network") {
                    "${request.method} ${request.url} ${response.status}\nBODY START" +
                            "\n$responseBody" +
                            "\nBODY END"
                }
            }
        }
    }
}

Вам также нужно добавить метод из класса Ktor Logger.kt в ваш класс с помощью HttpClient:

      internal suspend inline fun ByteReadChannel.tryReadText(charset: Charset): String? = try {
    readRemaining().readText(charset = charset)
} catch (cause: Throwable) {
    null
}
Другие вопросы по тегам