{"id":22750064,"url":"https://github.com/pwall567/log-front-kotlin","last_synced_at":"2026-02-21T11:00:58.442Z","repository":{"id":57739832,"uuid":"304587966","full_name":"pwall567/log-front-kotlin","owner":"pwall567","description":"Logging interface in Kotlin","archived":false,"fork":false,"pushed_at":"2025-11-11T11:14:59.000Z","size":69,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-11T11:35:21.635Z","etag":null,"topics":["kotlin","logging"],"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/pwall567.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-16T09:59:47.000Z","updated_at":"2025-11-11T11:14:44.000Z","dependencies_parsed_at":"2024-02-25T12:26:31.957Z","dependency_job_id":"218e8bf6-f177-40a7-9e57-976475cb3ece","html_url":"https://github.com/pwall567/log-front-kotlin","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/pwall567/log-front-kotlin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Flog-front-kotlin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Flog-front-kotlin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Flog-front-kotlin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Flog-front-kotlin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pwall567","download_url":"https://codeload.github.com/pwall567/log-front-kotlin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pwall567%2Flog-front-kotlin/sbom","scorecard":{"id":750707,"data":{"date":"2025-08-11","repo":{"name":"github.com/pwall567/log-front-kotlin","commit":"4667ee8a2c31334c0031f0b0a296c4a1f84fe04d"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.8,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/25 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/pwall567/log-front-kotlin/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/pwall567/log-front-kotlin/build.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy.yml:10: update your workflow using https://app.stepsecurity.io/secureworkflow/pwall567/log-front-kotlin/deploy.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/deploy.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/pwall567/log-front-kotlin/deploy.yml/main?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/deploy.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/deploy.yml:7"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-22T20:16:35.946Z","repository_id":57739832,"created_at":"2025-08-22T20:16:35.947Z","updated_at":"2025-08-22T20:16:35.947Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29679049,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T09:33:50.764Z","status":"ssl_error","status_checked_at":"2026-02-21T09:33:19.949Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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","logging"],"created_at":"2024-12-11T04:12:14.623Z","updated_at":"2026-02-21T11:00:58.419Z","avatar_url":"https://github.com/pwall567.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# log-front-kotlin\n\n[![Build Status](https://github.com/pwall567/log-front-kotlin/actions/workflows/build.yml/badge.svg)](https://github.com/pwall567/log-front-kotlin/actions/workflows/build.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Kotlin](https://img.shields.io/static/v1?label=Kotlin\u0026message=v2.0.21\u0026color=7f52ff\u0026logo=kotlin\u0026logoColor=7f52ff)](https://github.com/JetBrains/kotlin/releases/tag/v2.0.21)\n[![Maven Central](https://img.shields.io/maven-central/v/io.kstuff/log-front-kotlin?label=Maven%20Central)](https://central.sonatype.com/artifact/io.jstuff/log-front-kotlin)\n\nLogging interface in Kotlin.\n\nThis library provides access to the [`log-front`](https://github.com/pwall567/log-front) Java library in a Kotlin\nidiomatic manner.\n\n## Background\n\nThis is a Kotlin implementation of the [`log-front-api`](https://github.com/pwall567/log-front-api) (see that library\nfor a description of the motivation behind its creation).\n\nThe default behaviour is to use reflection to find one of the following underlying logging mechanisms:\n\n1. Gradle logging (for use in Gradle plugins).\n2. `slf4j`.\n3. Apache Commons Logging.\n4. Java Logging (`java.util.logging`)\n\nIf none of these is found, the library will create a `FormattingLogger`, which logs to `stdout` in a simple format.\n\nIf it is necessary to fit in with an existing logging framework other than `slf4j` or Java Logging, the API is designed\nto be simple to implement so that a thin interfacing layer can be created, accepting this API and invoking the required\ntarget logging mechanism.\n\nNew from version 4.0 onward is multi-line logging.\nThis is a security measure \u0026ndash; if an attacker is aware that input text is included in a log message, they may try to\ncreate fake log events by including a newline followed by what looks like a log message prefix in their text.\nThe library now splits messages containing line terminators into separate invocations of the underlying log output\nfunction, so each line of the log has the correct prefix.\n\n(The name `log-front` is a play on the name of the popular [Logback](https://logback.qos.ch/) project;\nalso on the fact that it is a front-end \u0026ndash; a fa\u0026ccedil;ade \u0026ndash; to an underlying logging system.)\n\n## Usage\n\n### Quick Start\n\nTo instantiate a `Logger` for the current class, regardless of whether the call is made from within the class or its\ncompanion object:\n```kotlin\n    val log = getLogger()   // requires: import io.kstuff.log.getLogger\n```\n\nTo output a log \"INFO\"-level message:\n```kotlin\n        log.info { \"message content\" }\n```\nBecause the lambda is evaluated only if logging is enabled for this logger and this level, complex substitutions may be\nused in the message with no loss of efficiency.\n\nThere are similar functions `trace`, `debug`, `warn` and `error`, and an additional version of `error` that takes a\n`Throwable` as the first parameter:\n```kotlin\n        log.error(exception) { \"Something went wrong!\" }\n```\n\n### `Level`\n\nA fundamental concept in logging is the `Level`.\nThis library uses five logging levels:\n\n- `TRACE`\n- `DEBUG`\n- `INFO`\n- `WARN`\n- `ERROR`\n\nFor information on how these levels map to the levels used by the underlying logging system, see the documentation\nrelating to the implementation classes.\n\n### `getLogger()`\n\nThere are several top-level functions to acquire a `Logger` instance, but in practice, most users will just use the\nfirst (and simplest) one:\n\n- `getLogger()`\n- `getLogger(level: Level)`\n- `getLogger(clock: Clock)`\n- `getLogger(level: Level, clock: Clock)`\n- `getLogger(name: String)`\n- `getLogger(name: String, level: Level)`\n- `getLogger(name: String, clock: Clock)`\n- `getLogger(name: String, level: Level, clock: Clock)`\n- `getLogger(javaClass: Class\u003c*\u003e)`\n- `getLogger(javaClass: Class\u003c*\u003e, level: Level)`\n- `getLogger(javaClass: Class\u003c*\u003e, clock: Clock)`\n- `getLogger(javaClass: Class\u003c*\u003e, level: Level, clock: Clock)`\n- `getLogger(kClass: KClass\u003c*\u003e)`\n- `getLogger(kClass: KClass\u003c*\u003e, level: Level)`\n- `getLogger(kClass: KClass\u003c*\u003e, clock: Clock)`\n- `getLogger(kClass: KClass\u003c*\u003e, level: Level, clock: Clock)`\n\nWhen no logger name is provided, the name will be determined automatically from the calling class.\nOtherwise, the name may be specified as a `String`, or as a Java `Class` or a Kotlin `KClass`, in which case the\nfully-qualified class name will be used.\nThe name must contain only ASCII characters (`0x00..0x7E`).\n\nThe `level` and `clock` (`java.time.Clock`), if specified, will be provided to the `LoggerFactory` to be used in the\ninstantiation of the `Logger`, although not all implementations will make use of these parameters.\n\n### `Logger`\n\nThe `Logger` has three properties:\n\n| Name    | Type     | Mutable |\n|---------|----------|---------|\n| `name`  | `String` | No      |\n| `level` | `Level`  | Yes     |\n| `clock` | `Clock`  | Yes     |\n\nIn many cases, the underlying implementation will ignore changes to the `level` or `clock`, so changing these properties\nshould usually be limited to test functions.\n\nThe functions to output a log message should be familiar to most:\n\n- `trace(message)`\n- `trace { messageFunction() }`\n- `debug(message)`\n- `debug { messageFunction() }`\n- `info(message)`\n- `info { messageFunction() }`\n- `warn(message)`\n- `warn { messageFunction() }`\n- `error(message)`\n- `error { messageFunction() }`\n\nThe `Logger` will check whether the level implied by the name is enabled for that instance, and will output the log\nmessage only if it is.\nImportantly, where the form of the function which takes a lambda is used, the lambda will not be executed if the\n`Logger` is not enabled for that level.\n\nThere are also functions that take a variable `Level`:\n\n- `log(level, message)`\n- `log(level) { messageFunction() }`\n\nAnd there are versions of the `error` function that take a `Throwable` (usually an `Exception`) to indicate the cause of\nthe error:\n\n- `error(cause, message)`\n- `error(cause) { messageFunction() }`\n\nEach of these logging functions has an additional form that takes a time (specified as an `Instant`) as the first\nparameter.\nFor example:\n```kotlin\n    log.info(time) { \"New account created: $accountId\" }\n```\nThis may be used to ensure that the time in the log matches some other event, such as the creation time of a database\nrecord.\nIt is also used by the [`CoLogger`](#cologger) class to ensure that log messages output asynchronously carry the\ntimestamp of their creation, not of their eventual output.\nIt should be noted, however, that several of the more popular logging frameworks (including `slf4j` and ACL) do not\nsupport the ability to specify the time of a log event, so the feature is only of value when `Logger` classes such as\n`FormattingLogger` are used.\n\nIt is important to note that the `message` (or the result of the `messageFunction()`) is of type `Any?`; the text that\nwill appear in the log will be the `toString()` of the object (the `toString()` of a `String` is itself).\nThis allows for an alternative form of lazy message string creation, since the `toString()` is invoked only after the\nlevel has been checked.\nIt also means that a `LogListener` has access to parts of the object that do not appear in the text.\n\n### `LoggerDelegate`\n\nThe `LoggerDelegate` class from previous versions of the library has been removed; its use has been superseded by the\n`getLogger()` functions.\n\n## Coroutines\n\nLogging is inherently an I/O operation, so logging in coroutines requires special handling.\nThe `CoLogger` class queues log messages to a `Channel`, and starts a coroutine using `Dispatchers.IO` to read from the\n`Channel` and perform the actual log output.\n\n### `CoLogger`\n\nA `CoLogger` contains a reference to an underlying `Logger` which will be used for the eventual output of the messages,\nfollowing queueing as described above.\nImportantly, the timestamp of the message is set when it is first queued, not when it is output.\n\nAll of the variations of the `trace`, `debug`, `info`, `warn` and `error` functions are available, but in this case they\nare implemented as suspend functions (among other things, this means that `CoLogger` does not implement the `Logger`\ninterface).\n\n### `getCoLogger()`\n\nThere are several variations on the `getCoLogger()` function to get a `CoLogger` from the default `LoggerFactory`.\nThese largely match the [`getLogger()`](#getlogger) functions described above:\n\n- `getCoLogger()`\n- `getCoLogger(level: Level)`\n- `getCoLogger(clock: Clock)`\n- `getCoLogger(level: Level, clock: Clock)`\n- `getCoLogger(name: String)`\n- `getCoLogger(name: String, level: Level)`\n- `getCoLogger(name: String, clock: Clock)`\n- `getCoLogger(name: String, level: Level, clock: Clock)`\n- `getCoLogger(javaClass: Class\u003c*\u003e)`\n- `getCoLogger(javaClass: Class\u003c*\u003e, level: Level)`\n- `getCoLogger(javaClass: Class\u003c*\u003e, clock: Clock)`\n- `getCoLogger(javaClass: Class\u003c*\u003e, level: Level, clock: Clock)`\n- `getCoLogger(kClass: KClass\u003c*\u003e)`\n- `getCoLogger(kClass: KClass\u003c*\u003e, level: Level)`\n- `getCoLogger(kClass: KClass\u003c*\u003e, clock: Clock)`\n- `getCoLogger(kClass: KClass\u003c*\u003e, level: Level, clock: Clock)`\n\nThere are also extension function on `LoggerFactory` and `Logger` to get `CoLogger` instances using a `Logger` from that\n`LoggerFactory`:\n\n- `LoggerFactory.getCoLogger(level: Level = defaultLevel, clock: Clock = defaultClock)`\n- `LoggerFactory.getCoLogger(name: String, level: Level = defaultLevel, clock: Clock = defaultClock)`\n- `LoggerFactory.getCoLogger(javaClass: Class\u003c*\u003e, level: Level = defaultLevel, clock: Clock = defaultClock)`\n- `LoggerFactory.getCoLogger(kClass: KClass\u003c*\u003e, level: Level = defaultLevel, clock: Clock = defaultClock)`\n\nThe `defaultLevel` and `defaultClock` values in the function signatures refer to the default values for the\n`LoggerFactory`.\n\nAnd lastly, a `CoLogger` may be created for an existing `Logger`:\n\n- `Logger.getCoLogger()`\n\n### `close()`\n\n`CoLogger` implements the `AutoCloseable` interface, and it is important that the `close()` function is called to ensure\nthat any log messages queued in the channel are output before the process ends.\nThis may be achieved by using the `use` function as follows:\n```kotlin\n    getCoLogger().use { logger -\u003e\n        // output to \"logger\" as appropriate\n    }\n```\n\n## Debugging\n\n### `LogListener`\n\nThe library provides a built-in mechanism for testing whether log items are output as expected.\nIf an object extending the `LogListener` abstract base class is created, it will be added to a list of listeners and\ncalled for every log event (calling `close()` on the listener will remove it from the list).\nThe library [`log-front-testk`](https://github.com/pwall567/log-front-testk) provides a mechanism for debugging, making\nuse of this interface.\n\nThe `LogListener` class implements the `AutoCloseable` interface, allowing it to be used in a `use { }` block.\nThis ensures that the listener is removed from the list when it is no longer required.\n\nThe mechanism is intended to be completely non-intrusive \u0026ndash; it should be possible to write code that outputs log\nmessages using `Logger` objects obtained from the default `LoggerFactory`, and then to create tests that intercept the\nlog messages and check for correctness.\nWhen the code is run in a production situation with no listeners, the overhead of checking for them will be minuscule.\n\n**Note that the listener mechanism is global (and simplistic) \u0026ndash; it is intended solely for unit testing purposes.**\nOnly log messages output via this library will be presented to the listener; messages output to the underlying logging\nsystem by other means will not be visible.\nAnd because logging systems create their own timestamps, the times on messages captured by this mechanism may differ\nvery slightly from the times generated by the underlying system.\n\n## Combining with Java `log-front`\n\nThis library is based on the Java [`log-front`](https://github.com/pwall567/log-front) library, and in many cases, the\nclasses of this library are declared as a `typealias` onto the equivalent class in the Java library.\nThis means that named parameters are not available for function calls to these classes, but otherwise, there should be\nlittle noticeable effect from this arrangement.\n\nImportantly, in a mixed-language project, a `Logger` declared in one language may be used in the other (using the Java\nclass name) and tests that use `LogListener` will include log events generated in both languages.\n\n## Dependency Specification\n\nThe latest version of the library is 7.0, and it may be obtained from the Maven Central repository.\n\n### Maven\n```xml\n    \u003cdependency\u003e\n      \u003cgroupId\u003eio.kstuff\u003c/groupId\u003e\n      \u003cartifactId\u003elog-front-kotlin\u003c/artifactId\u003e\n      \u003cversion\u003e7.0\u003c/version\u003e\n    \u003c/dependency\u003e\n```\n### Gradle\n```groovy\n    implementation 'io.kstuff:log-front-kotlin:7.0'\n```\n### Gradle (kts)\n```kotlin\n    implementation(\"io.kstuff:log-front-kotlin:7.0\")\n```\n\nPeter Wall\n\n2025-11-11\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpwall567%2Flog-front-kotlin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpwall567%2Flog-front-kotlin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpwall567%2Flog-front-kotlin/lists"}