Okhttp3.0ログブロッキングHttpLoggingInterceptorの大ファイルアップロード時OutOfMemoryおよび修復
9286 ワード
プロジェクトにはokhttp+logging-interceptorを使用してローカルに保存されているlogファイルがアップロードされています.
コードクリップ:
そして最初は1つのlogファイルしかなかったため、日がたつにつれてログファイルが大きく、80 M+の(これは複数のファイルに最適化されています)があり、
soが最初にアップロードした時HttpLoggingInterceptorの中で間違って報告して、ロゴを調べるのはOutOfMemoryで、
つまり次のようなコードフラグメントが間違っていて、HttpLoggingInterceptorで80 M+の内容を直接読み込みます.
ここでは判断条件が付けられておらず、サイズ制限もなく、表示に問題があります.
そこで考え方はログ印刷ブロックを書き直し、主にこの場所を読み取り、印刷することです.
後で公式issueを調べてみると、以前にもこの問題に遭遇した人がいて、彼らの案を参考にして、2つのグローバル変数requestBodyLogMaxとresponseBodyLogMaxを追加して、最大印刷のサイズを設定して、一部の内容だけを切り取って印刷することを超えました.
臨界値の設定
コードクリップ:
var logLevel = HttpLoggingInterceptor.Level.BODY // BODY
val builder = OkHttpClient.Builder()
//...
.addInterceptor(
HttpLoggingInterceptor().setLevel(logLevel)
そして最初は1つのlogファイルしかなかったため、日がたつにつれてログファイルが大きく、80 M+の(これは複数のファイルに最適化されています)があり、
soが最初にアップロードした時HttpLoggingInterceptorの中で間違って報告して、ロゴを調べるのはOutOfMemoryで、
つまり次のようなコードフラグメントが間違っていて、HttpLoggingInterceptorで80 M+の内容を直接読み込みます.
@Override
public Response intercept(Chain chain) throws IOException {
//...
if (isPlaintext(buffer)) {
logger.log(buffer.readString(charset)); // , ,
logger.log("--> END " + request.method()
+ " (" + requestBody.contentLength() + "-byte body)");
} else {
logger.log("--> END " + request.method() + " (binary "
+ requestBody.contentLength() + "-byte body omitted)");
}
}
ここでは判断条件が付けられておらず、サイズ制限もなく、表示に問題があります.
そこで考え方はログ印刷ブロックを書き直し、主にこの場所を読み取り、印刷することです.
後で公式issueを調べてみると、以前にもこの問題に遭遇した人がいて、彼らの案を参考にして、2つのグローバル変数requestBodyLogMaxとresponseBodyLogMaxを追加して、最大印刷のサイズを設定して、一部の内容だけを切り取って印刷することを超えました.
臨界値の設定
val builder = OkHttpClient.Builder()
builder
.addInterceptor(
CommonHttpLoggingInterceptor().setLevel(logLevel).setRequestBodyLogMax(2000).setResponseBodyLogMax(
2000
)
)
CommonHttpLoggingInterceptor.java :
class CommonHttpLoggingInterceptor(val logger: Logger = Logger.DEFAULT) : Interceptor {
@Volatile
private var level = Level.NONE
@Volatile
private var requestBodyLogMax = LOG_LIMITATION_NONE
@Volatile
private var responseBodyLogMax = LOG_LIMITATION_NONE
enum class Level {
/** No logs. */
NONE,
/**
* Logs request and response lines.
*
*
* Example:
* `--> POST /greeting http/1.1 (3-byte body)
*
* *
*/
BASIC,
/**
* Logs request and response lines and their respective headers.
*
*
* Example:
* `--> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
* --> END POST
*
* *
*/
HEADERS,
/**
* Logs request and response lines and their respective headers and bodies (if present).
*
*
* Example:
* `--> POST /greeting http/1.1
* Host: example.com
* Content-Type: plain/text
* Content-Length: 3
*
* Hi?
* --> END POST
*
* *
*/
BODY,
/**
* Logs request and response lines.
*
*
* Example:
* `--> POST /greeting http/1.1 (3-byte body)
* result...(part)
* *
*/
SIMPLE
}
interface Logger {
fun log(message: String)
companion object {
/** A [Logger] defaults output appropriate for the current platform. */
val DEFAULT: Logger = object : Logger {
override fun log(message: String) {
Platform.get().log(INFO, message, null)
}
}
}
}
/** Change the level at which this interceptor logs. */
fun setLevel(level: Level?): CommonHttpLoggingInterceptor {
if (level == null) throw NullPointerException("level == null. Use Level.NONE instead.")
this.level = level
return this
}
fun getLevel(): Level {
return level
}
/** Change the limitation of request body logs size. */
fun setRequestBodyLogMax(requestBodyLogMax: Long): CommonHttpLoggingInterceptor {
if (requestBodyLogMax < 0) {
this.requestBodyLogMax = LOG_LIMITATION_NONE
} else {
this.requestBodyLogMax = requestBodyLogMax
}
return this
}
/** Change the limitation of response body logs size. */
fun setResponseBodyLogMax(responseBodyLogMax: Long): CommonHttpLoggingInterceptor {
if (responseBodyLogMax < 0) {
this.responseBodyLogMax = LOG_LIMITATION_NONE
} else {
this.responseBodyLogMax = responseBodyLogMax
}
return this
}
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
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 logPartBody = level == Level.SIMPLE
val requestBody = request.body()
val hasRequestBody = requestBody != null
val connection = chain.connection()
var requestStartMessage = ("--> "
+ request.method()
+ ' '.toString() + request.url()
+ (connection?.protocol()?:""))
if (!logHeaders && hasRequestBody) {
requestStartMessage += " (${requestBody!!.contentLength()}-byte body)"
}
logger.log(requestStartMessage)
if (logHeaders) {
if (requestBody != null) {
// Request body headers are only present when installed as a network interceptor. Force
// them to be included (when available) so there values are known.
if (requestBody.contentType() != null) {
logger.log("Content-Type: ${requestBody.contentType()}")
}
if (requestBody.contentLength() != -1L) {
logger.log("Content-Length: ${requestBody.contentLength()}")
}
}
val headers = request.headers()
var i = 0
val count = headers.size()
while (i < count) {
val name = headers.name(i)
// Skip headers from the request body as they are explicitly logged above.
if (!"Content-Type".equals(name, ignoreCase = true) && !"Content-Length".equals(
name,
ignoreCase = true
)
) {
logger.log(name + ": " + headers.value(i))
}
i++
}
}
if (!(logBody || logPartBody) || !hasRequestBody) {
logger.log("--> END " + request.method())
} else if (bodyHasUnknownEncoding(request.headers())) {
logger.log("--> END " + request.method() + " (encoded body omitted)")
} else {
val buffer = Buffer()
requestBody!!.writeTo(buffer)
var charset: Charset? = UTF8
val contentType = requestBody.contentType()
if (contentType != null) {
charset = contentType.charset(UTF8)
}
logger.log("")
if (isPlaintext(buffer)) {
if (buffer.size() <= requestBodyLogMax || requestBodyLogMax < 0) {
var reqBodyContent = buffer.readString(charset!!)
if (logPartBody && reqBodyContent.length > 200) {
reqBodyContent = reqBodyContent.shrink(200, '.')
}
logger.log(reqBodyContent)
} else {
logger.log(
"Too large to output logs. "
+ "Current limitation is $requestBodyLogMax"
)
}
logger.log("--> END ${request.method()} (${requestBody.contentLength()}-byte body)")
} else {
logger.log(
("--> END ${request.method()} (binary ${requestBody.contentLength()}-byte body omitted)")
)
}
}
val startNs = System.nanoTime()
val response: Response
try {
response = chain.proceed(request)
} catch (e: Exception) {
logger.log(" 200) {
resBodyContent = resBodyContent.shrink(200, '.')
}
logger.log(resBodyContent)
} else {
logger.log(
("Too large to output logs. "
+ "Current limitation is $responseBodyLogMax")
)
}
}
if (gzippedLength != null) {
logger.log(
("