Kotlin multi-platform logging library with structured logging and coroutines support

Overview

Klogging Library

Klogging

License Build Maven Central

Klogging is a pure-Kotlin logging library that aims to be flexible and easy to use. It uses Kotlin idioms for creating loggers and sending log events. It takes advantage of Kotlin coroutines in environments that use them, for example the Ktor asynchronous service framework.

See https://klogging.io for more detailed documentation.

🚧 Klogging is alpha software in rapid development. The API and key implementation details will change 🚧

Contents

Goals

  • Provide a familiar logging experience for Java and C# developers.
  • Create structured log events by default.
  • Use message templates for simple logging of both text and data.
  • Use Kotlin coroutines for carrying scope context information to include in log events and for asynchronous dispatching of events.
  • Finest possible resolution of timestamps, down to nanosecond if available.
  • (Future) Pure Kotlin multiplatform. Current development focuses on the JVM.

Quick start (JVM)

  1. Include Klogging in your project with Gradle:

    implementation("io.klogging:klogging-jvm:0.4.0")

    or Maven:

    <dependency>
      <groupId>io.klogginggroupId>
      <artifactId>klogging-jvmartifactId>
      <version>0.4.0version>
    dependency>
  2. Configure logging early in your program startup using the configuration DSL. For simple logging to the console at INFO or higher level (more severe):

    fun main() = runBlocking {
        loggingConfiguration { DEFAULT_CONSOLE() }
        // ...
    }
  3. Create a logger attribute for a class, for example by using the Klogging interface for logging inside coroutines:

    class ImportantStuff : Klogging {
        suspend fun cleverAction(runId: String, input: String) = coroutineScope {
            launch(logContext("runId" to runId)) {
                logger.info("cleverAction using {input}", input)
            }
        }
    }

    Or by using the NoCoLogging interface for logging outside coroutines:

    class OtherStuff : NoCologging {
        fun funkyAction(input: String) {
            logger.info("funkyAction using {input}", input)
        }
    }

Using snapshot builds

If you want to use the latest snapshot builds, specify these in your build.gradle.kts:

repositories {
    // ...
    maven {
        url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
    }
}

dependencies {
    // ...
    implementation("io.klogging:klogging-jvm:0.4.0-SNAPSHOT")
}

Why another logging library?

Klogging is designed from the ground up to be standalone, pure Kotlin and to be used with coroutines.

I could not find a logging library for Kotlin that meets these requirements:

  • Send structured log events by default.
  • Simple, reliable capture and logging of information from the current execution scope.
  • High-resolution timestamps to ensure log events are aggregated in the correct order.

Why not Logback or Log4j?

These solid, but venerable Java libraries have formed the backbone of Java logging for more than 10 years. The limitations I find are:

  • They are designed to log strings of text. Whe you want to search for or filter logs by values within those messages you need to search within, or parse the strings.

  • There are add-ons for including structured data in logs, for example Logstash Logback Encoder, but they feel clumsy to use.

  • MDC (SLF4J/Logback) and ThreadContext (Log4j2) provide storage for context information but scopes are independent of thread lifecycles and need to be managed separately.

  • Logback is hamstrung by having timestamp resolution limited to milliseconds. This limit is baked in to the core of the library: that long value is milliseconds since the Unix Epoch.

Why not KotlinLogging, Log4j Kotlin, etc.?

These libraries (mostly) wrap underlying Java libraries and suffer from the same limitations.

