I CAN HAZ LOGZ?
Square Logcat
logcat { "I CAN HAZ LOGZ?" }
A tiny Kotlin API for cheap logging on top of Android's normal Log class.
Table of contents
This fantastic logo is brought to you by @rjrjr._
Setup
Add the logcat dependency to your library or app's build.gradle file:
dependencies {
implementation 'com.squareup.logcat:logcat:0.4'
}
Install AndroidLogcatLogger in Application.onCreate():
import android.app.Application
import logcat.AndroidLogcatLogger
import logcat.LogPriority.VERBOSE
class ExampleApplication : Application() { override fun onCreate() { super.onCreate() // Log all priorities in debug builds, no-op in release builds. AndroidLogcatLogger.installOnDebuggableApp(this, minPriority = VERBOSE) } }
Usage
The logcat() function has 3 parameters: an optional priority, an optional tag, and a required string producing lambda. The lambda is only evaluated if a logger is installed and the logger deems the priority loggable.
The priority defaults to LogPriority.DEBUG.
The tag defaults to the class name of the log call site, without any extra runtime cost. This works because logcat() is an inlined extension function of Any and has access to this from which it can extract the class name. If logging from a standalone function which has no this, use the logcat overload which requires a tag parameter.
The logcat() function does not take a Throwable parameter. Instead, the library provides a Throwable extension function: Throwable.asLog() which returns a loggable string.
import logcat.LogPriority.INFO
import logcat.asLog
import logcat.logcat
class MouseController {
fun play() { val state = "CHEEZBURGER" logcat { "I CAN HAZ $state?" } // logcat output: D/MouseController: I CAN HAZ CHEEZBURGER?
logcat(INFO) { "DID U ASK 4 MOAR INFO?" } // logcat output: I/MouseController: DID U ASK 4 MOAR INFO?
logcat { exception.asLog() } // logcat output: D/MouseController: java.lang.RuntimeException: FYLEZ KERUPTED // at sample.MouseController.play(MouseController.kt:22) // ...
logcat("Lolcat") { "OH HI" } // logcat output: D/Lolcat: OH HI } }
Motivations
We built this small library to fit the specific needs of the Square Point of Sale application. We used Timber heavily before that, and love the simplicity of its API and the ability of its DebugTree to automatically figure out from which class it's being called from and use that class name as its tag. Here are our motivations for replacing it with logcat() in the Square Point of Sale:
- Kotlin support for string interpolation is really nice. We love to use that for logs!
logcat() supports string interpolation without any
performance cost when logging is disabled.
- Timber's
DebugTreecaptures the calling class name as a tag by creating a stacktrace, which can
logcat() an extension function of Any, we can call this::class.java
and get the calling context without creating a stacktrace.
- The current implementation uses the outer most simple class name as the tag, i.e. the string
. and the first $. That might not always be what you want. Also, when
logging from an abstract class the tag will be the name of the subclass at runtime. We've found
these limitations to be totally fine with us so far.
- Most of the time, our developers just want to "send something to logcat" without even thinking
logcat() picks "debug" as the right default to provide more consistency across
a large codebase. Making the priority a parameter also means only one method to learn, and you
don't have to learn / think about priorities prior to writing a log. This becomes especially
important when there are several parameters requiring overloads (e.g. in Timber (6 priorities + 1
generic log method) * 3 overloads = 21 methods to choose from).
- The lack of throwable parameter is also intentional. It just creates more overloads and confusion
Throwable.asLog().
- The name
logcat()is intentionally boring and identical to the Android command line tool. This
- The API for installing a logger is separated out from the API to log, as these operations occur
License
Copyright 2021 Square Inc.Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.