Skip to content

Commit

Permalink
feat: add logger with userId and clientId (#2139)
Browse files Browse the repository at this point in the history
* feat: add logger with userId and clientId (#2100)

* feat: add logger with userid and clientid

* update lib version

* added some documentation to the KaliumLogger tags

* moved creating string tag to Tag sealed class

* fix detekt

* fix tests

* trigger build

* fixes after cherry pick

* fix detekt

* revert wrong change after automatic cherry-pick

---------

Co-authored-by: Michał Saleniuk <[email protected]>
Co-authored-by: Michał Saleniuk <[email protected]>
  • Loading branch information
3 people authored Oct 17, 2023
1 parent a71fb6d commit d706d77
Show file tree
Hide file tree
Showing 22 changed files with 292 additions and 187 deletions.
12 changes: 7 additions & 5 deletions cli/src/commonMain/kotlin/com/wire/kalium/cli/CLIApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.enum
import com.wire.kalium.logger.KaliumLogLevel
import com.wire.kalium.logger.KaliumLogger
import com.wire.kalium.logic.CoreLogger
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.featureFlags.KaliumConfigs
Expand Down Expand Up @@ -63,11 +64,12 @@ class CLIApplication : CliktCommand(allowMultipleSubcommands = true) {
)
}

if (logOutputFile != null) {
CoreLogger.setLoggingLevel(logLevel, fileLogger)
} else {
CoreLogger.setLoggingLevel(logLevel)
}
CoreLogger.init(
KaliumLogger.Config(
logLevel,
if (logOutputFile != null) listOf(fileLogger) else emptyList(),
)
)

currentContext.findObject<CoreLogic>()?.updateApiVersionsScheduler?.scheduleImmediateApiVersionUpdate()
Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,12 @@

package com.wire.kalium.cryptography

import co.touchlab.kermit.LogWriter
import com.wire.kalium.logger.KaliumLogLevel
import com.wire.kalium.logger.KaliumLogger

internal var kaliumLogger = KaliumLogger.disabled()

object CryptographyLogger {
fun setLoggingLevel(level: KaliumLogLevel, vararg logWriters: LogWriter = arrayOf()) {
kaliumLogger = KaliumLogger(
config = KaliumLogger.Config(
severity = level,
tag = "Crypto"
),
logWriters = logWriters
)
fun init(config: KaliumLogger.Config) {
kaliumLogger = KaliumLogger(config = config, tag = "Crypto")
}
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ core-crypto = "1.0.0-rc.12"
core-crypto-multiplatform = "0.6.0-rc.3-multiplatform-pre1"
completeKotlin = "1.1.0"
desugar-jdk = "2.0.3"
kermit = "1.2.2"
kermit = "2.0.1"
detekt = "1.19.0"
agp = "8.1.1"
dokka = "1.8.20"
Expand Down
208 changes: 146 additions & 62 deletions logger/src/commonMain/kotlin/com/wire/kalium/logger/KaliumLogger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
package com.wire.kalium.logger

import co.touchlab.kermit.LogWriter
import co.touchlab.kermit.LoggerConfig
import co.touchlab.kermit.MutableLoggerConfig
import co.touchlab.kermit.Severity
import co.touchlab.kermit.StaticConfig
import co.touchlab.kermit.platformLogWriter
import co.touchlab.kermit.Logger as KermitLogger

Expand Down Expand Up @@ -61,96 +62,179 @@ enum class KaliumLogLevel {
DISABLED
}

fun KaliumLogLevel.toMinSeverity(): Severity = when (this) {
KaliumLogLevel.VERBOSE -> Severity.Verbose
KaliumLogLevel.DEBUG -> Severity.Debug
KaliumLogLevel.INFO -> Severity.Info
KaliumLogLevel.WARN -> Severity.Warn
KaliumLogLevel.ERROR -> Severity.Error
KaliumLogLevel.DISABLED -> Severity.Assert
}

fun Severity.toKaliumLogLevel(): KaliumLogLevel = when (this) {
Severity.Verbose -> KaliumLogLevel.VERBOSE
Severity.Debug -> KaliumLogLevel.DEBUG
Severity.Info -> KaliumLogLevel.INFO
Severity.Warn -> KaliumLogLevel.WARN
Severity.Error -> KaliumLogLevel.ERROR
Severity.Assert -> KaliumLogLevel.DISABLED
}

/**
* the logWriter is to create a custom writer other than the existing log writers from kermit to intercept the logs
* in the android case we use it to write the logs on file
*
* Custom logger writer which uses multiplatform [KermitLogger] underneath to allow to customize log message or tag.
* @param config the [Config] object which contains the configuration of the logger
* @param tag the [Tag] object which identifies the source of the log message. Can combine multiple data and turns it
* into structured String used by the [KermitLogger] so that it can be parsed back again to the [Tag] object.
* To know more how it behaves and what are the possibilities, take a look at the [Tag] sealed class and its subtypes.
*/
class KaliumLogger(private val config: Config, vararg logWriters: LogWriter = arrayOf()) {
class KaliumLogger(
private val config: Config = Config.DEFAULT,
private val tag: Tag = Tag.Text("KaliumLogger")
) {

private var kermitLogger: KermitLogger
constructor(
config: Config = Config.DEFAULT,
tag: String = "KaliumLogger"
) : this(config, Tag.Text(tag))
private val kermitLogger: KermitLogger = KermitLogger(
config = config.kermitConfig()
)

init {
kermitLogger = if (logWriters.isEmpty()) {
KermitLogger(
config = StaticConfig(
minSeverity = config.severityLevel, listOf(platformLogWriter())
),
tag = config.tag
)
} else {
KermitLogger(
config = StaticConfig(
minSeverity = config.severityLevel, logWriters.asList()
),
tag = config.tag
)
}
}
private fun tag(): String = tag.tagString()

val severity = config.severity
fun logLevel(): KaliumLogLevel = config.logLevel()

/**
* Creates a new logger with custom tag that replaces the old tag and allows to specify which specific app flow,
* one of [ApplicationFlow], the logs sent by this logger relate to.
* When the logger already contains [Tag.UserClientText] type of logs, then user-related tag data will still be included,
* and this featureId tag part will be added as a prefix, to keep the standard pattern of the tag: "tag[userId|clientId]".
* In this case it will become "featureId:featureName[userId|clientId]".
* When current type of tag is [Tag.Text], then it will just replace it with the new one: "featureId:featureName".
*/
@Suppress("unused")
fun withFeatureId(featureId: ApplicationFlow): KaliumLogger {
val currentLogger = this
currentLogger.kermitLogger = kermitLogger.withTag("featureId:${featureId.name.lowercase()}")
return currentLogger
}
fun withFeatureId(featureId: ApplicationFlow): KaliumLogger = KaliumLogger(
config = config,
tag = "featureId:${featureId.name.lowercase()}".let {
when (tag) {
is Tag.Text -> Tag.Text(it)
is Tag.UserClientText -> Tag.UserClientText(it, tag.data)
}
}
)

/**
* Creates a new logger with custom tag that replaces the old tag and allows to add user-related data to the tag.
* When the logger already contains [Tag.UserClientText] type of tag, then user-related tag data part will be replaced,
* and if it contained already some text tag prefix part, then the same prefix will be also included in the new one,
* to keep the standard pattern of the tag: "tag[userId|clientId]".
*/
@Suppress("unused")
fun withUserDeviceData(data: () -> UserClientData): KaliumLogger = KaliumLogger(
config = config,
tag = when (tag) {
is Tag.Text -> Tag.UserClientText(tag.text, data)
is Tag.UserClientText -> Tag.UserClientText(tag.prefix, data)
}
)

@Suppress("unused")
fun v(message: String, throwable: Throwable? = null) =
throwable?.let {
kermitLogger.v(message, throwable)
} ?: kermitLogger.v(message)
fun v(message: String, throwable: Throwable? = null, tag: String = this.tag()) =
kermitLogger.v(message, throwable, tag)

@Suppress("unused")
fun d(message: String, throwable: Throwable? = null) =
throwable?.let {
kermitLogger.d(message, throwable)
} ?: kermitLogger.d(message)
fun d(message: String, throwable: Throwable? = null, tag: String = this.tag()) =
kermitLogger.d(message, throwable, tag)

@Suppress("unused")
fun i(message: String, throwable: Throwable? = null) =
throwable?.let {
kermitLogger.i(message, throwable)
} ?: kermitLogger.i(message)
fun i(message: String, throwable: Throwable? = null, tag: String = this.tag()) =
kermitLogger.i(message, throwable, tag)

@Suppress("unused")
fun w(message: String, throwable: Throwable? = null) =
throwable?.let {
kermitLogger.w(message, throwable)
} ?: kermitLogger.w(message)
fun w(message: String, throwable: Throwable? = null, tag: String = this.tag()) =
kermitLogger.w(message, throwable, tag)

@Suppress("unused")
fun e(message: String, throwable: Throwable? = null) =
throwable?.let {
kermitLogger.e(message, throwable)
} ?: kermitLogger.e(message)
fun e(message: String, throwable: Throwable? = null, tag: String = this.tag()) =
kermitLogger.e(message, throwable, tag)

class Config(
val severity: KaliumLogLevel,
val tag: String
val initialLevel: KaliumLogLevel,
val initialLogWriterList: List<LogWriter> = listOf(platformLogWriter())
) {
val severityLevel: Severity = when (severity) {
KaliumLogLevel.VERBOSE -> Severity.Verbose
KaliumLogLevel.DEBUG -> Severity.Debug
KaliumLogLevel.INFO -> Severity.Info
KaliumLogLevel.WARN -> Severity.Warn
KaliumLogLevel.ERROR -> Severity.Error
KaliumLogLevel.DISABLED -> Severity.Assert
private val mutableKermitConfig = object : MutableLoggerConfig {
override var logWriterList: List<LogWriter> = initialLogWriterList
override var minSeverity: Severity = initialLevel.toMinSeverity()
}

fun logLevel(): KaliumLogLevel = mutableKermitConfig.minSeverity.toKaliumLogLevel()

fun kermitConfig(): LoggerConfig = mutableKermitConfig

@Suppress("unused")
fun setLogLevel(level: KaliumLogLevel) {
mutableKermitConfig.minSeverity = level.toMinSeverity()
}

@Suppress("unused")
fun setLogWriterList(logWriterList: List<LogWriter>) {
mutableKermitConfig.logWriterList = logWriterList
}

companion object {
val DISABLED = Config(
severity = KaliumLogLevel.DISABLED,
tag = "KaliumLogger"
val DEFAULT = disabled()
fun disabled(): Config = Config(
initialLevel = KaliumLogLevel.DISABLED,
initialLogWriterList = listOf(platformLogWriter()),
)
}
}

/**
* Defined types of tags that can be provided to the [KaliumLogger] as a String text.
*/
sealed class Tag {
abstract fun tagString(): String

/**
* Simple String text tag.
*/
data class Text(val text: String) : Tag() {
override fun tagString(): String = text
}

/**
* User-related data tag. Contains String text prefix and [UserClientData] (userId and clientId).
* It will be added to the tag in the standard pattern: "tag[userId|clientId]",
* so it can be combined with a [Tag.Text] type by adding the tag text as a prefix in this one.
*/
data class UserClientText(val prefix: String, val data: () -> UserClientData) : Tag() {
override fun tagString(): String = data().let { "$prefix[${it.userId}|${it.clientId}]" }
}
}

data class UserClientData(val userId: String, val clientId: String) {

companion object {
private val regex = Regex("^.*\\[.+\\|.+\\]\$")

/**
* Parses the user-related data from the String tag in the standard pattern: "tag[userId|clientId]".
* Returns null if the tag doesn't match the pattern, which means it does not contain user-related data.
*/
@Suppress("unused")
fun getFromTag(tag: String): UserClientData? =
if (tag.matches(regex)) {
tag.substringAfterLast("[").substringBefore("]").split("|")
.let { data -> UserClientData(data[0], data[1]) }
} else null
}
}

companion object {
fun disabled(): KaliumLogger = KaliumLogger(
config = Config.DISABLED
config = Config.disabled(),
tag = "KaliumLogger"
)

enum class ApplicationFlow {
Expand Down
33 changes: 12 additions & 21 deletions logic/src/commonMain/kotlin/com/wire/kalium/logic/CoreLogger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,29 @@

package com.wire.kalium.logic

import co.touchlab.kermit.LogWriter
import com.wire.kalium.cryptography.CryptographyLogger
import com.wire.kalium.logger.KaliumLogLevel
import com.wire.kalium.logger.KaliumLogger
import com.wire.kalium.network.NetworkLogger
import com.wire.kalium.network.NetworkUtilLogger
import com.wire.kalium.persistence.PersistenceLogger

private var kaliumLoggerConfig = KaliumLogger.Config.disabled()
internal var kaliumLogger = KaliumLogger.disabled()
internal var callingLogger = KaliumLogger.disabled()

object CoreLogger {
fun setLoggingLevel(level: KaliumLogLevel, vararg logWriters: LogWriter = arrayOf()) {
kaliumLogger = KaliumLogger(
config = KaliumLogger.Config(
severity = level,
tag = "CoreLogic"
),
logWriters = logWriters
)

callingLogger = KaliumLogger(
config = KaliumLogger.Config(
severity = level,
tag = "Calling"
),
logWriters = logWriters
)

NetworkLogger.setLoggingLevel(level = level, logWriters = logWriters)
NetworkUtilLogger.setLoggingLevel(level = level, logWriters = logWriters)
CryptographyLogger.setLoggingLevel(level = level, logWriters = logWriters)
PersistenceLogger.setLoggingLevel(level = level, logWriters = logWriters)
fun init(config: KaliumLogger.Config) {
kaliumLoggerConfig = config
kaliumLogger = KaliumLogger(config = kaliumLoggerConfig, tag = "CoreLogic")
callingLogger = KaliumLogger(config = kaliumLoggerConfig, tag = "Calling")
NetworkLogger.init(config = config)
NetworkUtilLogger.init(config = config)
CryptographyLogger.init(config = config)
PersistenceLogger.init(config = config)
}
fun setLoggingLevel(level: KaliumLogLevel) {
kaliumLoggerConfig.setLogLevel(level)
}
}
Loading

0 comments on commit d706d77

Please sign in to comment.