kotlin-logging
kotlin-logging copied to clipboard
Add a separate android module that calls Android API directly
Currently Android is supported via slf4j extension which have issues and seems deprecated for that use. In #121 it was suggested to use Kotlin Multi Platform and have a module for android separated from the regular jvm module.
Below is a code sample that can assist.
Basically such module should be similar to the js module without slf4j dependency.
Here are the docs for android multiplatform support.
import android.util.Log
import mu.KLoggable
import mu.KLogger
import mu.Marker
import mu.KLoggerFactory
import java.lang.reflect.Modifier
/**
* Android Logging implementation of [KLogger]
*/
class AndroidLogger(override val name: String) : KLogger {
// No implementation, "catching" support not applied to android logger, unlikely to be used at this point
override fun <T : Throwable> catching(throwable: T) = Unit
override fun debug(msg: () -> Any?) {
if (Log.isLoggable(name, Log.DEBUG)) {
Log.d(name, msg.toStringSafely())
}
}
override fun debug(t: Throwable?, msg: () -> Any?) {
if (Log.isLoggable(name, Log.DEBUG)) {
Log.d(name, msg.toStringSafely(), t)
}
}
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun debug(marker: Marker?, msg: () -> Any?) = Unit
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun debug(marker: Marker?, t: Throwable?, msg: () -> Any?) = Unit
// No implementation, "entry" support not applied to android logger, unlikely to be used at this point
override fun entry(vararg argArray: Any?) = Unit
override fun error(msg: () -> Any?) {
if (Log.isLoggable(name, Log.ERROR)) {
Log.e(name, msg.toStringSafely())
}
}
override fun error(t: Throwable?, msg: () -> Any?) {
if (Log.isLoggable(name, Log.ERROR)) {
Log.e(name, msg.toStringSafely(), t)
}
}
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun error(marker: Marker?, msg: () -> Any?) = Unit
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun error(marker: Marker?, t: Throwable?, msg: () -> Any?) = Unit
// No implementation, "exit" support not applied to android logger, unlikely to be used at this point
override fun exit() = Unit
// No implementation, "exit" support not applied to android logger, unlikely to be used at this point
override fun <T> exit(result: T): T = throw NotImplementedError("Exit not supported by android logger")
override fun info(msg: () -> Any?) {
if (Log.isLoggable(name, Log.INFO)) {
Log.i(name, msg.toStringSafely())
}
}
override fun info(t: Throwable?, msg: () -> Any?) {
if (Log.isLoggable(name, Log.INFO)) {
Log.i(name, msg.toStringSafely(), t)
}
}
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun info(marker: Marker?, msg: () -> Any?) = Unit
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun info(marker: Marker?, t: Throwable?, msg: () -> Any?) = Unit
// No implementation, "throwing" support not applied to android logger, unlikely to be used at this point
override fun <T : Throwable> throwing(throwable: T): T =
throw NotImplementedError("throwing not supported by android logger")
override fun trace(msg: () -> Any?) {
if (Log.isLoggable(name, Log.VERBOSE)) {
Log.v(name, msg.toStringSafely())
}
}
override fun trace(t: Throwable?, msg: () -> Any?) {
if (Log.isLoggable(name, Log.VERBOSE)) {
Log.v(name, msg.toStringSafely(), t)
}
}
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun trace(marker: Marker?, msg: () -> Any?) = Unit
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun trace(marker: Marker?, t: Throwable?, msg: () -> Any?) = Unit
override fun warn(msg: () -> Any?) {
if (Log.isLoggable(name, Log.WARN)) {
Log.w(name, msg.toStringSafely())
}
}
override fun warn(t: Throwable?, msg: () -> Any?) {
if (Log.isLoggable(name, Log.WARN)) {
Log.w(name, msg.toStringSafely(), t)
}
}
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun warn(marker: Marker?, msg: () -> Any?) = Unit
// No implementation, "marker" support not applied to android logger, unlikely to be used at this point
override fun warn(marker: Marker?, t: Throwable?, msg: () -> Any?) = Unit
}
private fun (() -> Any?)?.toStringSafely() = this?.invoke()?.toString().orEmpty()
/**
* [KLoggerFactory] that emits [android.util.Log] backed log entries
*/
class AndroidLoggerFactory : KLoggerFactory {
override fun logger(func: () -> Unit): KLogger = logger(name(func))
override fun logger(name: String): KLogger = AndroidLogger(name)
override fun logger(loggable: KLoggable): KLogger = logger(unwrapCompanionClass(loggable.javaClass).name)
}
/**
* unwrap companion class to enclosing class given a Java Class
*/
private fun <T : Any> unwrapCompanionClass(clazz: Class<T>): Class<*> {
clazz.enclosingClass?.also { enclosingClass ->
try {
val field = enclosingClass.getField(clazz.simpleName)
if (Modifier.isStatic(field.modifiers) && field.type == clazz) {
// && field.get(null) === obj
// the above might be safer but problematic with initialization order
return enclosingClass
}
} catch (e: Exception) {
// ok, it is not a companion object
}
}
return clazz
}
/**
* get class name for function by the package of the function
*/
private fun name(func: () -> Unit): String {
val name = func.javaClass.name
return when {
name.contains("Kt$") -> name.substringBefore("Kt$")
name.contains("$") -> name.substringBefore("$")
else -> name
}
}