Skip to content

Instantly share code, notes, and snippets.

@marcellogalhardo
Last active February 25, 2023 15:59
Show Gist options
  • Save marcellogalhardo/256032be74cbe7ad692648a4e31ca067 to your computer and use it in GitHub Desktop.
Save marcellogalhardo/256032be74cbe7ad692648a4e31ca067 to your computer and use it in GitHub Desktop.
Helper functions to make logging easier.
@file:OptIn(ExperimentalTypeInference::class)
package logcat
import android.util.Log
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.experimental.ExperimentalTypeInference
@JvmName("logcatString")
@OverloadResolutionByLambdaReturnType
public inline fun Any.logcat(
priority: LogPriority,
tag: String?,
message: () -> String,
) {
internalRunIfLoggable(priority, tag) { innerTag -> internalLog(priority, innerTag, message()) }
}
@JvmName("logcatError")
@OverloadResolutionByLambdaReturnType
public inline fun Any.logcat(
priority: LogPriority,
tag: String?,
error: () -> Throwable,
) {
internalRunIfLoggable(priority, tag) { innerTag -> internalLog(priority, innerTag, error()) }
}
@JvmName("logcatResult")
@OverloadResolutionByLambdaReturnType
public inline fun <T> Any.logcat(
priority: LogPriority,
tag: String?,
result: () -> Result<T>,
) {
internalRunIfLoggable(priority, tag) { innerTag ->
internalLog(priority, innerTag, internalLogResult(result()))
}
}
@Suppress("NOTHING_TO_INLINE")
@JvmName("logcatResult")
public inline fun <T> Result<T>.logcat(
priority: LogPriority,
tag: String?,
): Result<T> = also { logcat(priority, tag, result = { this }) }
@JvmName("logcatString")
@PublishedApi
internal fun Any.internalRunIfLoggable(
priority: LogPriority,
tag: String?,
block: (tag: String) -> Unit,
) {
if (LogClient.isLoggable(priority)) block(tag ?: internalOuterClassSimpleName())
}
@PublishedApi
internal fun internalLog(
priority: LogPriority,
tag: String,
message: String,
) {
when (priority) {
LogPriority.VERBOSE -> Log.v(tag, message)
LogPriority.DEBUG -> Log.d(tag, message)
LogPriority.INFO -> Log.i(tag, message)
LogPriority.WARN -> Log.w(tag, message)
LogPriority.ERROR -> Log.e(tag, message)
LogPriority.ASSERT -> Log.wtf(tag, message)
}
}
@PublishedApi
internal fun internalLog(
priority: LogPriority,
tag: String,
error: Throwable,
) {
internalLog(priority, tag, internalLogException(error))
}
@PublishedApi
internal fun Any.internalOuterClassSimpleName(): String {
val javaClass = this::class.java
val fullClassName = javaClass.name
val outerClassName = fullClassName.substringBefore('$')
val simplerOuterClassName = outerClassName.substringAfterLast('.')
return if (simplerOuterClassName.isEmpty()) {
fullClassName
} else {
simplerOuterClassName.removeSuffix("Kt")
}
}
/**
* Utility to turn a [Throwable] into a loggable string.
*
* The implementation is based on Timber.getStackTraceString(). It's different
* from [android.util.Log.getStackTraceString] in the following ways:
* - No silent swallowing of UnknownHostException.
* - The buffer size is 256 bytes instead of the default 16 bytes.
*/
@PublishedApi
internal fun internalLogException(error: Throwable): String {
val stringWriter = StringWriter(256)
val printWriter = PrintWriter(stringWriter, false)
error.printStackTrace(printWriter)
printWriter.flush()
return stringWriter.toString()
}
@PublishedApi
internal fun <T> internalLogResult(result: Result<T>): String {
val classToString = result.toString()
val exception = result.exceptionOrNull()
return if (exception != null) {
val exceptionToString = internalLogException(exception)
"$classToString/n$exceptionToString"
} else {
classToString
}
}
package logcat
public object LogClient {
private var isInitialized: Boolean = false
@PublishedApi
internal var isEnabled: Boolean = false
@PublishedApi
internal var minPriority: LogPriority = LogPriority.DEBUG
public fun initialize(
enabled: Boolean,
minPriority: LogPriority,
) {
require(!isInitialized) { "LogClient.initialize should be called only once." }
isInitialized = true
this.isEnabled = enabled
this.minPriority = minPriority
}
@PublishedApi
internal fun isLoggable(priority: LogPriority): Boolean {
return isEnabled && priority.level >= minPriority.level
}
}
package logcat
public enum class LogPriority(@PublishedApi internal val level: Int) {
VERBOSE(level = 2),
DEBUG(level = 3),
INFO(level = 4),
WARN(level = 5),
ERROR(level = 6),
ASSERT(level = 7);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment