From eb2c8579753323a61831b874973783feab615915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B7=A1=E3=81=84=E3=81=AA=E3=81=A4?= <93428659+lightsummer233@users.noreply.github.com> Date: Sun, 19 Jul 2026 17:58:41 +0800 Subject: [PATCH 1/4] Add coil3 extension --- extensions/kermit-coil3/README.md | 114 ++++++++++++++++++ .../kermit-coil3/api/android/kermit-coil3.api | 15 +++ .../kermit-coil3/api/jvm/kermit-coil3.api | 15 +++ extensions/kermit-coil3/build.gradle.kts | 81 +++++++++++++ .../co/touchlab/kermit/coil3/Extensions.kt | 34 ++++++ .../kermit/coil3/KermitCoil3Logger.kt | 67 ++++++++++ gradle/libs.versions.toml | 2 + settings.gradle.kts | 2 + 8 files changed, 330 insertions(+) create mode 100644 extensions/kermit-coil3/README.md create mode 100644 extensions/kermit-coil3/api/android/kermit-coil3.api create mode 100644 extensions/kermit-coil3/api/jvm/kermit-coil3.api create mode 100644 extensions/kermit-coil3/build.gradle.kts create mode 100644 extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt create mode 100644 extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt diff --git a/extensions/kermit-coil3/README.md b/extensions/kermit-coil3/README.md new file mode 100644 index 00000000..54a44c3a --- /dev/null +++ b/extensions/kermit-coil3/README.md @@ -0,0 +1,114 @@ +# Kermit-Coil3 Integration + +The Kermit-Coil3 module lets +an [existing Kermit logger](https://kermit.touchlab.co/docs/configuration/LOGGER_SETUP) receive logs +from Coil3 image loaders. + +Firstly, add the Gradle dependency to your project. + +```kotlin +sourceSets { + commonMain { + dependencies { + implementation("co.touchlab:kermit-coil3:x.y.z") // Add the latest version + } + } +} +``` + +Then add the Kermit logger when you create your Coil image loader. + +```kotlin +val imageLoader = ImageLoader.Builder(context) + .logger(KermitCoil3Logger(logger = KermitLogger)) + .build() +``` + +or create the Kermit logger from a mutable configuration: + +```kotlin +val imageLoader = ImageLoader.Builder(context) + .logger( + KermitCoil3Logger( + config = mutableLoggerConfigInit(CommonWriter(), minSeverity = Severity.Info), + ), + ) + .build() +``` + +or create the Kermit logger from severity with optional logWriters: + +```kotlin +val imageLoader = ImageLoader.Builder(context) + .logger( + KermitCoil3Logger( + severity = Severity.Info, + CommonWriter(), + ), + ) + .build() +``` + +or if you want to use Coil3's `Logger.Level` to control the minimum log level, +you can use the `minLevel` property: + +```kotlin +import coil3.util.Logger as CoilLogger + +val imageLoader = ImageLoader.Builder(context) + .logger( + KermitCoil3Logger( + minLevel = CoilLogger.Level.Info, + CommonWriter(), + ), + ) + .build() +``` + +Like Coil3's default logger, `KermitCoil3Logger` can update the minimum log level at runtime. +You can change the minimum log level by setting the `minLevel` property on the logger instance: +> ⚠️ Note: Runtime `minLevel` updates require the underlying `LoggerConfig` to be a +`MutableLoggerConfig`. If the logger you pass in was created with an immutable config, +> setting `minLevel` at runtime will throw. If you need to change the log level at runtime, +> create your Kermit logger with a mutable config, or use one of the constructors +> that take a `MutableLoggerConfig`, `Severity`, or `CoilLogger.Level` directly. + +```kotlin +import coil3.util.Logger as CoilLogger + +val kermitCoil3Logger = KermitCoil3Logger( + minLevel = CoilLogger.Level.Info, + CommonWriter(), +) + +// Later in your code, you can change the minimum log level: +kermitCoil3Logger.minLevel = CoilLogger.Level.Debug +``` + +Additionally, `KermitCoil3Logger` bridges Coil3's `Logger.Level` and Kermit's `Severity` using two +public extension functions: + +```kotlin +import coil3.util.Logger as CoilLogger + +fun CoilLogger.Level.toKermitSeverity(): Severity +fun Severity.toCoilLoggerLevel(): CoilLogger.Level +``` + +Most levels map one-to-one: + +| Coil3 `Logger.Level` | Kermit `Severity` | +|----------------------|-------------------| +| `Verbose` | `Verbose` | +| `Debug` | `Debug` | +| `Info` | `Info` | +| `Warn` | `Warn` | +| `Error` | `Error` | + +Since Coil3 has no equivalent to Kermit's `Severity.Assert`, `Severity.Assert` is mapped down to +`CoilLogger.Level.Error` when converting from Kermit to Coil3. This conversion is lossy in one +direction only — converting back from `CoilLogger.Level.Error` will always produce +`Severity.Error`, never `Severity.Assert`. + +These extensions are public and can be used independently if you need to convert between the two +logging systems outside of `KermitCoil3Logger`. diff --git a/extensions/kermit-coil3/api/android/kermit-coil3.api b/extensions/kermit-coil3/api/android/kermit-coil3.api new file mode 100644 index 00000000..03513f8c --- /dev/null +++ b/extensions/kermit-coil3/api/android/kermit-coil3.api @@ -0,0 +1,15 @@ +public final class co/touchlab/kermit/coil3/ExtensionsKt { + public static final fun toCoilLoggerLevel (Lco/touchlab/kermit/Severity;)Lcoil3/util/Logger$Level; + public static final fun toKermitSeverity (Lcoil3/util/Logger$Level;)Lco/touchlab/kermit/Severity; +} + +public final class co/touchlab/kermit/coil3/KermitCoil3Logger : coil3/util/Logger { + public fun (Lco/touchlab/kermit/Logger;)V + public fun (Lco/touchlab/kermit/MutableLoggerConfig;)V + public fun (Lco/touchlab/kermit/Severity;[Lco/touchlab/kermit/LogWriter;)V + public fun (Lcoil3/util/Logger$Level;[Lco/touchlab/kermit/LogWriter;)V + public fun getMinLevel ()Lcoil3/util/Logger$Level; + public fun log (Ljava/lang/String;Lcoil3/util/Logger$Level;Ljava/lang/String;Ljava/lang/Throwable;)V + public fun setMinLevel (Lcoil3/util/Logger$Level;)V +} + diff --git a/extensions/kermit-coil3/api/jvm/kermit-coil3.api b/extensions/kermit-coil3/api/jvm/kermit-coil3.api new file mode 100644 index 00000000..03513f8c --- /dev/null +++ b/extensions/kermit-coil3/api/jvm/kermit-coil3.api @@ -0,0 +1,15 @@ +public final class co/touchlab/kermit/coil3/ExtensionsKt { + public static final fun toCoilLoggerLevel (Lco/touchlab/kermit/Severity;)Lcoil3/util/Logger$Level; + public static final fun toKermitSeverity (Lcoil3/util/Logger$Level;)Lco/touchlab/kermit/Severity; +} + +public final class co/touchlab/kermit/coil3/KermitCoil3Logger : coil3/util/Logger { + public fun (Lco/touchlab/kermit/Logger;)V + public fun (Lco/touchlab/kermit/MutableLoggerConfig;)V + public fun (Lco/touchlab/kermit/Severity;[Lco/touchlab/kermit/LogWriter;)V + public fun (Lcoil3/util/Logger$Level;[Lco/touchlab/kermit/LogWriter;)V + public fun getMinLevel ()Lcoil3/util/Logger$Level; + public fun log (Ljava/lang/String;Lcoil3/util/Logger$Level;Ljava/lang/String;Ljava/lang/Throwable;)V + public fun setMinLevel (Lcoil3/util/Logger$Level;)V +} + diff --git a/extensions/kermit-coil3/build.gradle.kts b/extensions/kermit-coil3/build.gradle.kts new file mode 100644 index 00000000..6052b536 --- /dev/null +++ b/extensions/kermit-coil3/build.gradle.kts @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2026 Touchlab + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.multiplatform) + id("kermit-jvm-target") + id("wasm-setup") + id("kermit-publish") +} + +kotlin { + androidTarget { + publishLibraryVariants("release") + } + jvm() + macosArm64() + iosArm64() + iosSimulatorArm64() + js { + browser() + nodejs() + } + + // TODO: These targets are stopped the support by Coil3 in newer version: + macosX64() + iosX64() + + // TODO: These targets are supported by Coil3 in newer version: + // linuxX64() + // linuxArm64() + + // TODO: These targets aren't supported by Coil3 yet: + // tvosArm64() + // tvosSimulatorArm64() + // tvosX64() + // watchosArm32() + // watchosArm64() + // watchosSimulatorArm64() + // watchosDeviceArm64() + // watchosX64() + // mingwX64() + // androidNativeArm32() + // androidNativeArm64() + // androidNativeX86() + // androidNativeX64() + + sourceSets { + commonMain.dependencies { + implementation(libs.coil3.core) + implementation(project(":kermit")) + } + + commonTest.dependencies { + implementation(kotlin("test")) + implementation(project(":kermit-test")) + } + } +} + +android { + namespace = "co.touchlab.kermit.coil3" + compileSdk = libs.versions.compileSdk.get().toInt() + defaultConfig { + minSdk = libs.versions.minSdk.get().toInt() + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } +} diff --git a/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt b/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt new file mode 100644 index 00000000..fdb18b11 --- /dev/null +++ b/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2026 Touchlab + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package co.touchlab.kermit.coil3 + +import co.touchlab.kermit.Severity +import coil3.util.Logger as CoilLogger + +fun CoilLogger.Level.toKermitSeverity(): Severity = when (this) { + CoilLogger.Level.Verbose -> Severity.Verbose + CoilLogger.Level.Debug -> Severity.Debug + CoilLogger.Level.Info -> Severity.Info + CoilLogger.Level.Warn -> Severity.Warn + CoilLogger.Level.Error -> Severity.Error +} + +fun Severity.toCoilLoggerLevel(): CoilLogger.Level = when (this) { + Severity.Verbose -> CoilLogger.Level.Verbose + Severity.Debug -> CoilLogger.Level.Debug + Severity.Info -> CoilLogger.Level.Info + Severity.Warn -> CoilLogger.Level.Warn + Severity.Error -> CoilLogger.Level.Error + Severity.Assert -> CoilLogger.Level.Error // Map Assert to Error for CoilLogger +} diff --git a/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt b/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt new file mode 100644 index 00000000..18ac7aca --- /dev/null +++ b/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2026 Touchlab + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package co.touchlab.kermit.coil3 + +import co.touchlab.kermit.LogWriter +import co.touchlab.kermit.Logger as KermitLogger +import co.touchlab.kermit.MutableLoggerConfig +import co.touchlab.kermit.Severity +import co.touchlab.kermit.mutableLoggerConfigInit +import coil3.util.Logger as CoilLogger + +class KermitCoil3Logger : CoilLogger { + + private val logger: KermitLogger + + constructor( + logger: KermitLogger, + ) : super() { + this.logger = logger + } + + constructor( + config: MutableLoggerConfig, + ) : super() { + logger = KermitLogger(config) + } + + constructor( + severity: Severity, + vararg logWriters: LogWriter, + ) : super() { + logger = KermitLogger( + mutableLoggerConfigInit(*logWriters, minSeverity = severity), + ) + } + + constructor( + minLevel: CoilLogger.Level, + vararg logWriters: LogWriter, + ) : super() { + logger = KermitLogger( + mutableLoggerConfigInit(*logWriters, minSeverity = minLevel.toKermitSeverity()), + ) + } + + override var minLevel: CoilLogger.Level + get() = logger.config.minSeverity.toCoilLoggerLevel() + set(value) { + logger.mutableConfig.minSeverity = value.toKermitSeverity() + } + + override fun log(tag: String, level: CoilLogger.Level, message: String?, throwable: Throwable?) { + if (message == null && throwable == null) return + logger.log(level.toKermitSeverity(), tag, throwable, message.orEmpty()) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index eec89f10..dd8de894 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,6 +36,7 @@ mavenPublish = "0.34.0" dokka = "2.0.0" touchlab-docusaurus-template = "0.1.11" ktor = "3.2.3" +coil3 = "3.3.0" ktlint-gradle = "12.3.0" @@ -56,6 +57,7 @@ bugsnag-android = { module = "com.bugsnag:bugsnag-android", version.ref = "bugsn ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } +coil3-core = { module = "io.coil-kt.coil3:coil-core", version.ref = "coil3" } stately-collections = { module = "co.touchlab:stately-collections", version.ref = "stately" } testhelp = { module = "co.touchlab:testhelp", version.ref = "testhelp" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 6b2ab394..f1e6a8b8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,11 +20,13 @@ include(":kermit-crashlytics") include(":kermit-bugsnag") include(":kermit-koin") include(":kermit-ktor") +include(":kermit-coil3") project(":kermit-crashlytics").projectDir = File("extensions/kermit-crashlytics") project(":kermit-bugsnag").projectDir = File("extensions/kermit-bugsnag") project(":kermit-koin").projectDir = File("extensions/kermit-koin") project(":kermit-ktor").projectDir = File("extensions/kermit-ktor") +project(":kermit-coil3").projectDir = File("extensions/kermit-coil3") // include(":kermit-gradle-plugin") // include(":kermit-ir-plugin") From db90d38e460ddcd54374f933e60d674b65ef9b36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B7=A1=E3=81=84=E3=81=AA=E3=81=A4?= <93428659+lightsummer233@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:01:47 +0800 Subject: [PATCH 2/4] Remove commonTest dependencies --- extensions/kermit-coil3/build.gradle.kts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/extensions/kermit-coil3/build.gradle.kts b/extensions/kermit-coil3/build.gradle.kts index 6052b536..a48128d4 100644 --- a/extensions/kermit-coil3/build.gradle.kts +++ b/extensions/kermit-coil3/build.gradle.kts @@ -60,11 +60,6 @@ kotlin { implementation(libs.coil3.core) implementation(project(":kermit")) } - - commonTest.dependencies { - implementation(kotlin("test")) - implementation(project(":kermit-test")) - } } } From 7675fbe4d18c26e60115922910ee390ef8b1f033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B7=A1=E3=81=84=E3=81=AA=E3=81=A4?= <93428659+lightsummer233@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:07:49 +0800 Subject: [PATCH 3/4] Minor changes --- extensions/kermit-coil3/README.md | 2 +- extensions/kermit-coil3/build.gradle.kts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/kermit-coil3/README.md b/extensions/kermit-coil3/README.md index 54a44c3a..24dc5487 100644 --- a/extensions/kermit-coil3/README.md +++ b/extensions/kermit-coil3/README.md @@ -68,7 +68,7 @@ val imageLoader = ImageLoader.Builder(context) Like Coil3's default logger, `KermitCoil3Logger` can update the minimum log level at runtime. You can change the minimum log level by setting the `minLevel` property on the logger instance: > ⚠️ Note: Runtime `minLevel` updates require the underlying `LoggerConfig` to be a -`MutableLoggerConfig`. If the logger you pass in was created with an immutable config, +> `MutableLoggerConfig`. If the logger you pass in was created with an immutable config, > setting `minLevel` at runtime will throw. If you need to change the log level at runtime, > create your Kermit logger with a mutable config, or use one of the constructors > that take a `MutableLoggerConfig`, `Severity`, or `CoilLogger.Level` directly. diff --git a/extensions/kermit-coil3/build.gradle.kts b/extensions/kermit-coil3/build.gradle.kts index a48128d4..a46d14ce 100644 --- a/extensions/kermit-coil3/build.gradle.kts +++ b/extensions/kermit-coil3/build.gradle.kts @@ -32,11 +32,11 @@ kotlin { nodejs() } - // TODO: These targets are stopped the support by Coil3 in newer version: + // TODO: These targets are stopped the support by Coil3 in newer versions: macosX64() iosX64() - // TODO: These targets are supported by Coil3 in newer version: + // TODO: These targets are supported by Coil3 in newer versions: // linuxX64() // linuxArm64() From 21dfe477f11c4cba7d2ab0f06d70a68471e45354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B7=A1=E3=81=84=E3=81=AA=E3=81=A4?= <93428659+lightsummer233@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:13:27 +0800 Subject: [PATCH 4/4] `Severity.toCoilLoggerLevel` -> `Severity.toCoil3LoggerLevel` --- extensions/kermit-coil3/api/android/kermit-coil3.api | 2 +- extensions/kermit-coil3/api/jvm/kermit-coil3.api | 2 +- .../commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt | 2 +- .../kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/kermit-coil3/api/android/kermit-coil3.api b/extensions/kermit-coil3/api/android/kermit-coil3.api index 03513f8c..bc2434cc 100644 --- a/extensions/kermit-coil3/api/android/kermit-coil3.api +++ b/extensions/kermit-coil3/api/android/kermit-coil3.api @@ -1,5 +1,5 @@ public final class co/touchlab/kermit/coil3/ExtensionsKt { - public static final fun toCoilLoggerLevel (Lco/touchlab/kermit/Severity;)Lcoil3/util/Logger$Level; + public static final fun toCoil3LoggerLevel (Lco/touchlab/kermit/Severity;)Lcoil3/util/Logger$Level; public static final fun toKermitSeverity (Lcoil3/util/Logger$Level;)Lco/touchlab/kermit/Severity; } diff --git a/extensions/kermit-coil3/api/jvm/kermit-coil3.api b/extensions/kermit-coil3/api/jvm/kermit-coil3.api index 03513f8c..bc2434cc 100644 --- a/extensions/kermit-coil3/api/jvm/kermit-coil3.api +++ b/extensions/kermit-coil3/api/jvm/kermit-coil3.api @@ -1,5 +1,5 @@ public final class co/touchlab/kermit/coil3/ExtensionsKt { - public static final fun toCoilLoggerLevel (Lco/touchlab/kermit/Severity;)Lcoil3/util/Logger$Level; + public static final fun toCoil3LoggerLevel (Lco/touchlab/kermit/Severity;)Lcoil3/util/Logger$Level; public static final fun toKermitSeverity (Lcoil3/util/Logger$Level;)Lco/touchlab/kermit/Severity; } diff --git a/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt b/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt index fdb18b11..6616c6b6 100644 --- a/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt +++ b/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/Extensions.kt @@ -24,7 +24,7 @@ fun CoilLogger.Level.toKermitSeverity(): Severity = when (this) { CoilLogger.Level.Error -> Severity.Error } -fun Severity.toCoilLoggerLevel(): CoilLogger.Level = when (this) { +fun Severity.toCoil3LoggerLevel(): CoilLogger.Level = when (this) { Severity.Verbose -> CoilLogger.Level.Verbose Severity.Debug -> CoilLogger.Level.Debug Severity.Info -> CoilLogger.Level.Info diff --git a/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt b/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt index 18ac7aca..49869d93 100644 --- a/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt +++ b/extensions/kermit-coil3/src/commonMain/kotlin/co/touchlab/kermit/coil3/KermitCoil3Logger.kt @@ -55,7 +55,7 @@ class KermitCoil3Logger : CoilLogger { } override var minLevel: CoilLogger.Level - get() = logger.config.minSeverity.toCoilLoggerLevel() + get() = logger.config.minSeverity.toCoil3LoggerLevel() set(value) { logger.mutableConfig.minSeverity = value.toKermitSeverity() }