Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions extensions/kermit-ktor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Kermit-Ktor Integration

The Kermit-Ktor module provides a way to add multiplatform logging to your Ktor client.

Firstly, add the gradle dependency to your project.

```kotlin
sourceSets {
commonMain {
dependencies {
implementation("co.touchlab:kermit-ktor:x.y.z") // Add the latest version
}
}
}
```

Then add the Kermit logger when you create your Ktor client -

```kotlin
val httpClient = HttpClient {
install(Logging) {
logger = KermitKtorLogger(severity = Severity.Info, logger = KermitLogger)
level = LogLevel.INFO
}
}
```

or alternatively

```kotlin
val httpClient = HttpClient {
install(Logging) {
logger = KermitKtorLogger(severity = Severity.Info, config = loggerConfigInit(CommonWriter()), tag = "")
level = LogLevel.INFO
}
}
```
7 changes: 7 additions & 0 deletions extensions/kermit-ktor/api/kermit-ktor.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public final class co/touchlab/kermit/ktor/KermitKtorLogger : io/ktor/client/plugins/logging/Logger {
public fun <init> (Lco/touchlab/kermit/Severity;Lco/touchlab/kermit/Logger;)V
public fun <init> (Lco/touchlab/kermit/Severity;Lco/touchlab/kermit/LoggerConfig;Ljava/lang/String;)V
public synthetic fun <init> (Lco/touchlab/kermit/Severity;Lco/touchlab/kermit/LoggerConfig;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun log (Ljava/lang/String;)V
}

57 changes: 57 additions & 0 deletions extensions/kermit-ktor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2025 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("kermit-publish")
}

kotlin {
androidTarget {
publishAllLibraryVariants()
}
macosX64()
macosArm64()
iosX64()
iosArm64()
iosSimulatorArm64()
tvosArm64()
tvosSimulatorArm64()
tvosX64()
watchosArm32()
watchosArm64()
watchosSimulatorArm64()
watchosDeviceArm64()
watchosX64()

sourceSets {
commonMain.dependencies {
implementation(libs.ktor.logging)
implementation(project(":kermit"))
}
}
}