Comments
  • Configurable contextItems for Klogger

    Configurable contextItems for Klogger

    It would be great if extraction of contextItems could be configurable, ability to provide a lambda etc.

    This way I could implement my own function to extract fields from for example ReactorContext (kotlinx.coroutines.reactor.ReactorContext) to avoid adding klogging context and mapping fields there on all log invocations.

    I suppose I could implement my own Klogger for this, but base support seems nice to have.

    enhancement 
    opened by skotsj 13
  • Exception when using any type of log

    Exception when using any type of log

    Whenever I use any type of log in my application, I get this exception below. For context, it is a Compose for Desktop application. I have tried defining the logger both through an interface and directly and they all cause this same crash once any log tries to be run.

    Exception in thread "DefaultDispatcher-worker-1" java.lang.IncompatibleClassChangeError: Found interface kotlin.time.TimeMark, but class was expected
    	at io.klogging.sending.BatchingKt.receiveBatch(Batching.kt:48)
    	at io.klogging.internal.Sink$sinkChannel$2$1.invokeSuspend(Sink.kt:58)
    	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
    	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
    	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [CoroutineName(sink-console), StandaloneCoroutine{Cancelling}@5415e1fd, Dispatchers.Default]
    
    opened by russellbanks 10
  • #17 Updates jacoco configuration for multiplatform project

    #17 Updates jacoco configuration for multiplatform project

    The issue is with jacoco configuration, with existing configuration jacoco was not able to generate report for kotlin multiplatform project.

    Reference: https://stackoverflow.com/questions/59802396/kotlin-multiplatform-coverage Coverage Report: https://codecov.io/github/ashishkujoy/klogging/commit/7702016938f7c55c0f117db48385d8e30bbf9caf

    hacktoberfest-accepted 
    opened by ashishkujoy 3
  • Create single object with Klogging state

    Create single object with Klogging state

    There is a single Kotlin object with state of Klogging in a running application. It contains properties with holders for values that can be safely mutated.

    Properties to include:

    • MInimum log level for Klogging
    • Current configuration in a KloggingConfiguration (as class) isntance
    • Coroutine dispatcher(s) used by Klogging
    opened by mjstrasser 3
  • Improvements to `Timestamp`

    Improvements to `Timestamp`

    These tests illustrate shortcomings to Timestamp:

        describe("Timestamps") {
            val somewhen = Timestamp(2L, 3L)
    
            it("represents as UTC time") {
                somewhen.isoString shouldBe "1970-01-01T00:00:02.000000003Z"
            }
            it("represents as local time") {
                // TODO: Control for locale and time zone
                somewhen.localString shouldBe "1970-01-01 08:00:02.000000003"
            }
        }
    
    1. Local time does not show the time zone or offset from GMT
    2. No format configuration available
      • Some users may want to control timestamp format, eg, for consumption by a system with format limitations
      • Item # 1 is less a concern if users can configure format
    3. Valid ISO 8601 formats include, meaning, these are the "official" formats (usually with an extension for millis, micros, or nanos), so there is not a single universal choice:
      • 2021-08-06T03:08:10+00:00
      • 2021-08-06T03:08:10Z
      • 20210806T030810Z
    4. For timestamps, local formatting should be strongly discouraged

    (Please ignore the TODO marker above. This is a note for myself to avoid a flaky time zone-based test.)

    opened by binkley 3
  • Reading tweaks, and fix build

    Reading tweaks, and fix build

    • Fix a typo with numbering of sequential items (1,2,3 vs 1,2,4)
    • Move logo to right, flow text around it
    • Scale logo (% vs pt) so it looks right on any device
    • IntelliJ reformatted the text layout
    • A grammar tweak

    Note: When Klogging has an independent site (eg, like Batect), the logo can be wrapped with an anchor tag for each one-click access.

    opened by binkley 3
  • Call log methods, without checking whether the respective log level is enabled

    Call log methods, without checking whether the respective log level is enabled

    Would be nice if you add support for lazy API as well e.g. logger.debug { "Some $expensive message!" }

    fun debug(msg: () -> Any?) {
        if(isDebugEnabled)  debug(msg().takeIf { it != Unit }?.toString().orEmpty())
    }
    
    opened by qoomon 2
  • Bump codecov/codecov-action from 2.1.0 to 3

    Bump codecov/codecov-action from 2.1.0 to 3

    Bumps codecov/codecov-action from 2.1.0 to 3.

    Release notes

    Sourced from codecov/codecov-action's releases.

    v3.0.0

    Breaking Changes

    • #689 Bump to node16 and small fixes

    Features

    • #688 Incorporate gcov arguments for the Codecov uploader

    Dependencies

    • #548 build(deps-dev): bump jest-junit from 12.2.0 to 13.0.0
    • #603 [Snyk] Upgrade @​actions/core from 1.5.0 to 1.6.0
    • #628 build(deps): bump node-fetch from 2.6.1 to 3.1.1
    • #634 build(deps): bump node-fetch from 3.1.1 to 3.2.0
    • #636 build(deps): bump openpgp from 5.0.1 to 5.1.0
    • #652 build(deps-dev): bump @​vercel/ncc from 0.30.0 to 0.33.3
    • #653 build(deps-dev): bump @​types/node from 16.11.21 to 17.0.18
    • #659 build(deps-dev): bump @​types/jest from 27.4.0 to 27.4.1
    • #667 build(deps): bump actions/checkout from 2 to 3
    • #673 build(deps): bump node-fetch from 3.2.0 to 3.2.3
    • #683 build(deps): bump minimist from 1.2.5 to 1.2.6
    • #685 build(deps): bump @​actions/github from 5.0.0 to 5.0.1
    • #681 build(deps-dev): bump @​types/node from 17.0.18 to 17.0.23
    • #682 build(deps-dev): bump typescript from 4.5.5 to 4.6.3
    • #676 build(deps): bump @​actions/exec from 1.1.0 to 1.1.1
    • #675 build(deps): bump openpgp from 5.1.0 to 5.2.1
    Changelog

    Sourced from codecov/codecov-action's changelog.

    3.0.0

    Breaking Changes

    • #689 Bump to node16 and small fixes

    Features

    • #688 Incorporate gcov arguments for the Codecov uploader

    Dependencies

    • #548 build(deps-dev): bump jest-junit from 12.2.0 to 13.0.0
    • #603 [Snyk] Upgrade @​actions/core from 1.5.0 to 1.6.0
    • #628 build(deps): bump node-fetch from 2.6.1 to 3.1.1
    • #634 build(deps): bump node-fetch from 3.1.1 to 3.2.0
    • #636 build(deps): bump openpgp from 5.0.1 to 5.1.0
    • #652 build(deps-dev): bump @​vercel/ncc from 0.30.0 to 0.33.3
    • #653 build(deps-dev): bump @​types/node from 16.11.21 to 17.0.18
    • #659 build(deps-dev): bump @​types/jest from 27.4.0 to 27.4.1
    • #667 build(deps): bump actions/checkout from 2 to 3
    • #673 build(deps): bump node-fetch from 3.2.0 to 3.2.3
    • #683 build(deps): bump minimist from 1.2.5 to 1.2.6
    • #685 build(deps): bump @​actions/github from 5.0.0 to 5.0.1
    • #681 build(deps-dev): bump @​types/node from 17.0.18 to 17.0.23
    • #682 build(deps-dev): bump typescript from 4.5.5 to 4.6.3
    • #676 build(deps): bump @​actions/exec from 1.1.0 to 1.1.1
    • #675 build(deps): bump openpgp from 5.1.0 to 5.2.1
    Commits
    • e3c5604 Merge pull request #689 from codecov/feat/gcov
    • 174efc5 Update package-lock.json
    • 6243a75 bump to 3.0.0
    • 0d6466f Bump to node16
    • d4729ee fetch.default
    • 351baf6 fix: bash
    • d8cf680 Merge pull request #675 from codecov/dependabot/npm_and_yarn/openpgp-5.2.1
    • b775e90 Merge pull request #676 from codecov/dependabot/npm_and_yarn/actions/exec-1.1.1
    • 2ebc2f0 Merge pull request #682 from codecov/dependabot/npm_and_yarn/typescript-4.6.3
    • 8e2ef2b Merge pull request #681 from codecov/dependabot/npm_and_yarn/types/node-17.0.23
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies github_actions 
    opened by dependabot[bot] 2
  • Match logger names that contain regular expression special characters

    Match logger names that contain regular expression special characters

    When a configuration specifies logger names that contain Regex special characters, they don’t match properly.

    For example the configuration:

        exactLogger("[R]:[KTOR]:[ExclusionRequestRateLimiter]")
    

    will not match because code in LoggingConfig constructs a Regex instance with:

        nameMatch = Regex("^$exactName\$")
    
    bug 
    opened by mjstrasser 2
  • Testing for `NONE` log level

    Testing for `NONE` log level

    Several commits together. Key points:

    • I started from writing tests, and wound up with the below to get the tests passing (see "does not log" block of tests)
    • IMPORTANT -- please review how NONE is handled, especially for: public fun isLevelEnabled(level: Level): Boolean = NONE != level && minLevel() <= level
    • Short-circuit checks in methods like log to skip processing if the log level is disabled
    • Formatting courtesy of IntelliJ
    • Remove useless code in batect.yml (which I added myself previously: I had not noticed the GRADLE_OPTS env var)
    opened by binkley 2
  • Package naming

    Package naming

    This is more a matter of personal taste. More open source projects use a package naming scheme corresponding roughly to a reversed FQDN domain name.

    So perhaps, org.klogger rather than just klogger?

    I also note that the domain name, "klogger.org", is not taken.

    opened by binkley 2
  • Performance-test `Dispatcher.sinksFor()` function

    Performance-test `Dispatcher.sinksFor()` function

    The function Dispatcher.sinksFor() calculates the current set of sinks every time a log event is dispatched. This might be very inefficient with large numbers of log events.

    Perhaps introduce a simple cache (map) of sinks, keyed by logger name. Considerations:

    • What if there is a very large number of logger names? Could the map grow very large? (YAGNI?)
    • How often might the configuration change? Level configurations can be changed by calling the config DSL in a running application. Perhaps introduce simple cache invalidation? (YAGNI?)
    opened by mjstrasser 1
  • Thread.sleep() necessary to see the logs?

    Thread.sleep() necessary to see the logs?

    Given this main function

    fun main() {
        loggingConfiguration {
            DEFAULT_CONSOLE()
        }
        val logger = noCoLogger("Log")
        logger.info("Hello World")
    
        /** Uncomment to see the log: INFO [main] Log : Hello World **/
        // Thread.sleep(100)
    }
    

    I expect to see:

    12:46:55.381571  INFO [                main] :        Configuration : Setting configuration using the DSL with append=false
    2022-03-01 12:47:19.539911 INFO [main] Log : Hello World
    

    but I see

    12:46:55.381571  INFO [                main] :        Configuration : Setting configuration using the DSL with append=false
    

    If I uncomment the Thread.sleep(100), I see both lines.

    What I am missing?

    opened by jmfayard 1
  • Optionally record file and line information in log events

    Optionally record file and line information in log events

    Optionally add file and line information to log events. See discussion in this thread: https://kotlinlang.slack.com/archives/C1NNEMDBK/p1634476948004100

    enhancement 
    opened by mjstrasser 0
  • Make Klogging file configuration more idiomatic

    Make Klogging file configuration more idiomatic

    Currently Klogging may be configured with klogging.json, but it's a better practice in the JVM world to use HOCON for user-controlled (i.e. not generated) configuration. tl;dr:

    JSON:

    - easy to generate programmatically
    - well-defined and standard
    - bad for human maintenance, with no way to write comments, and no mechanisms to avoid duplication of similar config sections
    

    HOCON/.conf:

    - nice for humans to read, type, and maintain, with more lenient syntax
    - built-in tools to avoid cut-and-paste
    - ways to refer to the system environment, such as system properties and environment variables
    

    Also, Ktor uses resources/application.conf (i.e. HOCON file in resources dir) for server configuration, so sourcing Klogging config from resources/klogging.conf would add more consistency to Ktor app configuration.

    opened by GinGin3203 3
  • Mapping syslog/greylog log levels

    Mapping syslog/greylog log levels

    For RenderGelf, the FATAL Klogging level, maps to "2" (critical) level for syslog/greylog. Why "critical" as opposed to "alert"? Perhaps documentation explaining the mapping.

    opened by binkley 0
