Skip to content

Commit ef33a73

Browse files
committed
Refactor of Log
1 parent b1ca65c commit ef33a73

File tree

7 files changed

+86
-60
lines changed

7 files changed

+86
-60
lines changed

lambda-runtime/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import dev.mokkery.MockMode
2+
13
plugins {
24
alias(libs.plugins.kotlin.multiplatform)
35
alias(libs.plugins.kotlin.serialization)
@@ -43,3 +45,6 @@ if (isTesting) allOpen {
4345
annotation("kotlin.Metadata")
4446
}
4547

48+
mokkery {
49+
defaultMockMode.set(MockMode.autoUnit)
50+
}

lambda-runtime/src/commonMain/kotlin/io/github/trueangle/knative/lambda/runtime/LambdaRuntime.kt

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ import io.github.trueangle.knative.lambda.runtime.handler.LambdaBufferedHandler
77
import io.github.trueangle.knative.lambda.runtime.handler.LambdaHandler
88
import io.github.trueangle.knative.lambda.runtime.handler.LambdaStreamHandler
99
import io.github.trueangle.knative.lambda.runtime.log.KtorLogger
10+
import io.github.trueangle.knative.lambda.runtime.log.LambdaLogger
1011
import io.github.trueangle.knative.lambda.runtime.log.Log
12+
import io.github.trueangle.knative.lambda.runtime.log.debug
13+
import io.github.trueangle.knative.lambda.runtime.log.error
14+
import io.github.trueangle.knative.lambda.runtime.log.fatal
15+
import io.github.trueangle.knative.lambda.runtime.log.info
16+
import io.github.trueangle.knative.lambda.runtime.log.warn
1117
import io.ktor.client.HttpClient
1218
import io.ktor.client.engine.HttpClientEngine
1319
import io.ktor.client.engine.curl.Curl
@@ -33,7 +39,7 @@ object LambdaRuntime {
3339
val curlHttpClient = createHttpClient(Curl.create())
3440
val lambdaClient = LambdaClient(curlHttpClient)
3541

36-
Runner(lambdaClient).run(initHandler)
42+
Runner(client = lambdaClient, log = Log).run(false, initHandler)
3743
}
3844

3945
@PublishedApi
@@ -53,14 +59,15 @@ object LambdaRuntime {
5359
@PublishedApi
5460
internal class Runner(
5561
val client: LambdaClient,
62+
val log: LambdaLogger
5663
) {
57-
suspend inline fun <reified I, reified O> run(crossinline initHandler: () -> LambdaHandler<I, O>) {
64+
suspend inline fun <reified I, reified O> run(singleEventMode: Boolean = false, crossinline initHandler: () -> LambdaHandler<I, O>) {
5865
val handler = try {
59-
Log.info("Initializing Kotlin Native Lambda Runtime")
66+
log.info("Initializing Kotlin Native Lambda Runtime")
6067

6168
initHandler()
6269
} catch (e: Exception) {
63-
Log.fatal(e)
70+
log.fatal(e)
6471

6572
client.reportError(e.asInitError())
6673
exitProcess(1)
@@ -70,55 +77,59 @@ internal class Runner(
7077
val inputTypeInfo = typeInfo<I>()
7178
val outputTypeInfo = typeInfo<O>()
7279

73-
while (true) {
80+
var shouldExit = false
81+
while (!shouldExit) {
7482
try {
75-
Log.info("Runtime is ready for a new event")
83+
log.info("Runtime is ready for a new event")
7684

7785
val (event, context) = client.retrieveNextEvent<I>(inputTypeInfo)
7886

79-
with(Log) {
87+
with(log) {
8088
setContext(context)
8189

8290
debug(event)
8391
debug(context)
92+
info("$handlerName invocation started")
8493
}
8594

86-
Log.info("$handlerName invocation started")
87-
8895
if (handler is LambdaStreamHandler<I, *>) {
8996
val response = streamingResponse { handler.handleRequest(event, it, context) }
9097

91-
Log.info("$handlerName started response streaming")
98+
log.info("$handlerName started response streaming")
9299

93100
client.streamResponse(context, response)
94101
} else {
95102
handler as LambdaBufferedHandler<I, O>
96103
val response = bufferedResponse(context) { handler.handleRequest(event, context) }
97104

98-
Log.info("$handlerName invocation completed")
99-
Log.debug(response)
105+
log.info("$handlerName invocation completed")
106+
log.debug(response)
100107

101108
client.sendResponse(context, response, outputTypeInfo)
102109
}
103110
} catch (e: LambdaRuntimeException) {
104-
Log.error(e)
111+
log.error(e)
105112

106113
client.reportError(e)
107114
} catch (e: LambdaEnvironmentException) {
108115
when (e) {
109116
is NonRecoverableStateException -> {
110-
Log.fatal(e)
117+
log.fatal(e)
111118

112119
exitProcess(1)
113120
}
114121

115-
else -> Log.error(e)
122+
else -> log.error(e)
116123
}
117124
} catch (e: Throwable) {
118-
Log.fatal(e)
125+
log.fatal(e)
119126

120127
exitProcess(1)
121128
}
129+
130+
if (singleEventMode) {
131+
shouldExit = singleEventMode
132+
}
122133
}
123134
}
124135

@@ -127,7 +138,7 @@ internal class Runner(
127138
try {
128139
handler(channel)
129140
} catch (e: Exception) {
130-
Log.warn("Exception occurred on streaming: " + e.message)
141+
log.warn("Exception occurred on streaming: " + e.message)
131142

132143
channel.writeStringUtf8(e.toTrailer())
133144
}

lambda-runtime/src/commonMain/kotlin/io/github/trueangle/knative/lambda/runtime/api/dto/LogMessageDto.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@ package io.github.trueangle.knative.lambda.runtime.api.dto
22

33
import io.github.trueangle.knative.lambda.runtime.log.LogLevel
44
import kotlinx.serialization.Contextual
5-
import kotlinx.serialization.KSerializer
65
import kotlinx.serialization.SerialName
76
import kotlinx.serialization.Serializable
8-
import kotlinx.serialization.encoding.Decoder
9-
import kotlinx.serialization.encoding.Encoder
107

118
@Serializable
129
internal data class LogMessageDto<T>(

lambda-runtime/src/commonMain/kotlin/io/github/trueangle/knative/lambda/runtime/log/Log.kt

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@ internal interface LogFormatter {
1515
fun onContextAvailable(context: Context) = Unit
1616
}
1717

18-
object Log {
19-
@PublishedApi
18+
interface LambdaLogger {
19+
fun <T> log(level: LogLevel, message: T?, typeInfo: TypeInfo)
20+
21+
fun setContext(context: Context)
22+
}
23+
24+
object Log : LambdaLogger {
2025
internal val currentLogLevel = LogLevel.fromEnv()
2126
private val writer = StdoutLogWriter()
2227
private val logFormatter = if (LambdaEnvironment.LAMBDA_LOG_FORMAT.equals("JSON", ignoreCase = true)) {
@@ -25,39 +30,37 @@ object Log {
2530
TextLogFormatter()
2631
}
2732

28-
inline fun <reified T> trace(message: T?) {
29-
write(LogLevel.TRACE, message, typeInfo<T>())
33+
override fun setContext(context: Context) {
34+
logFormatter.onContextAvailable(context)
3035
}
3136

32-
inline fun <reified T> debug(message: T?) {
33-
write(LogLevel.DEBUG, message, typeInfo<T>())
37+
override fun <T> log(level: LogLevel, message: T?, typeInfo: TypeInfo) {
38+
if (level >= currentLogLevel) {
39+
writer.write(level, logFormatter.format(level, message, typeInfo))
40+
}
3441
}
42+
}
3543

36-
inline fun <reified T> info(message: T?) {
37-
write(LogLevel.INFO, message, typeInfo<T>())
38-
}
44+
inline fun <reified T> LambdaLogger.trace(message: T?) {
45+
log(LogLevel.TRACE, message, typeInfo<T>())
46+
}
3947

40-
inline fun <reified T> warn(message: T?) {
41-
write(LogLevel.WARN, message, typeInfo<T>())
42-
}
48+
inline fun <reified T : Any> LambdaLogger.debug(message: T?) {
49+
log(LogLevel.DEBUG, message, typeInfo<T>())
50+
}
4351

44-
inline fun <reified T> error(message: T?) {
45-
write(LogLevel.ERROR, message, typeInfo<T>())
46-
}
52+
inline fun <reified T : Any> LambdaLogger.info(message: T?) {
53+
log(LogLevel.INFO, message, typeInfo<T>())
54+
}
4755

48-
inline fun <reified T> fatal(message: T?) {
49-
write(LogLevel.FATAL, message, typeInfo<T>())
50-
}
56+
inline fun <reified T : Any> LambdaLogger.warn(message: T?) {
57+
log(LogLevel.WARN, message, typeInfo<T>())
58+
}
5159

52-
@PublishedApi
53-
internal fun setContext(context: Context) {
54-
logFormatter.onContextAvailable(context)
55-
}
60+
inline fun <reified T : Any> LambdaLogger.error(message: T?) {
61+
log(LogLevel.ERROR, message, typeInfo<T>())
62+
}
5663

57-
@PublishedApi
58-
internal fun write(level: LogLevel, message: Any?, typeInfo: TypeInfo) {
59-
if (level >= currentLogLevel) {
60-
writer.write(level, logFormatter.format(level, message, typeInfo))
61-
}
62-
}
64+
inline fun <reified T : Any> LambdaLogger.fatal(message: T?) {
65+
log(LogLevel.FATAL, message, typeInfo<T>())
6366
}

lambda-runtime/src/nativeTest/kotlin/io/github/trueangle/knative/lambda/runtime/LambdaRunnerTest.kt

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
11
package io.github.trueangle.knative.lambda.runtime
22

3+
import dev.mokkery.matcher.any
4+
import dev.mokkery.mock
5+
import dev.mokkery.verify
6+
import dev.mokkery.verify.VerifyMode
37
import io.github.trueangle.knative.lambda.runtime.ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_FUNCTION_NAME
48
import io.github.trueangle.knative.lambda.runtime.ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_FUNCTION_VERSION
59
import io.github.trueangle.knative.lambda.runtime.ReservedRuntimeEnvironmentVariables.AWS_LAMBDA_RUNTIME_API
610
import io.github.trueangle.knative.lambda.runtime.api.Context
711
import io.github.trueangle.knative.lambda.runtime.api.LambdaClient
812
import io.github.trueangle.knative.lambda.runtime.handler.LambdaBufferedHandler
13+
import io.github.trueangle.knative.lambda.runtime.log.LambdaLogger
14+
import io.github.trueangle.knative.lambda.runtime.log.LogLevel
915
import io.ktor.client.engine.HttpClientEngine
1016
import io.ktor.client.engine.mock.MockEngine
1117
import io.ktor.client.engine.mock.respond
18+
import io.ktor.client.engine.mock.respondBadRequest
1219
import io.ktor.http.HttpHeaders
1320
import io.ktor.http.HttpStatusCode
14-
import io.ktor.http.Url
1521
import io.ktor.http.headers
1622
import io.ktor.http.headersOf
1723
import io.ktor.utils.io.ByteReadChannel
1824
import kotlinx.cinterop.ExperimentalForeignApi
1925
import kotlinx.cinterop.toKString
20-
import kotlinx.coroutines.launch
2126
import kotlinx.coroutines.test.runTest
2227
import platform.posix.getenv
2328
import platform.posix.setenv
24-
import kotlin.test.BeforeClass
2529
import kotlin.test.BeforeTest
2630
import kotlin.test.Test
31+
import kotlin.test.assertFailsWith
2732

2833
class LambdaRunnerTest {
34+
private val log = mock<LambdaLogger>()
2935

3036
@BeforeTest
3137
fun setup() {
@@ -35,36 +41,42 @@ class LambdaRunnerTest {
3541
@Test
3642
fun `GIVEN string event WHEN LambdaBufferedHandler THEN success invocation`() = runTest {
3743
val handlerResponse = "Response"
38-
val requestId = "156cb537-e2d4-11e8-9b34-d36013741fb9"
44+
val awsRequestId = "156cb537-e2d4-11e8-9b34-d36013741fb9"
3945
val deadline = "1542409706888"
4046

4147
val lambdaRunner = createRunner(MockEngine { request ->
48+
val path = request.url.encodedPath
4249
when {
43-
request.url.encodedPath.contains("invocation/next") -> respond(
50+
path.contains("invocation/next") -> respond(
4451
content = ByteReadChannel("""Hello world"""),
4552
status = HttpStatusCode.OK,
4653
headers = headers {
4754
append(HttpHeaders.ContentType, "application/json")
48-
append("Lambda-Runtime-Aws-Request-Id", requestId)
55+
append("Lambda-Runtime-Aws-Request-Id", awsRequestId)
4956
append("Lambda-Runtime-Deadline-Ms", deadline)
5057
append("Lambda-Runtime-Invoked-Function-Arn", "arn")
5158

5259
}
5360
)
5461

55-
else -> respond(
62+
path.contains("/invocation/${awsRequestId}/response") -> respond(
5663
content = ByteReadChannel("""{"ip":"127.0.0.1"}"""),
5764
status = HttpStatusCode.OK,
5865
headers = headersOf(HttpHeaders.ContentType, "application/json")
5966
)
67+
68+
else -> respondBadRequest()
6069
}
6170
})
6271

6372
val handler = object : LambdaBufferedHandler<String, String> {
6473
override suspend fun handleRequest(input: String, context: Context): String = handlerResponse
6574
}
6675

67-
lambdaRunner.run { handler }
76+
lambdaRunner.run(singleEventMode = true) { handler }
77+
78+
verify(VerifyMode.not) { log.log(LogLevel.ERROR, any<Any>(), any()) }
79+
verify(VerifyMode.not) { log.log(LogLevel.FATAL, any<Any>(), any()) }
6880
}
6981

7082
@OptIn(ExperimentalForeignApi::class)
@@ -84,6 +96,6 @@ class LambdaRunnerTest {
8496

8597
private fun createRunner(mockEngine: HttpClientEngine): Runner {
8698
val lambdaClient = LambdaClient(LambdaRuntime.createHttpClient(mockEngine))
87-
return Runner(lambdaClient)
99+
return Runner(lambdaClient, log)
88100
}
89101
}

sample/src/nativeMain/kotlin/com/github/trueangle/knative/lambda/runtime/sample/handler/ByteBodyLambdaHandler.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import io.github.trueangle.knative.lambda.runtime.api.Context
44
import io.github.trueangle.knative.lambda.runtime.handler.LambdaBufferedHandler
55
import io.github.trueangle.knative.lambda.runtime.handler.LambdaHandler
66
import io.github.trueangle.knative.lambda.runtime.log.Log
7+
import io.github.trueangle.knative.lambda.runtime.log.debug
78
import io.ktor.utils.io.core.toByteArray
89

910
class ByteBodyLambdaHandler : LambdaBufferedHandler<ByteArray, ByteArray> {

sample/src/nativeMain/kotlin/com/github/trueangle/knative/lambda/runtime/sample/handler/ObjectBodyLambdaHandler.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package com.github.trueangle.knative.lambda.runtime.sample.handler
22

33
import io.github.trueangle.knative.lambda.runtime.api.Context
4-
import io.github.trueangle.knative.lambda.runtime.events.apigateway.APIGatewayProxy
54
import io.github.trueangle.knative.lambda.runtime.events.apigateway.APIGatewayV2Request
65
import io.github.trueangle.knative.lambda.runtime.events.apigateway.APIGatewayV2Response
76
import io.github.trueangle.knative.lambda.runtime.handler.LambdaBufferedHandler
8-
import io.github.trueangle.knative.lambda.runtime.handler.LambdaHandler
9-
import io.github.trueangle.knative.lambda.runtime.log.Log
107
import kotlinx.serialization.Serializable
118

129
class ObjectBodyLambdaHandler : LambdaBufferedHandler<APIGatewayV2Request, APIGatewayV2Response> {

0 commit comments

Comments
 (0)