{"id":36412677,"url":"https://github.com/mflisar/lumberjack","last_synced_at":"2026-06-08T13:01:00.620Z","repository":{"id":64928116,"uuid":"71460539","full_name":"MFlisar/Lumberjack","owner":"MFlisar","description":"Lumberjack - a customisable and extensible file/console logger (with optional Timber support)","archived":false,"fork":false,"pushed_at":"2026-06-06T17:42:01.000Z","size":6263,"stargazers_count":46,"open_issues_count":0,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-06-06T19:16:21.680Z","etag":null,"topics":["android","logging","lumberjack","timber"],"latest_commit_sha":null,"homepage":"https://mflisar.github.io/Lumberjack/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MFlisar.png","metadata":{"files":{"readme":"README.md","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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2016-10-20T12:25:42.000Z","updated_at":"2026-06-06T17:42:05.000Z","dependencies_parsed_at":"2024-04-24T17:32:19.790Z","dependency_job_id":"180bbf8e-50a0-429d-bfd1-8791187d5a5f","html_url":"https://github.com/MFlisar/Lumberjack","commit_stats":null,"previous_names":[],"tags_count":129,"template":false,"template_full_name":null,"purl":"pkg:github/MFlisar/Lumberjack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MFlisar%2FLumberjack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MFlisar%2FLumberjack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MFlisar%2FLumberjack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MFlisar%2FLumberjack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MFlisar","download_url":"https://codeload.github.com/MFlisar/Lumberjack/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MFlisar%2FLumberjack/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34063159,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"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":["android","logging","lumberjack","timber"],"created_at":"2026-01-11T16:54:22.550Z","updated_at":"2026-06-08T13:01:00.609Z","avatar_url":"https://github.com/MFlisar.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Maven Central](https://img.shields.io/maven-central/v/io.github.mflisar.lumberjack/core?style=for-the-badge\u0026color=blue)](https://central.sonatype.com/artifact/io.github.mflisar.lumberjack/core) ![API](https://img.shields.io/badge/api-23%2B-brightgreen.svg?style=for-the-badge) ![Kotlin](https://img.shields.io/github/languages/top/MFlisar/Lumberjack.svg?style=for-the-badge\u0026amp;color=blueviolet) ![Kotlin Multiplatform](https://img.shields.io/badge/Kotlin_Multiplatform-blue?style=for-the-badge\u0026amp;label=Kotlin)\n# Lumberjack\n![Platforms](https://img.shields.io/badge/PLATFORMS-black?style=for-the-badge) ![Android](https://img.shields.io/badge/android-3DDC84?style=for-the-badge) ![iOS](https://img.shields.io/badge/ios-A2AAAD?style=for-the-badge) ![Windows](https://img.shields.io/badge/windows-5382A1?style=for-the-badge) ![macOS](https://img.shields.io/badge/macos-B0B0B0?style=for-the-badge) ![WebAssembly](https://img.shields.io/badge/wasm-624DE7?style=for-the-badge)\n\nThis library provides following main features:\n\n* simply lazy evalution based logger based on a `L` class for the shortest possible log codes\n* easily extendible with custom loggers\n* offers console and file logger implementations\n* supports log filtering\n* providers a viewer for log files\n\n# Table of Contents\n\n- [Screenshots](#camera-screenshots)\n- [Supported Platforms](#computer-supported-platforms)\n- [Versions](#arrow_right-versions)\n- [Setup](#wrench-setup)\n- [Usage](#rocket-usage)\n- [Modules](#file_folder-modules)\n- [Demo](#sparkles-demo)\n- [More](#information_source-more)\n- [API](#books-api)\n- [Other Libraries](#bulb-other-libraries)\n\n# :camera: Screenshots\n\n### composeviewer\n\n|  |  |  |\n|---|---|---|\n| ![compose-viewer1](documentation/screenshots/composeviewer/compose-viewer1.jpg) | ![compose-viewer2](documentation/screenshots/composeviewer/compose-viewer2.jpg) |\n\n### core\n\n|  |  |  |\n|---|---|---|\n| ![log1](documentation/screenshots/core/log1.png) | ![log2](documentation/screenshots/core/log2.png) |\n\n# :computer: Supported Platforms\n\n| Module | android | iOS | windows | macOS | wasm | Notes |\n|---|---|---|---|---|---|---|\n| core | ✅ | ✅ | ✅ | ✅ | ✅ | the core module of lumberjack |\n| implementation | ✅ | ✅ | ✅ | ✅ | ✅ | the implementation of lumberjack |\n| logger-console | ✅ | ✅ | ✅ | ✅ | ✅ | a console logger for lumberjack |\n| logger-file | ✅ | ✅ | ✅ | ✅ | ❌ | a file logger for lumberjack |\n| extension-composeviewer | ✅ | ✅ | ✅ | ✅ | ✅ | a compose viewer for lumberjack |\n| extension-feedback | ✅ | ✅ | ❌ | ❌ | ❌ | a feedback module for lumberjack |\n\n# :arrow_right: Versions\n\n| Dependency | Version |\n|---|---|\n| Kotlin | `2.4.0` |\n| Jetbrains Compose | `1.11.1` |\n| Jetbrains Compose Material3 | `1.9.0` |\n\n\u003e :warning: Following experimental annotations are used:\n\u003e - **OptIn**\n\u003e   - `androidx.compose.material3.ExperimentalMaterial3Api` (3x)\n\u003e   - `kotlinx.cinterop.ExperimentalForeignApi` (2x)\n\u003e\n\u003e I try to use as less experimental features as possible, but in this case the ones above are needed!\n\n# :wrench: Setup\n\n\u003cdetails open\u003e\n\n\u003csummary\u003e\u003cb\u003eUsing Version Catalogs\u003c/b\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\nDefine the dependencies inside your **libs.versions.toml** file.\n\n```toml\n[versions]\n\nlumberjack = \"\u003cLATEST-VERSION\u003e\"\n\n[libraries]\n\nlumberjack-core = { module = \"io.github.mflisar.lumberjack:core\", version.ref = \"lumberjack\" }\nlumberjack-implementation = { module = \"io.github.mflisar.lumberjack:implementation\", version.ref = \"lumberjack\" }\nlumberjack-logger-console = { module = \"io.github.mflisar.lumberjack:logger-console\", version.ref = \"lumberjack\" }\nlumberjack-logger-file = { module = \"io.github.mflisar.lumberjack:logger-file\", version.ref = \"lumberjack\" }\nlumberjack-extension-composeviewer = { module = \"io.github.mflisar.lumberjack:extension-composeviewer\", version.ref = \"lumberjack\" }\nlumberjack-extension-feedback = { module = \"io.github.mflisar.lumberjack:extension-feedback\", version.ref = \"lumberjack\" }\n```\n\nAnd then use the definitions in your projects **build.gradle.kts** file like following:\n\n```java\nimplementation(libs.lumberjack.core)\nimplementation(libs.lumberjack.implementation)\nimplementation(libs.lumberjack.logger.console)\nimplementation(libs.lumberjack.logger.file)\nimplementation(libs.lumberjack.extension.composeviewer)\nimplementation(libs.lumberjack.extension.feedback)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n\u003csummary\u003e\u003cb\u003eDirect Dependency Notation\u003c/b\u003e\u003c/summary\u003e\n\n\u003cbr\u003e\n\nSimply add the dependencies inside your **build.gradle.kts** file.\n\n```kotlin\nval lumberjack = \"\u003cLATEST-VERSION\u003e\"\n\nimplementation(\"io.github.mflisar.lumberjack:core:${lumberjack}\")\nimplementation(\"io.github.mflisar.lumberjack:implementation:${lumberjack}\")\nimplementation(\"io.github.mflisar.lumberjack:logger-console:${lumberjack}\")\nimplementation(\"io.github.mflisar.lumberjack:logger-file:${lumberjack}\")\nimplementation(\"io.github.mflisar.lumberjack:extension-composeviewer:${lumberjack}\")\nimplementation(\"io.github.mflisar.lumberjack:extension-feedback:${lumberjack}\")\n```\n\n\u003c/details\u003e\n\n# :rocket: Usage\n\n#### 1. Define a `SettingsModel`\n\n```kotlin\n// Depending on the platform:\n//   - common: DataStoreStorage.create(name = \"preferences\")\n//   - jvm: DataStoreStorage.create(folder = File(System.getProperty(\"user.dir\")), name = \"preferences\")\n//   - android/iOS: DataStoreStorage.create(name = \"preferences\")\n//   - ...\nobject Preferences : SettingsModel(DataStoreStorage.create(name = \"preferences\")) {\n\n    // main data types\n    val someString by stringPref(\"value\")\n    val someBool by boolPref(false)\n    val someInt by intPref(123)\n    val someLong by intPref(123L)\n    val someFloat by intPref(123f)\n    val someDouble by intPref(123.0)\n\n    // enum\n    val someEnum by enumPref(Enum.Value1)\n\n    // custom\n    val someCustomClass1 by anyStringPref(TestClass.CONVERTER, TestClass()) // converts TestClass to a string and saves this string\n    val someCustomClass2 by anyIntPref(TestClass.CONVERTER, TestClass())    // converts TestClass to an int and saves this int\n    val someCustomClass3 by anyLongPref(TestClass.CONVERTER, TestClass())   // converts TestClass to a long and saves this long\n\n    // sets\n    val someStringSet by stringSetPref(setOf(\"a\"))\n    val someIntSet by intSetPref(setOf(1))\n    val someLongSet by longSetPref(setOf(1L))\n    val someFloatSet by floatSetPref(setOf(1f))\n    val someDoubleSet by doubleSetPref(setOf(1.0))\n\n    // NULLABLE vs NON NULLABLE\n    val nonNullableString by stringPref()\n    val nullableString by nullableStringPref()\n    val nonNullableInt by intPref()\n    val nullableInt by nullableIntPref()\n    val nonNullableFloat by floatPref()\n    val nullableFloat by nullableFloatPref()\n    val nonNullableDouble by doublePref()\n    val nullableDouble by nullableDoublePref()\n    val nonNullableLong by longPref()\n    val nullableLong by nullableLongPref()\n    val nonNullableBool by boolPref()\n    val nullableBool by nullableBoolPref()\n\n    // custom\n    val someCustomClass4 by nullableAnyStringPref(TestClass.CONVERTER, TestClass())\n    val someCustomClass5 by nullableAnyIntPref(TestClass.CONVERTER, TestClass())\n    val someCustomClass6 by nullableAnyLongPref(TestClass.CONVERTER, TestClass())\n}\n```\n\n#### 2a) Use the `SettingsModel` in plain kotlin (`flows` + `suspending functions`)\n\n```kotlin\n// 1) get a flow\nval flow = Preferences.someString.flow\n\n// 2) read/update values by suspend functions\nscope.launch(Dispatchers.IO) {\n    val value = Preferences.someInt.read()\n    Preferences.someInt.update(value + 1)\n}\n```\n\n#### 2b) Use the `SettingsModel` in views (e.g. with `Lifecycle Scope`)\n\n```kotlin\n// 1) simply observe a setting\nPreferences.someString.observe(lifecycleScope) {\n    L.d { \"someString = $it\"}\n}\n\n// 2) direct read (not recommended if not necessary but may be useful in many cases)\n// =\u003e simply returns read() in a blocking way)\nval name = Preferences.someString.value\n\n// 3) observe a setting once\nPreferences.someString.observeOnce(lifecycleScope) {\n    L.d { \"someString = $it\"}\n}\n\n// 4) observe ALL settings\nPreferences.changes.onEach {\n    L.d { \"[ALL SETTINGS OBSERVER] Setting '${it.setting.key}' changed its value to ${it.value}\" }\n}.launchIn(lifecycleScope)\n\n// 5) observe SOME settings\nPreferences.changes\n    .filter {\n        it.setting == Preferences.someString ||\n        it.setting == Preferences.someBool\n    }.onEach {\n        L.d { \"[SOME SETTINGS OBSERVER] Setting '${it.setting.key}' changed its value to ${it.value}\" }\n    }.launchIn(lifecycleScope)\n\n// 6) read multiple settings in a suspending way\nlifecycleScope.launch(Dispatchers.IO) {\n    val someString = Preferences.someString.read()\n    val someBool = Preferences.someBool.read()\n}\n```\n\n#### 2c) Use the `SettingsModel` in `compose`\n\n```kotlin\nval name = Preferences.someString.collectAsState()\nval name = Preferences.someString.collectAsStateWithLifecycle()\n\n// simply use the state inside your composables, the state will change whenever the setting behind it will change\n\nval name = Preferences.someString.asMutableState()\n\n// simple use and even update the state now\n\n```\n\n# :file_folder: Modules\n\n- [Compose Viewer](documentation/Modules/Compose%20Viewer.md)\n- [Feedback](documentation/Modules/Feedback.md)\n\n# :sparkles: Demo\n\nA full [demo](/demo) is included inside the demo module, it shows nearly every usage with working examples.\n\n# :information_source: More\n\n- Advanced\n  - [Custom Logger](documentation/Advanced/Custom%20Logger.md)\n- Migration\n  - [Migration to version 10](documentation/Migration/v10.md)\n  - [Migration to version 7](documentation/Migration/v7.md)\n\n# :books: API\n\nCheck out the [API documentation](https://MFlisar.github.io/Lumberjack/).\n\n# :bulb: Other Libraries\n\nYou can find more libraries (all multiplatform) of mine that all do work together nicely [here](https://mflisar.github.io/Libraries/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmflisar%2Flumberjack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmflisar%2Flumberjack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmflisar%2Flumberjack/lists"}