Owner
Klogging
Easy and powerful logging from Kotlin with coroutine support
Klogging
This is an Kotlin Library that enables Annotation-triggered method call logging for Kotlin Multiplatform.

This is an Kotlin Library that enables Annotation-triggered method call logging for Kotlin Multiplatform.

Jens Klingenberg 187 Dec 18, 2022
simple Kotlin logging: colorized logs for Kotlin on the JVM

sklog - simple Kotlin logging Kotlin (JVM) logging library for printing colorized text to the console, with an easy upgrade path to the popular kotlin

null 1 Jul 26, 2022
An in-display logging library for Android 📲

Vlog provides an easy and convenient way to access logs right on your phone.

girish budhwani 121 Dec 26, 2022
Jambo is an open source remote logging library

Jambo Jambo is an open source remote logging library. For those who would like to see their logs remotely on their android device Jambo is the library

Tabasumu 6 Aug 26, 2022
Kermit is a Kotlin Multiplatform logging utility with composable log outputs

Kermit is a Kotlin Multiplatform logging utility with composable log outputs. The library provides prebuilt loggers for outputting to platform logging tools such as Logcat and NSLog.

Touchlab 395 Jan 4, 2023
A tiny Kotlin API for cheap logging on top of Android's normal Log class.

A tiny Kotlin API for cheap logging on top of Android's normal Log class.

