Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a separate android module that calls Android API directly #122

Closed
oshai opened this issue Jul 16, 2020 · 2 comments
Closed

Add a separate android module that calls Android API directly #122

oshai opened this issue Jul 16, 2020 · 2 comments

Comments

@oshai
Copy link
Owner

oshai commented Jul 16, 2020

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
    }
}
@oshai
Copy link
Owner Author

oshai commented Dec 16, 2022

I have a plan to move forward with that, since it's a breaking change it will be in a new major version 4:

  • break out the current jvm module into another directory / artifact.
  • create sub-modules: slf4j and android (optionally we can have a separation also slf4j/slf4j2).

@oshai
Copy link
Owner Author

oshai commented Feb 11, 2023

Version 4.0.0-beta-15 contains native android logging with artifact kotlin-logging-android.

@oshai oshai closed this as completed Feb 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant