ラムダが使えるLoggerを自作してみた

ラムダを使用して、ログレベルを超えない場合は評価しないロガーを作ってみた。

* ログメッセージ自体の生成にCPUを使うようなことはないと思うが、ネックになりうるなら有用かもしれない。
* `inline fun` だからラムダのオーバーヘッドがないはず。
* 正直Timberとかそこら辺のライブラリ使ったほうがいいと思う。

```kotlin:Logger.kt
/**
 * ラムダを使用したロガー
 *
 * 使用例
 * ```
 * class MyApplication : Application() {
 *     override fun onCreate() {
 *         Logger.initialize(if (BuildConfig.DEBUG) Log.VERBOSE else Log.ERROR, AndroidLogFactory())
 *     }
 * }
 * 
 * class MyFragment : Fragment() {
 *     private val logger = Logger("MyFragment")
 *
 *     fun doSomething() {
 *         logger.v { "foo bar baz" }
 *         logger.e({ "error" }, RuntimeException())
 *     }
 * }
 * ```
 */
class Logger(val tag: String) {

    companion object {
        var priority: Int = Log.VERBOSE
            private set
        var logFactory: LogFactory = SystemLogFactory()
            private set

        fun initialize(priority: Int, logFactory: LogFactory) {
            this.priority = priority
            this.logFactory = logFactory
        }
    }

    inline fun log(priority: Int, msgCreator: () -> String, error: Throwable?) {
        if (priority < Companion.priority) return
        factory.log(priority, tag, msgCreator(), error)
    }

    inline fun v(msgCreator: () -> String) = log(android.util.Log.VERBOSE, msgCreator, null)
    inline fun v(msgCreator: () -> String, error: Throwable) = log(android.util.Log.VERBOSE, msgCreator, error)
    inline fun d(msgCreator: () -> String) = log(android.util.Log.DEBUG, msgCreator, null)
    inline fun d(msgCreator: () -> String, error: Throwable) = log(android.util.Log.DEBUG, msgCreator, error)
    inline fun i(msgCreator: () -> String) = log(android.util.Log.INFO, msgCreator, null)
    inline fun i(msgCreator: () -> String, error: Throwable) = log(android.util.Log.INFO, msgCreator, error)
    inline fun w(msgCreator: () -> String) = log(android.util.Log.WARN, msgCreator, null)
    inline fun w(msgCreator: () -> String, error: Throwable) = log(android.util.Log.WARN, msgCreator, error)
    inline fun e(msgCreator: () -> String) = log(android.util.Log.ERROR, msgCreator, null)
    inline fun e(msgCreator: () -> String, error: Throwable) = log(android.util.Log.ERROR, msgCreator, error)
    inline fun wtf(msgCreator: () -> String) = log(android.util.Log.ASSERT, msgCreator, null)
    inline fun wtf(msgCreator: () -> String, error: Throwable) = log(android.util.Log.ASSERT, msgCreator, error)

    interface LogFactory {
        fun log(priority: Int, tag: String, msg: String, error: Throwable?)
    }

    class SystemLogFactory : LogFactory {
        override fun log(priority: Int, tag: String, msg: String, error: Throwable?) {
            System.out.println("${priority.label}/$tag: $msg")
            error?.printStackTrace(System.err)
        }

        private val Int.label: String get() = when (this) {
            android.util.Log.VERBOSE -> "V"
            android.util.Log.DEBUG -> "D"
            android.util.Log.INFO -> "I"
            android.util.Log.WARN -> "W"
            android.util.Log.ERROR -> "E"
            android.util.Log.ASSERT -> "WTF"
            else -> throw IllegalArgumentException("$this is unknown priority.")
        }
    }
}
```

```kotlin:AndroidLogFactory
/**
 * [Log]を使用するログファクトリー
 * テストコードでのクラッシュを避けるために実装
 */
class AndroidLogFactory : Logger.LogFactory {
    override fun log(priority: Int, tag: String, msg: String, error: Throwable?) {
        val errorMessage = error?.let { "\n" + Log.getStackTraceString(it) } ?: ""
        Log.println(priority, tag, msg + errorMessage)
    }
}
```

KDocにあるように、JVMテスト時に `android.util.Log` のメソッドコールによるクラッシュを避けたかったのが主な目的。