Square 849 Dec 23, 2022
A demonstration of Kotlin-logging with logback.

try-kotlin-logging A demonstration of Kotlin-logging with logback. Usage # output a log to STDOUT and file(myApp.log) $ ./gradlew run # => 2021-12-11

Hayato Tachikawa 1 Dec 13, 2021
📄The reliable, generic, fast and flexible logging framework for Android

logback-android v2.0.0 Overview logback-android brings the power of logback to Android. This library provides a highly configurable logging framework

Tony Trinh 1.1k Jan 5, 2023
Annotation-triggered method call logging for your debug builds.

Hugo Annotation-triggered method call logging for your debug builds. As a programmer, you often add log statements to print method calls, their argume

Jake Wharton 7.9k Dec 31, 2022
An OkHttp interceptor which has pretty logger for request and response. +Mock support

LoggingInterceptor - Interceptor for OkHttp3 with pretty logger Usage val client = OkHttpClient.Builder() client.addInterceptor(LoggingInterceptor

ihsan BAL 1.3k Dec 26, 2022
Napier is a logger library for Kotlin Multiplatform.

Napier is a logger library for Kotlin Multiplatform. It supports for the android, ios, jvm, js. Logs written in common module are displayed on logger

Akira Aratani 457 Jan 7, 2023
Utility logger library for storing logs into database and push them to remote server for debugging

HyperLog Android Overview Log format Download Initialize Usage Get Logs in a File Push Logs Files to Remote Server Sample Testing Endpoint using Reque

HyperTrack 675 Nov 14, 2022
Library that makes debugging, log collection, filtering and analysis easier.

AndroidLogger Android Library that makes debugging, log collection, filtering and analysis easier. Contains 2 modules: Logger: 'com.github.ShiftHackZ.

ShiftHackZ 2 Jul 13, 2022
Gadget is a library that makes analytics tracking easier for android apps

gadget (In RC Stage) Gadget is a library that makes analytics tracking easier for android apps.

Taylan Sabırcan 54 Nov 29, 2022
FileLogger - a library for saving logs on Files with custom-formatter on background I/O threads, mobile-ready, android compatible,

The FileLogger is a library for saving logs on Files with custom-formatter on background I/O threads, mobile-ready, android compatible, powered by Java Time library for Android.

Abolfazl Abbasi 12 Aug 23, 2022
Yakl - Yet Another Kotlin Logger

YAKL Yet Another Kotlin Logger. Motivation Current jvm loggers have some disadva

Mark Kosichkin 4 Jan 19, 2022
✔️ Simple, pretty and powerful logger for android

Logger Simple, pretty and powerful logger for android Setup Download implementation 'com.orhanobut:logger:2.2.0' Initialize Logger.addLogAdapter(new A

Orhan Obut 13.5k Dec 30, 2022
Timber + Logger Integration. Make Logcat Prettier, show thread information and more.

Pretty Timber Android Logcat Timber + Logger Integration Video Instructions: https://youtu.be/zoS_i8VshCk Code App.kt class App : Application() {

Awesome Dev Notes | Android Dev Notes YouTube 29 Jun 6, 2022
Simple application to log your mood through the day and explain feature flags.

Mood Logger App (Android version) This Repo This repository contains code for building a very basic application to log your mood through the days. The

MongoDB Developer Relations 3 Oct 24, 2021