I CAN HAZ LOGZ?
logcat { "I CAN HAZ LOGZ?" }
A tiny Kotlin API for cheap logging on top of Android's normal Log
class.
This fantastic logo is brought to you by @rjrjr.
Add the logcat
dependency to your library or app's build.gradle
file:
dependencies {
implementation 'com.squareup.logcat:logcat:0.1'
}
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)
}
}
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
}
}
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:
logcat()
supports string interpolation without any
performance cost when logging is disabled.DebugTree
captures the calling class name as a tag by creating a stacktrace, which can
be expensive. By making logcat()
an extension function of Any
, we can call this::class.java
and get the calling context without creating a stacktrace..
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.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).Throwable.asLog()
.logcat()
is intentionally boring and identical to the Android command line tool. This
makes it easy to remember and developers know exactly what this does, i.e. log to the local device.
One could setup a custom logger that send logs remotely in release builds, however we do not
recommend doing so: in our experience, remote logs should be distinct in code from local logs and
clearly identified as such, because the volume and performance impact should be very distinct.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.