-
-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Kotlin/Wasm (JS browser) support (#388)
Partially solves #306. No Node.js support for now, as it requires a canary Node.js version (21.0.0-v8-canary202309143a48826a08 or newer). No WASI support also. Considering the raw state of Kotlin WASI, this currently seems too hard to implement. Overall, this is a fairly simple implementation based on the jsMain and jsTest modules. I hope this will be ok as a first solution, just to provide initial support for WASM.
- Loading branch information
1 parent
d960f37
commit b0e682b
Showing
6 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
src/wasmJsMain/kotlin/io/github/oshai/kotlinlogging/ConsoleOutputAppender.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package io.github.oshai.kotlinlogging | ||
|
||
public class ConsoleOutputAppender : FormattingAppender() { | ||
override fun logFormattedMessage(loggingEvent: KLoggingEvent, formattedMessage: Any?) { | ||
when (loggingEvent.level) { | ||
Level.TRACE -> consoleLog(formattedMessage.toString()) | ||
Level.DEBUG -> consoleLog(formattedMessage.toString()) | ||
Level.INFO -> consoleInfo(formattedMessage.toString()) | ||
Level.WARN -> consoleWarn(formattedMessage.toString()) | ||
Level.ERROR -> consoleError(formattedMessage.toString()) | ||
Level.OFF -> Unit | ||
} | ||
} | ||
} | ||
|
||
private fun consoleLog(message: String): Unit = js("console.log(message)") | ||
|
||
private fun consoleInfo(message: String): Unit = js("console.info(message)") | ||
|
||
private fun consoleWarn(message: String): Unit = js("console.warn(message)") | ||
|
||
private fun consoleError(message: String): Unit = js("console.error(message)") |
7 changes: 7 additions & 0 deletions
7
src/wasmJsMain/kotlin/io/github/oshai/kotlinlogging/KotlinLoggingConfiguration.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.github.oshai.kotlinlogging | ||
|
||
public actual object KotlinLoggingConfiguration { | ||
public actual var logLevel: Level = Level.INFO | ||
public actual var formatter: Formatter = DefaultMessageFormatter(includePrefix = true) | ||
public actual var appender: Appender = ConsoleOutputAppender() | ||
} |
18 changes: 18 additions & 0 deletions
18
src/wasmJsMain/kotlin/io/github/oshai/kotlinlogging/internal/KLoggerNameResolver.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package io.github.oshai.kotlinlogging.internal | ||
|
||
internal actual object KLoggerNameResolver { | ||
|
||
internal actual fun name(func: () -> Unit): String { | ||
var found = false | ||
val exception = Exception() | ||
for (line in exception.stackTraceToString().split("\n")) { | ||
if (found) { | ||
return line.substringBefore(".kt").substringAfterLast(".").substringAfterLast("/") | ||
} | ||
if (line.contains("at KotlinLogging")) { | ||
found = true | ||
} | ||
} | ||
return "" | ||
} | ||
} |
180 changes: 180 additions & 0 deletions
180
src/wasmJsTest/kotlin/io/github/oshai/kotlinlogging/ConsoleOutputAppenderTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
package io.github.oshai.kotlinlogging | ||
|
||
import kotlin.test.AfterTest | ||
import kotlin.test.BeforeTest | ||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
|
||
class ConsoleOutputAppenderTest { | ||
private lateinit var defaultLogLevel: Level | ||
private lateinit var defaultFormatter: Formatter | ||
private lateinit var defaultAppender: Appender | ||
|
||
private lateinit var testAppender: ConsoleOutputAppender | ||
|
||
@BeforeTest | ||
fun setup() { | ||
defaultLogLevel = KotlinLoggingConfiguration.logLevel | ||
defaultFormatter = KotlinLoggingConfiguration.formatter | ||
defaultAppender = KotlinLoggingConfiguration.appender | ||
|
||
testAppender = ConsoleOutputAppender() | ||
|
||
KotlinLoggingConfiguration.logLevel = Level.TRACE | ||
KotlinLoggingConfiguration.formatter = TestFormatter() | ||
KotlinLoggingConfiguration.appender = testAppender | ||
|
||
setupConsole() | ||
} | ||
|
||
@AfterTest | ||
fun cleanup() { | ||
KotlinLoggingConfiguration.logLevel = defaultLogLevel | ||
KotlinLoggingConfiguration.formatter = defaultFormatter | ||
KotlinLoggingConfiguration.appender = defaultAppender | ||
|
||
cleanupConsole() | ||
} | ||
|
||
@Test | ||
fun logTraceTest() { | ||
testAppender.log(createTestEvent(Level.TRACE)) | ||
|
||
assertEquals(expected = "testing... TRACE", actual = getTestLog()) | ||
assertEquals("", getTestInfo()) | ||
assertEquals("", getTestWarn()) | ||
assertEquals("", getTestError()) | ||
} | ||
|
||
@Test | ||
fun logDebugTest() { | ||
testAppender.log(createTestEvent(Level.DEBUG)) | ||
|
||
assertEquals(expected = "testing... DEBUG", actual = getTestLog()) | ||
assertEquals("", getTestInfo()) | ||
assertEquals("", getTestWarn()) | ||
assertEquals("", getTestError()) | ||
} | ||
|
||
@Test | ||
fun logInfoTest() { | ||
testAppender.log(createTestEvent(Level.INFO)) | ||
|
||
assertEquals("", getTestLog()) | ||
assertEquals(expected = "testing... INFO", actual = getTestInfo()) | ||
assertEquals("", getTestWarn()) | ||
assertEquals("", getTestError()) | ||
} | ||
|
||
@Test | ||
fun logWarnTest() { | ||
testAppender.log(createTestEvent(Level.WARN)) | ||
|
||
assertEquals("", getTestLog()) | ||
assertEquals("", getTestInfo()) | ||
assertEquals(expected = "testing... WARN", actual = getTestWarn()) | ||
assertEquals("", getTestError()) | ||
} | ||
|
||
@Test | ||
fun logErrorTest() { | ||
testAppender.log(createTestEvent(Level.ERROR)) | ||
|
||
assertEquals("", getTestLog()) | ||
assertEquals("", getTestInfo()) | ||
assertEquals("", getTestWarn()) | ||
assertEquals(expected = "testing... ERROR", actual = getTestError()) | ||
} | ||
|
||
@Test | ||
fun logOffTest() { | ||
testAppender.log(createTestEvent(Level.OFF)) | ||
|
||
assertEquals("", getTestLog()) | ||
assertEquals("", getTestInfo()) | ||
assertEquals("", getTestWarn()) | ||
assertEquals("", getTestError()) | ||
} | ||
|
||
class TestFormatter : Formatter { | ||
override fun formatMessage(loggingEvent: KLoggingEvent): String = | ||
"testing... ${loggingEvent.level}" | ||
} | ||
|
||
private fun createTestEvent(level: Level) = | ||
KLoggingEvent( | ||
level = level, | ||
marker = null, | ||
loggerName = "test logger", | ||
message = "test message", | ||
cause = null, | ||
payload = null, | ||
) | ||
} | ||
|
||
// Access intercepted console.* test messages | ||
|
||
private fun getTestLog(): String = js("""window.__testLog.toString()""") | ||
|
||
private fun getTestInfo(): String = js("""window.__testInfo.toString()""") | ||
|
||
private fun getTestWarn(): String = js("""window.__testWarn.toString()""") | ||
|
||
private fun getTestError(): String = js("""window.__testError.toString()""") | ||
|
||
private fun setupConsole() { | ||
js( | ||
""" | ||
{ | ||
// Save standard console.* | ||
window.__stdLog = console.log; | ||
window.__stdInfo = console.info; | ||
window.__stdWarn = console.warn; | ||
window.__stdError = console.error; | ||
// Define list containers for the intercepted messages | ||
window.__testLog = []; | ||
window.__testInfo = []; | ||
window.__testWarn = []; | ||
window.__testError = []; | ||
// Intercept console.* calls and | ||
// save all intercepted messages to respectful list containers | ||
console.log = function (msg) { | ||
window.__testLog.push(msg); | ||
window.__stdLog.apply(console, arguments); | ||
}; | ||
console.info = function (msg) { | ||
window.__testInfo.push(msg); | ||
window.__stdInfo.apply(console, arguments); | ||
}; | ||
console.warn = function (msg) { | ||
window.__testWarn.push(msg); | ||
window.__stdWarn.apply(console, arguments); | ||
}; | ||
console.error = function (msg) { | ||
window.__testError.push(msg); | ||
window.__stdError.apply(console, arguments); | ||
}; | ||
}""" | ||
) | ||
} | ||
|
||
private fun cleanupConsole() { | ||
js( | ||
""" | ||
{ | ||
// Reset console.* | ||
console.log = window.__stdLog; | ||
console.info = window.__stdInfo; | ||
console.warn = window.__stdWarn; | ||
console.error = window.__stdError; | ||
// Clear list containers | ||
window.__testLog = []; | ||
window.__testInfo = []; | ||
window.__testWarn = []; | ||
window.__testError = []; | ||
}""" | ||
) | ||
} |
50 changes: 50 additions & 0 deletions
50
src/wasmJsTest/kotlin/io/github/oshai/kotlinlogging/SimpleWasmJsTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package io.github.oshai.kotlinlogging | ||
|
||
import kotlin.test.* | ||
|
||
private val logger = KotlinLogging.logger("SimpleWasmJsTest") | ||
|
||
class SimpleWasmJsTest { | ||
private lateinit var appender: SimpleAppender | ||
|
||
@BeforeTest | ||
fun setup() { | ||
appender = createAppender() | ||
KotlinLoggingConfiguration.appender = appender | ||
} | ||
|
||
@AfterTest | ||
fun cleanup() { | ||
KotlinLoggingConfiguration.appender = ConsoleOutputAppender() | ||
KotlinLoggingConfiguration.logLevel = Level.INFO | ||
} | ||
|
||
@Test | ||
fun simpleWasmJsTest() { | ||
assertEquals("SimpleWasmJsTest", logger.name) | ||
logger.info { "info msg" } | ||
assertEquals("INFO: [SimpleWasmJsTest] info msg", appender.lastMessage) | ||
assertEquals("info", appender.lastLevel) | ||
} | ||
|
||
@Test | ||
fun offLevelWasmJsTest() { | ||
KotlinLoggingConfiguration.logLevel = Level.OFF | ||
assertTrue(logger.isLoggingOff()) | ||
logger.error { "error msg" } | ||
assertEquals("NA", appender.lastMessage) | ||
assertEquals("NA", appender.lastLevel) | ||
} | ||
|
||
private fun createAppender(): SimpleAppender = SimpleAppender() | ||
|
||
class SimpleAppender : Appender { | ||
var lastMessage: String = "NA" | ||
var lastLevel: String = "NA" | ||
|
||
override fun log(loggingEvent: KLoggingEvent) { | ||
lastMessage = DefaultMessageFormatter(includePrefix = true).formatMessage(loggingEvent) | ||
lastLevel = loggingEvent.level.name.lowercase() | ||
} | ||
} | ||
} |