实现效果
- 响应体若为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.
}
}
}
注意:本文归作者所有,未经作者允许,不得转载