module {
// install CallId...
install(DoubleReceive) {
receiveEntireContent = true
}
install(Logging) {
logRequests = true
logResponses = true
logFullUrl = true
logBody = true
logHeaders = true
filterPath("/api", "/version", "/openapi")
}
}
You can select which calls you want to log with filter
and filterPath
configuration functions.
Logging of request/response payloads requires installation of DoubleReceive
plugin with receiveEntireContent = true
.
Warning
|
Payloads may contain sensitive data not suitable for logging. That’s why this is disabled by default. |
Example
2019-12-03 11:21:27.086 INFO [atcher-worker-5] (Logging.kt:87) : Received request: GET http://localhost:8080/api/entities/102 HTTP/1.1 Host: localhost:8080 Connection: Keep-Alive User-Agent: Apache-HttpClient/4.5.9 (Java/11.0.4) Accept-Encoding: gzip,deflate 2019-12-03 11:21:27.265 INFO [atcher-worker-5] (Logging.kt:81) : 253 ms - 404 - GET http://localhost:8080/api/entities/102 2019-12-03 11:21:27.269 INFO [atcher-worker-5] (Logging.kt:104) : Sent response: HTTP/1.1 404 Not Found X-Request-ID: dc3d3804-23b5-4b9f-a8d4-714eec6b4c55 Date: Tue, 03 Dec 2019 10:21:27 GMT Server: kotlin-template/0.1-SNAPSHOT Content-Length: 271 Content-Type: application/json; charset=UTF-8 Connection: keep-alive {"status":404,"type":"my.app.exceptions.ItemNotFoundException","title":"Not Found","detail":"Could not find entity id=102","instance":"dc3d3804-23b5-4b9f-a8d4-714eec6b4c55","path":"/api/entities/102","timestamp":"2019-12-03T11:21:27.186445+01:00"}
Plugin is fully open for customizations. You can base on it your own logging plugin, which you are encouraged to do.
For example with logback’s logstash encoder you can enhance your log JSONs like that:
override fun logPerformance(call: ApplicationCall) {
val duration = System.currentTimeMillis() - call.attributes[startTimeKey]
val route = call.attributes.getOrNull(routeKey)?.parent.toString()
val method = call.request.httpMethod.value
val requestURI = if (logFullUrl) call.request.origin.uri else call.request.path()
val requestInfo = mapOf(
"method" to method,
"protocol" to call.request.origin.version,
"url" to call.request.origin.run { "$scheme://$host:$port$requestURI" },
"api" to "$method $route",
"route" to route,
"remoteHost" to call.request.origin.remoteHost,
"contentType" to call.request.contentType().toString(),
"contentLength" to call.request.headers[HttpHeaders.ContentLength]?.toInt()
)
val responseInfo = mapOf(
"status" to call.response.status()?.value,
"contentType" to call.response.headers[HttpHeaders.ContentType],
"contentLength" to call.response.headers[HttpHeaders.ContentLength]?.toInt()
)
val additionalInfo = mapOf(
"request" to requestInfo,
"response" to responseInfo
)
log.info("{} ms - {} - {} {}", value("duration", duration), responseInfo["status"], method, requestInfo["url"], appendEntries(additionalInfo))
}
{
"@timestamp": "2019-11-28T13:24:47.832+01:00",
"@version": "1",
"message": "3 ms - 200 - GET http://localhost:8080/api/entities",
"logger_name": "koriit.kotlin.app.Logging",
"thread_name": "worker-4",
"level": "INFO",
"level_value": 20000,
"correlationId": "db4f0ccb-0ba8-45f8-a21b-6adb83c6bd86",
"duration": 3,
"request": {
"method": "GET",
"protocol": "HTTP/1.1",
"url": "http://localhost:8080/api/entities",
"api": "GET /api/entities",
"route": "/api/entities",
"remoteHost": "unknown",
"contentType": "*/*",
"contentLength": null
},
"response": {
"status": 200,
"contentType": "application/json; charset=UTF-8",
"contentLength": 5606
}
}