-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor and Json log formatter tests
- Loading branch information
Showing
12 changed files
with
195 additions
and
76 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
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
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 |
---|---|---|
@@ -1,29 +1,43 @@ | ||
plugins { | ||
alias(libs.plugins.kotlin.multiplatform) | ||
alias(libs.plugins.kotlin.serialization) | ||
alias(libs.plugins.mokkery) | ||
alias(libs.plugins.allopen) | ||
} | ||
|
||
kotlin { | ||
val isArm64 = System.getProperty("os.arch") == "aarch64" | ||
val nativeTarget = if (isArm64) linuxArm64() else linuxX64() | ||
val hostOs = System.getProperty("os.name") | ||
val isMingwX64 = hostOs.startsWith("Windows") | ||
val nativeTarget = when { | ||
hostOs == "Mac OS X" -> if (isArm64) macosArm64() else macosX64() | ||
hostOs == "Linux" -> if (isArm64) linuxArm64() else linuxX64() | ||
isMingwX64 -> mingwX64("native") | ||
else -> throw GradleException("Host OS is not supported in Kotlin/Native.") | ||
} | ||
|
||
sourceSets { | ||
commonMain.dependencies { | ||
implementation(libs.ktor.client.core) | ||
implementation(libs.kotlin.serialization.json) | ||
implementation(libs.kotlin.io.core) | ||
implementation(libs.kotlin.date.time) | ||
//implementation(libs.ktor.client.cio) | ||
implementation(libs.ktor.client.curl) | ||
implementation(libs.ktor.client.logging) | ||
implementation(libs.ktor.content.negotiation) | ||
implementation(libs.ktor.content.json) | ||
} | ||
|
||
nativeMain.dependencies {} | ||
|
||
commonTest.dependencies { | ||
nativeTest.dependencies { | ||
implementation(libs.kotlin.test) | ||
} | ||
} | ||
} | ||
|
||
fun isTestingTask(name: String) = name.endsWith("Test") | ||
val isTesting = gradle.startParameter.taskNames.any(::isTestingTask) | ||
|
||
if (isTesting) allOpen { | ||
annotation("kotlin.Metadata") | ||
} | ||
|
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
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
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
39 changes: 22 additions & 17 deletions
39
lambda-runtime/src/commonMain/kotlin/io/github/trueangle/knative/lambda/runtime/log/Log.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 |
---|---|---|
@@ -1,59 +1,64 @@ | ||
package io.github.trueangle.knative.lambda.runtime.log | ||
|
||
import io.github.trueangle.knative.lambda.runtime.LambdaEnvironment | ||
import io.github.trueangle.knative.lambda.runtime.LambdaRuntime | ||
import io.github.trueangle.knative.lambda.runtime.api.Context | ||
import io.github.trueangle.knative.lambda.runtime.log.Log.write | ||
import io.ktor.util.reflect.TypeInfo | ||
import io.ktor.util.reflect.typeInfo | ||
|
||
internal interface LogWriter { | ||
fun write(level: LogLevel, message: Any?) | ||
} | ||
|
||
internal interface LogFormatter { | ||
fun format(logLevel: LogLevel, message: Any?): Any? | ||
fun onContextAvailable(context: Context) {} | ||
fun <T> format(logLevel: LogLevel, message: T?, messageType: TypeInfo): String? | ||
fun onContextAvailable(context: Context) = Unit | ||
} | ||
|
||
object Log { | ||
@PublishedApi | ||
internal val currentLogLevel = LogLevel.fromEnv() | ||
private val writer = StdoutLogWriter() | ||
private val logFormatter = if (LambdaEnvironment.LAMBDA_LOG_FORMAT == "JSON") { | ||
JsonLogFormatter() | ||
JsonLogFormatter(LambdaRuntime.json) | ||
} else { | ||
TextLogFormatter() | ||
} | ||
|
||
fun trace(message: Any?) { | ||
write(LogLevel.TRACE, message) | ||
inline fun <reified T> trace(message: T?) { | ||
write(LogLevel.TRACE, message, typeInfo<T>()) | ||
} | ||
|
||
fun debug(message: Any?) { | ||
write(LogLevel.DEBUG, message) | ||
inline fun <reified T> debug(message: T?) { | ||
write(LogLevel.DEBUG, message, typeInfo<T>()) | ||
} | ||
|
||
fun info(message: Any?) { | ||
write(LogLevel.INFO, message) | ||
inline fun <reified T> info(message: T?) { | ||
write(LogLevel.INFO, message, typeInfo<T>()) | ||
} | ||
|
||
fun warn(message: Any?) { | ||
write(LogLevel.WARN, message) | ||
inline fun <reified T> warn(message: T?) { | ||
write(LogLevel.WARN, message, typeInfo<T>()) | ||
} | ||
|
||
fun error(message: Any?) { | ||
write(LogLevel.ERROR, message) | ||
inline fun <reified T> error(message: T?) { | ||
write(LogLevel.ERROR, message, typeInfo<T>()) | ||
} | ||
|
||
fun fatal(message: Any?) { | ||
write(LogLevel.FATAL, message) | ||
inline fun <reified T> fatal(message: T?) { | ||
write(LogLevel.FATAL, message, typeInfo<T>()) | ||
} | ||
|
||
@PublishedApi | ||
internal fun setContext(context: Context) { | ||
logFormatter.onContextAvailable(context) | ||
} | ||
|
||
private fun write(level: LogLevel, message: Any?) { | ||
@PublishedApi | ||
internal fun write(level: LogLevel, message: Any?, typeInfo: TypeInfo) { | ||
if (level >= currentLogLevel) { | ||
writer.write(level, logFormatter.format(level, message)) | ||
writer.write(level, logFormatter.format(level, message, typeInfo)) | ||
} | ||
} | ||
} |
4 changes: 3 additions & 1 deletion
4
.../src/commonMain/kotlin/io/github/trueangle/knative/lambda/runtime/log/TextLogFormatter.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
89 changes: 89 additions & 0 deletions
89
.../src/nativeTest/kotlin/io/github/trueangle/knative/lambda/runtime/JsonLogFormatterTest.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,89 @@ | ||
package io.github.trueangle.knative.lambda.runtime | ||
|
||
import dev.mokkery.answering.returns | ||
import dev.mokkery.every | ||
import dev.mokkery.mock | ||
import io.github.trueangle.knative.lambda.runtime.api.dto.LogMessageDto | ||
import io.github.trueangle.knative.lambda.runtime.log.JsonLogFormatter | ||
import io.github.trueangle.knative.lambda.runtime.log.LogLevel | ||
import io.ktor.util.reflect.typeInfo | ||
import kotlinx.datetime.Clock | ||
import kotlinx.serialization.ExperimentalSerializationApi | ||
import kotlinx.serialization.Serializable | ||
import kotlinx.serialization.encodeToString | ||
import kotlinx.serialization.json.Json | ||
import kotlin.test.Test | ||
import kotlin.test.assertEquals | ||
|
||
class JsonLogFormatterTest { | ||
private val clock = mock<Clock>() | ||
private val requestId = "awsRequestId" | ||
private val timestamp = Clock.System.now() | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
private val formatter = JsonLogFormatter(clock = clock, json = Json { explicitNulls = true }).apply { | ||
onContextAvailable(mockContext(requestId)) | ||
} | ||
|
||
@Test | ||
fun `GIVEN message of object WHEN format THEN json`() { | ||
val message = SampleObject("Hello world") | ||
|
||
every { clock.now() } returns (timestamp) | ||
|
||
val expected = Json.encodeToString( | ||
LogMessageDto( | ||
timestamp = timestamp.toString(), | ||
message = message, | ||
level = LogLevel.INFO, | ||
awsRequestId = requestId | ||
) | ||
) | ||
val actual = formatter.format(LogLevel.INFO, message, typeInfo<SampleObject>()) | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `GIVEN message of primitive WHEN format THEN json`() { | ||
val message = "Hello world" | ||
|
||
every { clock.now() } returns (timestamp) | ||
|
||
val expected = Json.encodeToString( | ||
LogMessageDto( | ||
timestamp = timestamp.toString(), | ||
message = message, | ||
level = LogLevel.INFO, | ||
awsRequestId = requestId | ||
) | ||
) | ||
val actual = formatter.format(LogLevel.INFO, message, typeInfo<String>()) | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Test | ||
fun `GIVEN non-serializable message object WHEN format THEN json`() { | ||
val message = NoSerialObject("Hello world") | ||
|
||
every { clock.now() } returns (timestamp) | ||
|
||
val expected = Json.encodeToString( | ||
LogMessageDto( | ||
timestamp = timestamp.toString(), | ||
message = message.toString(), | ||
level = LogLevel.INFO, | ||
awsRequestId = requestId | ||
) | ||
) | ||
val actual = formatter.format(LogLevel.INFO, message, typeInfo<NoSerialObject>()) | ||
|
||
assertEquals(expected, actual) | ||
} | ||
|
||
@Serializable | ||
private data class SampleObject(val hello: String) | ||
|
||
private data class NoSerialObject(val hello: String) | ||
} |
Oops, something went wrong.