Retrofit 自定义Logger

iwdael 13天前 ⋅ 15 阅读

实现效果

  • 响应体若为json,则自动格式化
  • 自定义输入方式
class LoggerInterceptor : Interceptor {

    private val gson = GsonBuilder().setPrettyPrinting().create()
    private val jsonParser = JsonParser()

    @set:JvmName("level")
    @Volatile
    var level = Level.BODY

    enum class Level {
        NONE,
        BASIC,
        HEADERS,
        BODY
    }

    @Throws(IOException::class)
    override fun intercept(chain: Interceptor.Chain): Response {
        val logBuilder = StringBuilder()
        val level = this.level

        val request = chain.request()
        if (level == Level.NONE) {
            return chain.proceed(request)
        }

        val logBody = level == Level.BODY
        val logHeaders = logBody || level == Level.HEADERS

        val requestBody = request.body

        val connection = chain.connection()
        var requestStartMessage =
            ("--> ${request.method} ${request.url}${if (connection != null) " " + connection.protocol() else ""}")
        if (!logHeaders && requestBody != null) {
            requestStartMessage += " (${requestBody.contentLength()}-byte body)"
        }
        logBuilder.appendLn(requestStartMessage)

        if (logHeaders) {
            val headers = request.headers

            if (requestBody != null) {
                // Request body headers are only present when installed as a network interceptor. When not
                // already present, force them to be included (if available) so their values are known.
                requestBody.contentType()?.let {
                    if (headers["Content-Type"] == null) {
                        logBuilder.appendLn("Content-Type: $it")
                    }
                }
                if (requestBody.contentLength() != -1L) {
                    if (headers["Content-Length"] == null) {
                        logBuilder.appendLn("Content-Length: ${requestBody.contentLength()}")
                    }
                }
            }

            for (i in 0 until headers.size) {
                logHeader(logBuilder, headers, i)
            }

            if (!logBody || requestBody == null) {
                logBuilder.append("--> END ${request.method}")
            } else if (bodyHasUnknownEncoding(request.headers)) {
                logBuilder.append("--> END ${request.method} (encoded body omitted)")
            } else if (requestBody.isDuplex()) {
                logBuilder.append("--> END ${request.method} (duplex request body omitted)")
            } else if (requestBody.isOneShot()) {
                logBuilder.append("--> END ${request.method} (one-shot body omitted)")
            } else {
                val buffer = Buffer()
                if (requestBody !is MultipartBody) {
                    requestBody.writeTo(buffer)
                } else {
                    requestBody.writeForLog(buffer)
                }

                val contentType = requestBody.contentType()
                val charset: Charset = contentType?.charset(UTF_8) ?: UTF_8

                logBuilder.appendLn("")
                if (buffer.isProbablyUtf8()) {
                    val jsonMaybe = buffer.readString(charset)
                    try {
                        if (BuildConfig.DEBUG)
                            logBuilder.appendLn(gson.toJson(jsonParser.parse(jsonMaybe)))
                        else
                            logBuilder.appendLn(jsonMaybe)
                    } catch (e: Exception) {
                        logBuilder.appendLn(jsonMaybe)
                    }
                    logBuilder.append("--> END ${request.method} (${requestBody.contentLength()}-byte body)")
                } else {
                    logBuilder.append("--> END ${request.method} (binary ${requestBody.contentLength()}-byte body omitted)")
                }
            }
        }
        Logger.t("HttpClient").i(logBuilder.toString())
        logBuilder.clear()
        val startNs = System.nanoTime()
        val response: Response
        try {
            response = chain.proceed(request)
        } catch (e: Exception) {
            logBuilder.appendLn("<-- HTTP FAILED: $e")
            throw e
        }

        val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)

        val responseBody = response.body!!
        val contentLength = responseBody.contentLength()
        val bodySize = if (contentLength != -1L) "$contentLength-byte" else "unknown-length"
        logBuilder.appendLn(
            "<-- ${response.code}${if (response.message.isEmpty()) "" else ' ' + response.message} ${response.request.url} (${tookMs}ms${if (!logHeaders) ", $bodySize body" else ""})"
        )

        if (logHeaders) {
            val headers = response.headers
            for (i in 0 until headers.size) {
                logHeader(logBuilder, headers, i)
            }

            if (!logBody || !response.promisesBody()) {
                logBuilder.append("<-- END HTTP")
            } else if (bodyHasUnknownEncoding(response.headers)) {
                logBuilder.append("<-- END HTTP (encoded body omitted)")
            } else {
                val source = responseBody.source()
                source.request(Long.MAX_VALUE) // Buffer the entire body.
                var buffer = source.buffer

                var gzippedLength: Long? = null
                if ("gzip".equals(headers["Content-Encoding"], ignoreCase = true)) {
                    gzippedLength = buffer.size
                    GzipSource(buffer.clone()).use { gzippedResponseBody ->
                        buffer = Buffer()
                        buffer.writeAll(gzippedResponseBody)
                    }
                }

                val contentType = responseBody.contentType()
                val charset: Charset = contentType?.charset(UTF_8) ?: UTF_8

                if (!buffer.isProbablyUtf8()) {
                    logBuilder.appendLn("")
                    logBuilder.append("<-- END HTTP (binary ${buffer.size}-byte body omitted)")
                    return response
                }

                if (contentLength != 0L) {
                    logBuilder.appendLn("")
                    val jsonMaybe = buffer.clone().readString(charset)
                    try {
                        if (BuildConfig.DEBUG)
                            logBuilder.appendLn(gson.toJson(jsonParser.parse(jsonMaybe)))
                        else
                            logBuilder.appendLn(jsonMaybe)
                    } catch (e: Exception) {
                        logBuilder.appendLn(jsonMaybe)
                    }
                }

                if (gzippedLength != null) {
                    logBuilder.append("<-- END HTTP (${buffer.size}-byte, $gzippedLength-gzipped-byte body)")
                } else {
                    logBuilder.append("<-- END HTTP (${buffer.size}-byte body)")
                }
            }
        }
        Logger.t("HttpClient").i(logBuilder.toString())
        logBuilder.clear()
        return response
    }

    private fun logHeader(logBuilder: StringBuilder, headers: Headers, i: Int) {
        val value = headers.value(i)
        logBuilder.appendLn(headers.name(i) + ": " + value)
    }

    private fun bodyHasUnknownEncoding(headers: Headers): Boolean {
        val contentEncoding = headers["Content-Encoding"] ?: return false
        return !contentEncoding.equals("identity", ignoreCase = true) &&
                !contentEncoding.equals("gzip", ignoreCase = true)
    }

    private fun StringBuilder.appendLn(content: String) {
        append(content).append("\n")
    }

    private fun Buffer.isProbablyUtf8(): Boolean {
        try {
            val prefix = Buffer()
            val byteCount = size.coerceAtMost(64)
            copyTo(prefix, 0, byteCount)
            for (i in 0 until 16) {
                if (prefix.exhausted()) {
                    break
                }
                val codePoint = prefix.readUtf8CodePoint()
                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
                    return false
                }
            }
            return true
        } catch (_: EOFException) {
            return false // Truncated UTF-8 sequence.
        }
    }

}

全部评论: 0

    我有话说: