Skip to content

Instantly share code, notes, and snippets.

@sabirove
Last active March 5, 2022 06:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sabirove/ff9f731c88a5910537f2728fdeacf49a to your computer and use it in GitHub Desktop.
Save sabirove/ff9f731c88a5910537f2728fdeacf49a to your computer and use it in GitHub Desktop.
// dependencies:
// https://mvnrepository.com/artifact/org.slf4j/slf4j-api/1.7.30
/**
* Wrapper over slf4j [Logger] providing Kotlin functional-style API with inline call optimization:
* lambda parameter is inlined by the Kotlin compiler while actual method invocations should
* eventually be removed by the branch predictor whenever corresponding logging level is not
* active so there's no performance hit from inactive logging statements whatsoever.
*
* Note: "Logg" because it's short and avoids all kinds of namespace conflicts.
*/
class Logg(loggerName: String) {
@Suppress("PropertyName")
val _log: Logger = LoggerFactory.getLogger(loggerName)
/** Infers [Logg] name from the [klass]' FQDN */
constructor(klass: KClass<*>) : this(klass.qualifiedName ?: error("can't get FQDN for $klass"))
/**
* Infers [Logg] name implicitly using a normalized class' FQDN obtained from
* the [capture] scope. Use for concise log declarations, e.g. `val log = Logg {}`
*
* Normalization rules:
* - Companion object declaration is "unwrapped" and inferred as a top-level class' FQDN
* - File-level declaration is inferred as a file name without the `Kt` postfix
* - Other declarations (including nested/inner classes) are inferred as a class' FQDN
*
* NB: if class inheritance is used and logger is declared on the base type instance level,
* it's name will be inferred from the base type' FQDN and NOT the actual inheritor.
* Use `val log = Logg(this::class)` instead to yield the inherited class' FQDN name instead.
*/
constructor(capture: () -> Unit) : this(
loggerName = capture.javaClass.name.let {
when {
// companion declaration (might be nested class companion, so normalization is due)
it.contains("\$Companion") ->
it.substringBefore("\$Companion").replace('$', '.')
// file-level declaration
it.contains("Kt$") -> it.substringBefore("Kt$")
// other declaration (might be nested/inner class, so normalization is due)
else -> it.substringBeforeLast('$')
.substringBeforeLast('$')
.replace('$', '.')
}
}
)
inline fun trace(lazyMessage: () -> String) {
if (_log.isTraceEnabled) _log.trace(lazyMessage())
}
inline fun trace(e: Throwable, lazyMessage: () -> String) {
if (_log.isTraceEnabled) _log.trace(lazyMessage(), e)
}
inline fun debug(lazyMessage: () -> String) {
if (_log.isDebugEnabled) _log.debug(lazyMessage())
}
inline fun debug(e: Throwable, lazyMessage: () -> String) {
if (_log.isDebugEnabled) _log.debug(lazyMessage(), e)
}
inline fun info(lazyMessage: () -> String) {
if (_log.isInfoEnabled) _log.info(lazyMessage())
}
inline fun info(e: Throwable, lazyMessage: () -> String) {
if (_log.isInfoEnabled) _log.info(lazyMessage(), e)
}
inline fun warn(lazyMessage: () -> String) {
if (_log.isWarnEnabled) _log.warn(lazyMessage())
}
inline fun warn(e: Throwable, lazyMessage: () -> String) {
if (_log.isWarnEnabled) _log.warn(lazyMessage(), e)
}
inline fun error(lazyMessage: () -> String) {
if (_log.isErrorEnabled) _log.error(lazyMessage())
}
inline fun error(e: Throwable, lazyMessage: () -> String) {
if (_log.isErrorEnabled) _log.error(lazyMessage(), e)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment