HttpLoggingInterceptorのJsonログを整形して表示する

ふと思いついて調べてみたらさくっとできて便利だと思ったので📝です。

HttpLoggingInterceptor

OkHttpには、HttpLoggingInterceptorという便利なInterceptorが用意されています。 このクラスを利用することで、API通信時のヘッダー、レスポンスやリクエストボディーなどを簡単にLogcat上で確認できます。

基本的にな使い方や、どのような設定ができるか等は、以下のREADMEを読むのが良いと思います。

github.com

このクラスを利用することで、レスポンスやリクエストボディーなどの値を確認できますが、デフォルトの設定ではLogcatに表示されるJsonや配列の値は整形されません。 ここでは、表示する内容を事前に整形してログに表示するようにする方法をメモ程度に紹介です。

ログデータを整形する

HttpLoggingInterceptorには、コンストラクタ経由でLoggerを渡すことができます。 デフォルトでは、単純にlogを表示するだけの処理を行う、事前にライブラリ側が用意するLoggerがセットされています。

// HttpLoggingInterceptor.kt
class HttpLoggingInterceptor @JvmOverloads constructor(
  private val logger: Logger = Logger.DEFAULT
) : Interceptor {
...

interface Logger {
    fun log(message: String)

    companion object {
      /** A [Logger] defaults output appropriate for the current platform. */
      @JvmField
      val DEFAULT: Logger = object : Logger {
        override fun log(message: String) {
          Platform.get().log(message) // 単純にlogを表示するだけの処理を行っています
        }
      }
    }
  }
...

整形するためには

カスタムなHttpLoggingInterceptor.Loggerを定義し、HttpLoggingInterceptorのコンストラクタに渡すことで、どのような値をログに表示するかを制御できます。

以下、GsonとMoshiを用いた実装例です。

Gson

internal object InterceptorLogger : HttpLoggingInterceptor.Logger {
  private val gson = GsonBuilder().setPrettyPrinting().create()

  override fun log(message: String) {
    if (message.isJsonOrArray()) {
      Platform.get().log(gson.toJson(JsonParser.parseString(message)))
    } else {
      Platform.get().log(message)
    }
  }
}

private fun String.isJsonOrArray() = startsWith('{') || startsWith('[')

Gsonの場合は、setPrettyPrinting()という設定をして初期化することで、整形したJsonの値を返してくれます。

Moshi

// Moshi
internal class InterceptorLogger(moshi: Moshi) : HttpLoggingInterceptor.Logger {
  private val adapter = moshi.adapter(Any::class.java).indent("   ")

  override fun log(message: String) {
    if (message.isJsonOrArray()) {
      val jsonValue = JsonReader.of(Buffer().writeUtf8(message)).readJsonValue()
      Platform.get().log(adapter.toJson(jsonValue))
    } else {
      Platform.get().log(message)
    }
  }
}

private fun String.isJsonOrArray() = startsWith('{') || startsWith('[')

Moshiの場合は、adapterに対してindentというのを設定できます。各階層ごとにindentをうまく挿入して整形してくれます。

参考リンク等