Kotlin の高階関数で関数の入退場をロギングする
Kotlin で Androidアプリを開発しています。Kotlin の高階関数が楽しかったので関数の入退場をログ出力するロガーを作成しました。
下記のような出力をするやつです。
1 2 3 4 5 6 7 8 9 |
D/QRefine: |DBG|MainActivity|onCreate start <- onCreate開始 V/Monotype: SetAppTypeFace- try to flip, app = com.ryuta46.qrefine V/Monotype: Typeface getFontPathFlipFont - systemFont = default#default V/BoostFramework: mAcquireFunc method = public int com.qualcomm.qti.Performance.perfLockAcquire(int,int[]) V/BoostFramework: mReleaseFunc method = public int com.qualcomm.qti.Performance.perfLockRelease() V/BoostFramework: mIOPStart method = public int com.qualcomm.qti.Performance.perfIOPrefetchStart(int,java.lang.String) V/BoostFramework: mIOPStop method = public int com.qualcomm.qti.Performance.perfIOPrefetchStop() D/QRefine: |DBG|MainActivity|onCreate end <- onCreate 終了 |
コード
まずは Logger の方のコードから。 trace 関数以外は Android の Log をラップするだけのクラスです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
class Logger(tag: String) { companion object { val LOG_LEVEL_NONE = 0 val LOG_LEVEL_ERROR = 1 val LOG_LEVEL_WARN = 2 val LOG_LEVEL_INFO = 3 val LOG_LEVEL_DEBUG = 4 val LOG_LEVEL_VERBOSE = 5 private val TAG = "QRefine" var level = LOG_LEVEL_NONE } val tag = tag fun e(message: String) { if (level >= LOG_LEVEL_ERROR) Log.e(TAG, "|ERR|$tag|$message") } fun w(message: String) { if (level >= LOG_LEVEL_WARN) Log.w(TAG, "|WRN|$tag|$message") } fun i(message: String) { if (level >= LOG_LEVEL_INFO) Log.i(TAG, "|INF|$tag|$message") } fun d(message: String) { if (level >= LOG_LEVEL_DEBUG) Log.d(TAG, "|DBG|$tag|$message") } fun v(message: String) { if (level >= LOG_LEVEL_VERBOSE) Log.v(TAG, "|VRB|$tag|$message") } inline fun <T> trace(body: () -> T): T { val callerName = if (level >= LOG_LEVEL_DEBUG) { Throwable().stackTrace[0].methodName } else { null } try { callerName?.let { d("$callerName start") } return body() } finally { callerName?.let { d("$callerName end") } } } } |
呼び出し側の方では以下のようにして使います。
1 2 3 4 5 6 7 8 9 10 11 |
class MainActivity : AppCompatActivity() { private val logger = Logger(this.javaClass.simpleName) override fun onCreate(savedInstanceState: Bundle?) { logger.trace { setContentView(R.layout.activity_main) ... } } ... |
Logger#trace
でラムダ式で関数本体の処理を渡します。関数本体実行前に start、関数本体実行後に end のログが出るようになります。
ポイントとしては trace 関数を inline で宣言していることです。これにより、下記のようにラムダ式内で return を書くことができ、この場合は Logger#trace
の呼び出し元からの return を意味することになります(大域脱出)。このように return した場合や例外が発生した場合でも finally ブロックにより関数の end のログがでます。
1 2 3 4 5 6 |
logger.trace { // Inflate the layout for this fragment val view = inflater!!.inflate(R.layout.fragment, container, false) return view } |
良い感じに使えそうですね。欠点は、ラムダ式で囲むので一段右寄りになるところでしょうか。全ての関数で使う必要はないですが、気になる主要な関数には入れておくと、何か起きた時のデバッグに役立ちそうです。
さいごに
Kotlin 楽しいです。iOSで Objective-C から Swift に移行したときも気持ちよくコードが書けるようになったのを覚えていますが、Android も Java ではなく Kotlin の方が書きやすいですね。
これからも Kotlin でまた発見があれば書いていきます。
本記事のコード、ちょっと整理してライブラリ化しました。
最後まで読んでいただきありがとうございます。 このブログを「いいな」と感じていただけましたら、Twiter にてフォローいただけるとうれしいです。ブログ更新情報などもお届けします。
Follow @ryuta461
この記事をシェアする