Repository: ktorio/ktor
File: ktor-client/ktor-client-plugins/ktor-client-logging/common/src/io/ktor/client/plugins/logging/Logging.kt (Kotlin)

Code before:
            ""
        }

        if (!isBinary) {
            var lastCharIndex = -1
            for (ch in firstChunkText) {
                lastCharIndex += 1
            }

            for ((i, ch) in firstChunkText.withIndex()) {
                if (ch == '\ufffd' && i != lastCharIndex) {
                    isBinary = true
                    break
                }
            }
        }

        if (!isBinary) {
            val channel = ByteChannel()

            client.launch {
                try {
                    channel.writeFully(firstChunk, 0, firstReadSize)
                    body.copyTo(channel)
                } catch (cause: Throwable) {
                    channel.close(cause)
                } finally {
                    channel.flushAndClose()
                }
            }

            return Triple(isBinary, null, channel)
        }

        return Triple(isBinary, contentLength, body)
    }

    suspend fun logRequestBody(
        content: OutgoingContent,
        contentLength: Long?,
        headers: Headers,
        method: HttpMethod,
        logLines: MutableList<String>,
        body: ByteReadChannel
    ) {
        val (isBinary, size, newBody) = detectIfBinary(body, contentLength, content.contentType, headers)

        if (!isBinary) {
            val contentType = content.contentType
            val charset = if (contentType != null) {
                contentType.charset() ?: Charsets.UTF_8

Review comment (suggestion):
I think for errors in the logging it would be better to just format the cause and write it to the channel so that we don't get the logger throwing exceptions.

Code after:
            ""
        }

        if (!isBinary) {
            var lastCharIndex = -1
            for (ch in firstChunkText) {
                lastCharIndex += 1
            }

            for ((i, ch) in firstChunkText.withIndex()) {
                if (ch == '\ufffd' && i != lastCharIndex) {
                    isBinary = true
                    break
                }
            }
        }

        if (!isBinary) {
            val channel = client.writer {
                channel.writeFully(firstChunk, 0, firstReadSize)
                body.copyTo(channel)
            }.channel

            return Triple(isBinary, null, channel)
        }

        return Triple(isBinary, contentLength, body)
    }

    suspend fun logRequestBody(
        content: OutgoingContent,
        contentLength: Long?,
        headers: Headers,
        method: HttpMethod,
        logLines: MutableList<String>,
        body: ByteReadChannel
    ) {
        val (isBinary, size, newBody) = detectIfBinary(body, contentLength, content.contentType, headers)

        if (!isBinary) {
            val contentType = content.contentType
            val charset = if (contentType != null) {
                contentType.charset() ?: Charsets.UTF_8
            } else {
                Charsets.UTF_8
            }

            val buffer = newBody.readBuffer()
            var byteSize = buffer.size
            logLines.add(buffer.readText(charset = charset))
            logLines.add("--> END ${method.value} (${size ?: byteSize}-byte body)")