Skip to content

Commit 6d77cad

Browse files
committed
Implement call logging gor quotes-backend
1 parent e8ec9d5 commit 6d77cad

3 files changed

Lines changed: 33 additions & 29 deletions

File tree

quotes-backend/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ dependencies {
3131
// Ktor
3232
implementation("io.ktor:ktor-server-core:$ktorVersion")
3333
implementation("io.ktor:ktor-server-netty:$ktorVersion")
34+
implementation("io.ktor:ktor-server-call-logging:$ktorVersion")
3435
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
3536
implementation("io.ktor:ktor-server-status-pages:$ktorVersion")
3637
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
@@ -47,6 +48,7 @@ dependencies {
4748
exclude(group = "ch.qos.logback") // Use defined Logback version
4849
}
4950
implementation("io.opentelemetry.instrumentation:opentelemetry-logback-mdc-1.0:$opentelemetryVersion")
51+
implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-3.0:$opentelemetryVersion")
5052

5153
// Testing
5254
testImplementation("io.ktor:ktor-server-tests:$ktorVersion")

quotes-backend/src/main/kotlin/io/nais/quotesbackend/Application.kt

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.ktor.server.application.*
66
import io.ktor.server.engine.embeddedServer
77
import io.ktor.server.netty.Netty
88
import io.ktor.server.plugins.BadRequestException
9+
import io.ktor.server.plugins.callloging.CallLogging
910
import io.ktor.server.plugins.contentnegotiation.*
1011
import io.ktor.server.plugins.openapi.*
1112
import io.ktor.server.plugins.statuspages.*
@@ -18,6 +19,7 @@ import kotlin.random.Random
1819
import kotlinx.serialization.Serializable
1920
import kotlinx.serialization.SerializationException
2021
import kotlinx.serialization.json.Json
22+
import org.slf4j.event.Level
2123

2224
var globalErrorRate: Double = 0.1
2325

@@ -30,9 +32,16 @@ fun main() {
3032
fun Application.module() {
3133
log.info("Using global error rate: $globalErrorRate")
3234

35+
install(CallLogging) {
36+
level = Level.INFO
37+
disableDefaultColors()
38+
}
39+
3340
install(StatusPages) {
3441
exception<BadRequestException> { call, cause ->
35-
call.application.log.warn("Bad request for ${call.request.path()}: ${cause.message}")
42+
call.application.environment.log.warn(
43+
"Bad request for ${call.request.path()}: ${cause.message}"
44+
)
3645
call.respond(
3746
HttpStatusCode.BadRequest,
3847
mapOf(
@@ -42,7 +51,7 @@ fun Application.module() {
4251
)
4352
}
4453
exception<SerializationException> { call, cause ->
45-
call.application.log.warn(
54+
call.application.environment.log.warn(
4655
"Request deserialization failed for ${call.request.path()}: ${cause.message}"
4756
)
4857
call.respond(
@@ -111,14 +120,18 @@ fun Application.module() {
111120
routing {
112121
route("/api/quotes") {
113122
get {
114-
if (Random.nextDouble() < globalErrorRate) {
123+
try {
124+
if (Random.nextDouble() < globalErrorRate) {
125+
throw IllegalStateException("Database connection failed")
126+
}
127+
call.respond(quotes.values)
128+
} catch (e: IllegalStateException) {
129+
call.application.environment.log.error("Database error: ${e.message}")
115130
call.respond(
116131
HttpStatusCode.InternalServerError,
117-
"Simulated error for observability testing"
132+
mapOf("error" to "DATABASE_ERROR", "message" to e.message)
118133
)
119-
return@get
120134
}
121-
call.respond(quotes.values)
122135
}
123136

124137
post {
@@ -130,42 +143,29 @@ fun Application.module() {
130143
return@post
131144
}
132145
val quoteRequest = call.receive<Quote>()
133-
application.log.info("Received quote request (id should be null): $quoteRequest")
134-
135-
val maxExistingId =
136-
quotes.keys
137-
.mapNotNull { key ->
138-
try {
139-
key.toInt()
140-
} catch (e: NumberFormatException) {
141-
null
142-
}
143-
}
144-
.maxOrNull()
145-
?: 0
146-
application.log.info("Max existing ID determined: $maxExistingId")
146+
call.application.environment.log.debug("Received quote request: $quoteRequest")
147147

148+
val maxExistingId = quotes.keys.mapNotNull { it.toIntOrNull() }.maxOrNull() ?: 0
148149
val newId = (maxExistingId + 1).toString()
149-
application.log.info("Generated new ID: $newId")
150-
151150
val newQuote = quoteRequest.copy(id = newId)
152-
application.log.info("Created newQuote object to be stored and responded: $newQuote")
153151

154-
if (quotes.containsKey(newId)) {
155-
application.log.warn("Warning: Overwriting existing quote with ID $newId")
156-
}
157152
quotes[newId] = newQuote
158153

159-
application.log.info("Responding with: $newQuote")
154+
call.application.environment.log.info("New quote created with ID: $newId")
160155
call.respond(HttpStatusCode.Created, newQuote)
161156
}
162157
}
163158

164159
get("/api/quotes/{id}") {
165-
if (Random.nextDouble() < globalErrorRate) {
160+
try {
161+
if (Random.nextDouble() < globalErrorRate) {
162+
throw IllegalStateException("Failed to retrieve quote due to database error")
163+
}
164+
} catch (e: IllegalStateException) {
165+
call.application.environment.log.error("Error occurred while fetching quote: ${e.message}")
166166
call.respond(
167167
HttpStatusCode.InternalServerError,
168-
"Simulated error for observability testing"
168+
mapOf("error" to "DATABASE_ERROR", "message" to e.message)
169169
)
170170
return@get
171171
}

quotes-backend/src/main/resources/logback.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@
1313
<root level="INFO">
1414
<appender-ref ref="OTEL" />
1515
</root>
16+
17+
<logger name="io.netty" level="INFO"/>
1618
</configuration>

0 commit comments

Comments
 (0)