From 6d0776a4291cd212c842cd8bd4e6e8feddfa97c0 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Tue, 30 Apr 2024 22:53:47 -0500 Subject: [PATCH 1/2] Chunked log writer that breaks long messages before passing the log on to a wrapped log writer instance --- .../co/touchlab/kermit/ChunkedLogWriter.kt | 54 ++++++++++++ .../touchlab/kermit/ChunkedLogWriterTest.kt | 82 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 kermit-core/src/commonMain/kotlin/co/touchlab/kermit/ChunkedLogWriter.kt create mode 100644 kermit-core/src/commonTest/kotlin/co/touchlab/kermit/ChunkedLogWriterTest.kt diff --git a/kermit-core/src/commonMain/kotlin/co/touchlab/kermit/ChunkedLogWriter.kt b/kermit-core/src/commonMain/kotlin/co/touchlab/kermit/ChunkedLogWriter.kt new file mode 100644 index 00000000..f332680c --- /dev/null +++ b/kermit-core/src/commonMain/kotlin/co/touchlab/kermit/ChunkedLogWriter.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024 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 + +class ChunkedLogWriter( + internal val wrapped: LogWriter, + private val maxMessageLength: Int, + private val minMessageLength: Int +) : LogWriter() { + override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) { + chunkedLog(severity, message, tag, throwable) + } + + private tailrec fun chunkedLog( + severity: Severity, + message: String, + tag: String, + throwable: Throwable? + ) { + if (message.length > maxMessageLength) { + var msgSubstring = message.substring(0, maxMessageLength) + var msgSubstringEndIndex = maxMessageLength + + // Try to find a substring break at a newline char. + msgSubstring.lastIndexOf('\n').let { lastIndex -> + if (lastIndex >= minMessageLength) { + msgSubstring = msgSubstring.substring(0, lastIndex) + // skip over new line char + msgSubstringEndIndex = lastIndex + 1 + } + } + + // Log the substring. + wrapped.log(severity, msgSubstring, tag, throwable) + + // Recursively log the remainder. + chunkedLog(severity, message.substring(msgSubstringEndIndex), tag, throwable) + } else { + wrapped.log(severity, message, tag, throwable) + } + } +} + +fun LogWriter.chunked(maxMessageLength: Int = 4000, minMessageLength: Int = 3000): LogWriter = + ChunkedLogWriter(this, maxMessageLength, minMessageLength) + diff --git a/kermit-core/src/commonTest/kotlin/co/touchlab/kermit/ChunkedLogWriterTest.kt b/kermit-core/src/commonTest/kotlin/co/touchlab/kermit/ChunkedLogWriterTest.kt new file mode 100644 index 00000000..c5c711a9 --- /dev/null +++ b/kermit-core/src/commonTest/kotlin/co/touchlab/kermit/ChunkedLogWriterTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024 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 + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertSame + +@OptIn(ExperimentalKermitApi::class) +class ChunkedLogWriterTest { + private val testLogWriter = TestLogWriter(Severity.Warn) + private val testObject = testLogWriter.chunked(12, 8) as ChunkedLogWriter + + @Test + fun returnsWrappedWriter() { + assertSame(testLogWriter, testObject.wrapped) + } + + @Test + fun nonChunkedOutput() { + testObject.log(Severity.Error, "test", "my-tag") + + assertEquals(1, testLogWriter.logs.size) + + with(testLogWriter.logs[0]) { + assertEquals(Severity.Error, severity) + assertEquals("my-tag", tag) + assertEquals("test", message) + assertEquals(null, throwable) + } + } + + @Test + fun twoChunksNoLinebreak() { + testObject.log(Severity.Error, "test test test test", "my-tag") + + assertEquals(2, testLogWriter.logs.size) + + with(testLogWriter.logs[0]) { + assertEquals(Severity.Error, severity) + assertEquals("my-tag", tag) + assertEquals("test test te", message) + assertEquals(null, throwable) + } + + with(testLogWriter.logs[1]) { + assertEquals(Severity.Error, severity) + assertEquals("my-tag", tag) + assertEquals("st test", message) + assertEquals(null, throwable) + } + } + + @Test + fun twoChunksWithLinebreak() { + testObject.log(Severity.Error, "test test\ntest test", "my-tag") + + assertEquals(2, testLogWriter.logs.size) + + with(testLogWriter.logs[0]) { + assertEquals(Severity.Error, severity) + assertEquals("my-tag", tag) + assertEquals("test test", message) + assertEquals(null, throwable) + } + + with(testLogWriter.logs[1]) { + assertEquals(Severity.Error, severity) + assertEquals("my-tag", tag) + assertEquals("test test", message) + assertEquals(null, throwable) + } + } +} \ No newline at end of file From 47cb3efb5e170db55532032c1986cecd3ea9a5be Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sun, 19 May 2024 09:33:03 -0500 Subject: [PATCH 2/2] Updated the API dump --- kermit-core/api/android/kermit-core.api | 10 ++++++++++ kermit-core/api/jvm/kermit-core.api | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/kermit-core/api/android/kermit-core.api b/kermit-core/api/android/kermit-core.api index 47b15e44..43b08f56 100644 --- a/kermit-core/api/android/kermit-core.api +++ b/kermit-core/api/android/kermit-core.api @@ -7,6 +7,16 @@ public class co/touchlab/kermit/BaseLogger { public final fun processLog (Lco/touchlab/kermit/Severity;Ljava/lang/String;Ljava/lang/Throwable;Ljava/lang/String;)V } +public final class co/touchlab/kermit/ChunkedLogWriter : co/touchlab/kermit/LogWriter { + public fun (Lco/touchlab/kermit/LogWriter;II)V + public fun log (Lco/touchlab/kermit/Severity;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V +} + +public final class co/touchlab/kermit/ChunkedLogWriterKt { + public static final fun chunked (Lco/touchlab/kermit/LogWriter;II)Lco/touchlab/kermit/LogWriter; + public static synthetic fun chunked$default (Lco/touchlab/kermit/LogWriter;IIILjava/lang/Object;)Lco/touchlab/kermit/LogWriter; +} + public class co/touchlab/kermit/CommonWriter : co/touchlab/kermit/LogWriter { public fun ()V public fun (Lco/touchlab/kermit/MessageStringFormatter;)V diff --git a/kermit-core/api/jvm/kermit-core.api b/kermit-core/api/jvm/kermit-core.api index f8cba814..ccf33603 100644 --- a/kermit-core/api/jvm/kermit-core.api +++ b/kermit-core/api/jvm/kermit-core.api @@ -7,6 +7,16 @@ public class co/touchlab/kermit/BaseLogger { public final fun processLog (Lco/touchlab/kermit/Severity;Ljava/lang/String;Ljava/lang/Throwable;Ljava/lang/String;)V } +public final class co/touchlab/kermit/ChunkedLogWriter : co/touchlab/kermit/LogWriter { + public fun (Lco/touchlab/kermit/LogWriter;II)V + public fun log (Lco/touchlab/kermit/Severity;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V +} + +public final class co/touchlab/kermit/ChunkedLogWriterKt { + public static final fun chunked (Lco/touchlab/kermit/LogWriter;II)Lco/touchlab/kermit/LogWriter; + public static synthetic fun chunked$default (Lco/touchlab/kermit/LogWriter;IIILjava/lang/Object;)Lco/touchlab/kermit/LogWriter; +} + public class co/touchlab/kermit/CommonWriter : co/touchlab/kermit/LogWriter { public fun ()V public fun (Lco/touchlab/kermit/MessageStringFormatter;)V