{"id":29873397,"url":"https://github.com/koriit/slf4j-utils","last_synced_at":"2025-08-17T11:11:38.001Z","repository":{"id":57725954,"uuid":"218063664","full_name":"Koriit/slf4j-utils","owner":"Koriit","description":"SLF4J utils for Kotlin","archived":false,"fork":false,"pushed_at":"2022-01-09T20:03:49.000Z","size":147,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-30T23:49:23.571Z","etag":null,"topics":["kotlin","kotlin-coroutines","logging","slf4j","utils"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Koriit.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-28T14:18:34.000Z","updated_at":"2022-01-15T22:47:21.000Z","dependencies_parsed_at":"2022-09-02T04:02:29.947Z","dependency_job_id":null,"html_url":"https://github.com/Koriit/slf4j-utils","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/Koriit/slf4j-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koriit%2Fslf4j-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koriit%2Fslf4j-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koriit%2Fslf4j-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koriit%2Fslf4j-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Koriit","download_url":"https://codeload.github.com/Koriit/slf4j-utils/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Koriit%2Fslf4j-utils/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270837583,"owners_count":24654391,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-17T02:00:09.016Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["kotlin","kotlin-coroutines","logging","slf4j","utils"],"created_at":"2025-07-30T23:01:27.340Z","updated_at":"2025-08-17T11:11:37.992Z","avatar_url":"https://github.com/Koriit.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"= SLF4J Utils\n\nimage:https://github.com/Koriit/slf4j-utils/actions/workflows/build.yaml/badge.svg[Build, link=\"https://github.com/Koriit/slf4j-utils/actions/workflows/build.yaml\"]\nimage:https://www.codefactor.io/repository/github/koriit/slf4j-utils/badge[CodeFactor,link=https://www.codefactor.io/repository/github/koriit/slf4j-utils]\nimage:https://img.shields.io/badge/code%20style-%E2%9D%A4-FF4081.svg[ktlint,link=https://ktlint.github.io/]\n\nimage:https://img.shields.io/maven-central/v/com.koriit.kotlin/slf4j-utils.svg?label=Maven%20Central[Maven Central, link=\"https://search.maven.org/search?q=g:%22com.koriit.kotlin%22%20AND%20a:%22slf4j-utils%22\"]\nimage:https://img.shields.io/github/license/koriit/slf4j-utils[GitHub]\n\nWARNING: From version 0.8.0 all package names have been renamed to match new artifact group id.\n\nSLF4J utils for Kotlin.\n\n== Logger factory\nWith Kotlin lambdas, creating a logger object can be as simple as `logger {}`.\n\nPlease, note that this function is opinionated. Logger name is a fully qualified name of\nclass/file where logger object is defined.\nThis allows precise control over your loggers.\n\n== Bridges\nThis library also includes bridges that intercept JUL, JCL, and Log4J log statements and pass them to SLF4J.\n\nThis is handy as there are many libraries and frameworks that do not use SLF4J,\nand we usually want to see/control *all* of our logs.\n\n== Fatal Logs\nSLF4J has limited levels of logging and does not allow creating any custom levels.\nDevelopers coming from many backgrounds tend to notice that certain errors -\na.k.a. FATAL - are more important than the other and require a faster reaction.\n\nAccording to SLF4J's http://www.slf4j.org/faq.html#fatal[FAQ]:\n[quote]\nThe Marker interface, part of the org.slf4j package, renders the FATAL level largely redundant. If a given error requires attention beyond that allocated for ordinary errors, simply mark the logging statement with a specially designated marker which can be named \"FATAL\" or any other name to your liking.\n\nHowever, preparing such a marker and adding it to every fatal error can be a bit tedious.\nThus this library provides a set of `fatal` extension functions that do this for you.\n\n[WARNING]\nFatal logs are still logged as `ERROR`. Whether this difference is visible in the\noutput depends on your format, whether it includes markers in some way.\n\n== Exception Handling\nThis library provides `watch` and `watched` extension functions on the `Logger` to\ncreate a simple exception handler that just logs caught exception and eats it, or\nshuts down the JVM.\n\nIt is useful in concurrent, long-lived workers, for example:\n\n.Logger.watch\n[source,kotlin]\n----\nprivate val log = logger {}\n\nprivate val jobQueue = Channel\u003cJob\u003e(UNLIMITED)\n\nprivate val worker = launch(correlated()) {\n    with(log) {\n        info(\"Started worker\")\n        watch(fatal = true) {\n            for (job in jobQueue) {\n                continueCorrelation((job as Continuation\u003c*\u003e).context) {\n                    watch(ignore = listOf(MyCancellation::class)) {\n                        job.await()\n                    }\n                }\n            }\n        }\n        jobQueue.close()\n        info(\"Closed worker\")\n    }\n}\n----\n\n.Logger.watched\n[source,kotlin]\n----\n// Ktor config\nparentCoroutineContext += continueCorrelation() + log.watched(shutdown = true)\n----\n\n== Correlation\n\nIt may prove challenging to search for all logs related to a particular process, especially\nin applications that use any kind of concurrency as it leads to the interleaving of logs.\n\nTo solve this issue it is possible to add special correlation id to logs that belong to the same process.\n\nThe easiest way to achieve that is by using thread-local MDC provided by SLF4j.\n\nHowever, it is a bit harder with Kotlin's coroutines as usually they are not bound to any specific thread during its execution.\nThe proper solution requires building correlated coroutine contexts.\n\nThe following functions ease the building of said coroutine contexts and correlating threads in general.\n\n=== Correlated\n[source,kotlin]\n----\n// ...\n// Threaded code (Correlation #1)\n// ...\n\nval job = launch(correlated()) {\n    // (Correlation #2)\n    while (isActive) {\n        withCorrelation {\n            // (Correlation #3)\n        }\n    }\n    // (Correlation #2)\n}\n\n// ...\n// Threaded code (Correlation #1)\n// ...\n----\n\n=== SubCorrelated\n[NOTE]\nSub-correlation can be nested only by one level. This is by design.\n\n[source,kotlin]\n----\n// ...\n// Threaded code (Correlation #1, no SubCorrelation)\n// ...\n\nval job = launch(continueCorrelation() + subCorrelated()) {\n    // (Correlation #1, SubCorrelation #1)\n    while (isActive) {\n        withSubCorrelation {\n            // (Correlation #1, SubCorrelation #2)\n        }\n    }\n    // (Correlation #1, SubCorrelation #2)\n}\n\n// ...\n// Threaded code (Correlation #1, no SubCorrelation)\n// ...\n----\n\n=== ContinueCorrelation\n[source,kotlin]\n----\n// ...\n// Threaded code (Correlation #1, no SubCorrelation)\n// ...\n\nval job = async(continueCorrelation() + subCorrelated(), start=LAZY) {\n    // (Correlation #1, SubCorrelation #1)\n}\n\n// ...\n// Other threaded code (Correlation #2, no SubCorrelation)\n// ...\nlaunch(continueCorrelation() + subCorrelated()) {\n    // (Correlation #2, SubCorrelation #2)\n    continueCorrelation((job as Continuation\u003c*\u003e).context) {\n        // (Correlation #1, SubCorrelation #1)\n        val value = job.await()\n        // (Correlation #1, SubCorrelation #1)\n    }\n    // (Correlation #2, SubCorrelation #2)\n}\n\n----\n\n=== ThreadCorrelation\n[source,kotlin]\n----\nfun main() {\n    // (no Correlation)\n    val correlationId = correlateThread()\n    // (Correlation #1)\n\n    Thread {\n        // (no Correlation)\n        correlateThread(correlationId)\n        // (Correlation #1)\n    }.run()\n\n    // (Correlation #1)\n\n    Thread {\n        // (no Correlation)\n        correlateThread(correlationId = correlationId, subCorrelationId = newCorrelationId())\n        // (Correlation #1, SubCorrelation #1)\n    }.run()\n\n    // (Correlation #1)\n\n    Thread {\n        // (no Correlation)\n        correlateThread()\n        // (Correlation #2)\n    }.run()\n\n    // (Correlation #1)\n    // ...\n}\n----\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoriit%2Fslf4j-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkoriit%2Fslf4j-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoriit%2Fslf4j-utils/lists"}