Kermit Logging Library

repository·main·Indexed 21 days ago

https://github.com/touchlab/kermit

A unified logging API for Kotlin Multiplatform designed to provide consistent logging across Android, iOS, and Browser platforms. Kermit includes extensions for integration with Bugsnag, Firebase Crashlytics, Koin, and Ktor, as well as testing utilities via kermit-test for verifying log behavior.

Tokens
13.1K
Snippets
61
Records
83
Agent score
76%

What's inside Kermit

  1. Choose between StaticConfig and MutableLoggerConfig

    main

    Kermit provides two types of configuration implementations:

    1. StaticConfig: Values cannot be changed after initialization. This is the preferred choice for local Logger instances to maximize performance, as it avoids the overhead of volatile/atomic field access required for thread-safety in mutable configs.
    2. MutableLoggerConfig: Allows configuration values to be changed after the Logger is created. This is used by the global Logger instance and is necessary if you need to update a logger's settings at runtime.

    If you want the convenience of a global logger but the performance of static configuration, you can define your own global object using StaticConfig via loggerConfigInit.

    object MyLogger : Logger(
        config = loggerConfigInit(
            platformLogWriter(NoTagLogFormatter),
            minSeverity = Severity.Info
        ),
        tag = "MyAppTag"
    )
    
    fun hello(){
        MyLogger.i { "Hello" }
    }
  2. Inject loggers using Koin dependency injection helpers

    main

    Kermit provides helpers to make logger injection easier within Koin. You can use either a Koin module or a direct extension method:

    1. Using a Module: Use kermitLoggerModule() to get a Koin module that declares a factory for logger instances. This is the preferred way to enable dependency injection for loggers.
    2. Direct Extension: Use the getLoggerWithTag() extension method to retrieve a logger directly without using the Koin factory.

    Injecting logger instances is recommended over using the global Logger instance, particularly to facilitate easier unit testing.

  3. Core concepts: Logger and LogWriter

    main

    Kermit's logging architecture is built around two primary components:

    1. Logger: The core logging coordinator. It manages the logging lifecycle and dispatches log messages to registered writers.
    2. LogWriter: The implementation responsible for the actual output of log messages (e.g., writing to Logcat, Bugsnag, or Crashlytics).
  4. Configure crash log writer parameters

    main

    When using crash LogWriter implementations (for Crashlytics or Bugsnag), you can configure how logs and exceptions are handled using three parameters:

    1. minSeverity: Severity: Sets the minimum severity level required for a log statement to be recorded in the crash reporter's breadcrumb cache. The default is Severity.Info, meaning Debug and Verbose logs are ignored.
    2. minCrashSeverity: Severity?: Determines when a logged Throwable is sent as a soft/handled exception. If the log statement's severity is equal to or above this threshold, the Throwable is reported. The default is Severity.Warn. To disable sending exception reports entirely, set this to null.
    3. messageStringFormatter: MessageStringFormatter: Defines how log message strings are formatted. The default is DefaultFormatter.
  5. Understand the LogWriter abstraction

    main
    A LogWriter is responsible for deciding the destination of log messages. While the Logger provides the API for recording logs, the LogWriter implementation determines whether those logs go to the console, a platform-specific system (like Android's LogCat), or a remote server. You can provide multiple writers to the Logger to send logs to several destinations simultaneously.
  6. Understand the relationship between kermit and kermit-core

    main

    Kermit is structured into two primary layers:

    1. kermit: A thin API layer that provides the standard logging interface used by most developers.
    2. kermit-core: The underlying module containing the core configuration and functionality.

    Users who want a custom API surface should depend directly on kermit-core to avoid the constraints of the standard kermit API while retaining compatibility with Kermit extensions.

  7. How crash reporting works with Kermit

    main

    Kermit does not implement crash reporting directly. Instead, it provides LogWriter instances that act as a bridge to specialized crash reporting tools. Kermit writes breadcrumb/log statements to these tools and can be configured to send 'soft' (handled) exceptions when Throwable instances are logged.

    Crash reporting is primarily handled via CrashKiOS. Kermit and CrashKiOS currently support:

  8. Understand Log Message Components and Formatter behavior

    main

    A log message consists of three components: Severity, Tag, and the Message itself.

    Because different logging systems have varying support for these components, Kermit uses MessageStringFormatter to manage how they are presented. If a LogWriter natively supports a component (like Severity or Tag), it will pass null to the MessageStringFormatter for that component. The formatter's job is to handle cases where the underlying system does not natively support them.

    Platform Support Matrix:

    SystemSeverityTag
    Android
    OSLog (iOS)
    JS-Console
    SystemWriter (jvm)
    Common (println)

    Note: The Android LogcatWriter does not currently use a MessageStringFormatter. To customize formatting for Android, you must create a custom LogWriter implementation.

  9. Add kermit-test to your dependencies

    main

    To use the testing utilities, add kermit-test to your commonTest dependencies in your build.gradle(.kts) file.

    Note: The test APIs are currently experimental and require opting into the @ExperimentalKermitApi annotation.

    sourceSets {
        commonTest {
            dependencies {
                implementation("co.touchlab:kermit-test:x.y.z") // Add latest version
            }
        }
    }
  10. Customize Message Formatting with MessageStringFormatter

    main
    Use MessageStringFormatter to centrally configure how log messages are formatted. This is useful when LogWriter instances need to include specific metadata, such as the log tag or severity, or when you want to inject custom information into every log message string.
  11. Configure KermitKtorLogger in a Ktor client

    main

    You can integrate Kermit into your Ktor client by installing the Logging plugin and providing a KermitKtorLogger instance. There are two primary ways to initialize the logger:

    1. Using an existing KermitLogger: Pass an existing KermitLogger instance and specify the desired Severity.
    2. Using a custom configuration: Use loggerConfigInit to provide a custom CommonWriter and a specific tag.

    In both cases, you should also set the level of the Ktor Logging plugin (e.g., LogLevel.INFO) to ensure logs are captured.

    // Option 1: Using an existing KermitLogger
    val httpClient = HttpClient {
        install(Logging) {
            logger = KermitKtorLogger(severity = Severity.Info, logger = KermitLogger)
            level = LogLevel.INFO
        }
    }
    
    // Option 2: Using custom configuration via loggerConfigInit
    val httpClient = HttpClient {
        install(Logging) {
            logger = KermitKtorLogger(severity = Severity.Info, config = loggerConfigInit(CommonWriter()), tag = "")
            level = LogLevel.INFO
        }
    }
  12. Configure Kermit logging in Koin

    main

    When starting your Koin application, you can integrate Kermit by providing a KermitKoinLogger instance to the logger() function. This allows Kermit to handle Koin's internal logging. It is recommended to create a specific Kermit logger instance with a tag (e.g., "koin") for this purpose.

    val koinApplication = startKoin {
      modules( ... )
      
      val kermit = Logger.withTag("koin")
      logger(KermitKoinLogger(kermit))
    }