android {
namespace = "co.touchlab.kermit.ktor"
compileSdk = libs.versions.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2025 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.ktor

import co.touchlab.kermit.Logger as KermitLogger
import co.touchlab.kermit.LoggerConfig
import co.touchlab.kermit.Severity
import io.ktor.client.plugins.logging.Logger as KtorLogger

class KermitKtorLogger(private val severity: Severity, private val logger: KermitLogger) : KtorLogger {

constructor(
severity: Severity,
config: LoggerConfig,
tag: String = "",
) : this(
logger = KermitLogger(config = config, tag = tag),
severity = severity,
)

override fun log(message: String) {
when (severity) {
Severity.Verbose -> logger.v(messageString = message)
Severity.Debug -> logger.d(messageString = message)
Severity.Info -> logger.i(messageString = message)
Severity.Warn -> logger.w(messageString = message)
Severity.Error -> logger.e(messageString = message)
Severity.Assert -> logger.a(messageString = message)
}
}
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ android-junitTest = "1.1.5"
junit = "4.13.2"
dokka = "2.0.0"
touchlab-docusaurus-template = "0.1.11"
ktor = "3.3.1"

ktlint-gradle = "12.3.0"

Expand All @@ -52,6 +53,7 @@ androidx-runner = { module = "androidx.test:runner", version.ref = "android-test
crashkios-crashlytics = { module = "co.touchlab.crashkios:crashlytics", version.ref = "crashkios" }
crashkios-bugsnag = { module = "co.touchlab.crashkios:bugsnag", version.ref = "crashkios" }
bugsnag-android = { module = "com.bugsnag:bugsnag-android", version.ref = "bugsnag" }
ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
stately-collections = { module = "co.touchlab:stately-collections", version.ref = "stately" }
testhelp = { module = "co.touchlab:testhelp", version.ref = "testhelp" }

Expand Down
3 changes: 3 additions & 0 deletions samples/sample/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ package co.touchlab.KermitSample
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import co.touchlab.KermitSample.databinding.FragmentFirstBinding
import co.touchlab.kermit.Logger
import co.touchlab.kermitsample.CommonClient
import co.touchlab.kermitsample.SampleMobile
import kotlinx.coroutines.launch

/**
* A simple [Fragment] subclass as the default destination in the navigation.
Expand All @@ -28,6 +31,11 @@ class FirstFragment : Fragment(R.layout.fragment_first) {
val sample = SampleMobile(context?.filesDir?.path ?: "")
val binding = FragmentFirstBinding.bind(view)
binding.btnClickCount.setOnClickListener { sample.onClickI() }
binding.btnNetwork.setOnClickListener {
lifecycleScope.launch {
CommonClient().testNetworkCall()
}
}
binding.btnException.setOnClickListener { sample.logException() }
}
}
18 changes: 14 additions & 4 deletions samples/sample/app/src/main/res/layout/fragment_first.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,33 @@
android:id="@+id/btn_click_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="244dp"
android:text="@string/click_count"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/btn_network"
/>

<Button
android:id="@+id/btn_network"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Network"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toBottomOf="@id/btn_click_count"
app:layout_constraintBottom_toTopOf="@id/btn_exception"/>

<Button
android:id="@+id/btn_exception"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.501"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_click_count"
app:layout_constraintTop_toBottomOf="@+id/btn_network"
app:layout_constraintVertical_bias="0.107"/>
</androidx.constraintlayout.widget.ConstraintLayout>
5 changes: 5 additions & 0 deletions samples/sample/shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ kotlin {
sourceSets {
commonMain.dependencies {
implementation("co.touchlab:kermit:${KERMIT_VERSION}")
implementation("co.touchlab:kermit-ktor:${KERMIT_VERSION}")

implementation("io.ktor:ktor-client-logging:3.3.1")
implementation("io.ktor:ktor-client-core:3.3.1")
implementation("io.ktor:ktor-client-cio:3.3.1")
}

commonTest.dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2025 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.kermitsample

import co.touchlab.kermit.Logger
import co.touchlab.kermit.Severity
import co.touchlab.kermit.ktor.KermitKtorLogger
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.get

class CommonClient {
private val client = HttpClient(CIO) {
install(Logging) {
logger = KermitKtorLogger(Severity.Info, Logger.withTag("Ktor"))
}
}

suspend fun testNetworkCall() {
client.get("https://dog.ceo/api/breeds/image/random")
}
}
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ include(":kermit-test")
include(":kermit-crashlytics")
include(":kermit-bugsnag")
include(":kermit-koin")
include(":kermit-ktor")

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")

// include(":kermit-gradle-plugin")
// include(":kermit-ir-plugin")
Expand Down
2 changes: 1 addition & 1 deletion website/docs/extensions/KOIN.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Koin Integration

Kermit's Koin integation comes in two parts - a logger implementation that writes
Kermit's Koin integration comes in two parts - a logger implementation that writes
Koin logger output to Kermit, and dependency injection helpers.

## Setup
Expand Down
41 changes: 41 additions & 0 deletions website/docs/extensions/KTOR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Ktor Integration

Kermit's Ktor integration allows you to pass in a Kermit logger instance to your Ktor client.

## Setup

### Add the dependency

```kotlin
commonMain {
dependencies {
implementation("co.touchlab:kermit-ktor:{{LATEST_GITHUB_VERSION}}")
}
}
```

### Add the logger to your client

```kotlin
val httpClient = HttpClient {
install(Logging) {
logger = KermitKtorLogger(severity = Severity.Info, logger = KermitLogger)
level = LogLevel.INFO
}
}
```

or alternatively

```kotlin
val httpClient = HttpClient {
install(Logging) {
logger = KermitKtorLogger(severity = Severity.Info, config = loggerConfigInit(CommonWriter()), tag = "")
level = LogLevel.INFO
}
}
```

This takes in a Kermit logger instance, or alternatively a Kermit logger configuration to create a logger from.
This should allow some flexibility in how you want to set up your logging. You will also need to pass in a severity
level to log to, since the Ktor logger interface does not have levels itself.
6 changes: 5 additions & 1 deletion website/docs/extensions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ Bugsnag breadcrumb writing.

## [Koin](KOIN)

Integration with Koin, for easy logger injection.
Integration with Koin, for easy logger injection.

## [Ktor](KTOR)

Integration with Ktor, for easy client logging.
Loading