katokay
06/17/2018, 12:58 AMfun main(args: Array<String>) {
val server = embeddedServer(Netty, port = 9000) {
intercept(ApplicationCallPipeline.Infrastructure){
val requestId = UUID.randomUUID()
MDC.putCloseable("req.Id", requestId.toString()).use {
<http://this.context.application.log.info|this.context.application.log.info>("Interceptor")
proceed()
}
}
routing {
get("/") {
<http://call.application.log.info|call.application.log.info>("Inside /")
call.respondText("Hello World!", ContentType.Text.Plain)
}
}
}
server.start(wait = true)
}
I tested using wrk -t6 -c200 -d15s <http://localhost:9000/>
just to get a little bit of concurrency.
Added context:
https://github.com/Kotlin/kotlinx.coroutines/issues/119katokay
06/17/2018, 1:15 AMkatokay
06/17/2018, 1:16 AMDeactivated User
06/17/2018, 12:24 PMMDC.put(key, value)
MDC.remove(key)
Deactivated User
06/17/2018, 12:47 PMval REQ_ID = AttributeKey<String>("REQ_ID")
fun ApplicationCall.checkReqId() {
check(MDC.get("req.Id") == attributes.get(REQ_ID))
}
fun main(args: Array<String>) {
val server = embeddedServer(Netty, port = 9000) {
intercept(ApplicationCallPipeline.Infrastructure) {
val requestId = UUID.randomUUID()
call.attributes.put(REQ_ID, requestId.toString())
MDC_associate("req.Id", requestId.toString()) {
call.checkReqId()
<http://this.context.application.log.info|this.context.application.log.info>("Interceptor[start] ${MDC.get("req.Id")}")
proceed()
call.checkReqId()
<http://this.context.application.log.info|this.context.application.log.info>("Interceptor[end] ${MDC.get("req.Id")}")
}
}
routing {
get("/") {
call.checkReqId()
<http://call.application.log.info|call.application.log.info>("Inside / ${MDC.get("req.Id")}")
call.checkReqId()
call.respondText("Hello World! ${MDC.get("req.Id")}", ContentType.Text.Plain)
call.checkReqId()
}
}
}
server.start(wait = true)
}
suspend fun MDC_associate(key: String, value: String, block: suspend () -> Unit) {
withContext(MDCSetContext(key, value, DefaultDispatcher)) {
block()
}
MDC.remove(key)
}
class MDCSetContext(
private var mdcKey: String,
private var mdcValue: String,
private val dispatcher: CoroutineDispatcher
) : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
dispatcher.interceptContinuation(Wrapper(continuation))
inner class Wrapper<T>(private val continuation: Continuation<T>): Continuation<T> {
private inline fun wrap(block: () -> Unit) {
MDC.put(mdcKey, mdcValue)
block()
}
override val context: CoroutineContext get() = continuation.context
override fun resume(value: T) = wrap { continuation.resume(value) }
override fun resumeWithException(exception: Throwable) = wrap { continuation.resumeWithException(exception) }
}
}
Deactivated User
06/17/2018, 12:47 PMDeactivated User
06/17/2018, 12:48 PMcheckReqId
and associated the requestId to the call, so you can verify that it worksDeactivated User
06/17/2018, 12:57 PMMDC.remove
from my example potentially wouldn’t remove it from some thread locals of the MDC. But at least should work when wrapping the whole routekatokay
06/17/2018, 2:42 PMkatokay
06/17/2018, 2:53 PMDeactivated User
06/17/2018, 2:55 PMDeactivated User
06/17/2018, 3:03 PMfun Route.handleRoot() {
get("/") {
logAttach("req.Id", UUID.randomUUID().toString()) {
logInfo("Inside /")
call.respondText("Hello World!")
}
}
}
val LOG_ATTRIBS = AttributeKey<LinkedHashMap<String, String>>("LOG_ATTRIBS")
val ApplicationCall.logAttribs get() = attributes.computeIfAbsent(LOG_ATTRIBS) { LinkedHashMap() }
inline fun PipelineContext<Unit, ApplicationCall>.logAttach(key: String, value: String, callback: () -> Unit) {
call.logAttribs[key] = value
try {
callback()
} finally {
call.logAttribs.remove(key)
}
}
fun PipelineContext<Unit, ApplicationCall>.logInfo(text: String) {
<http://call.application.log.info|call.application.log.info>(text, call.logAttribs) // Does this logger support a map with attributes?
}
Deactivated User
06/17/2018, 3:07 PMfun Route.handleRoot() {
get("/") {
val log = MyStructuredLogger(application.log)
log.attach("req.Id", UUID.randomUUID().toString()) {
<http://log.info|log.info>("Inside /")
call.respondText("Hello World!")
}
}
}
class MyStructuredLogger(val logger: Logger) {
val attributes = LinkedHashMap<String, String>()
inline fun attach(key: String, value: String, callback: () -> Unit) {
attributes[key] = value
try {
callback()
} finally {
attributes.remove(key)
}
}
fun info(text: String) {
<http://logger.info|logger.info>(text, attributes)
}
}
Deactivated User
06/17/2018, 3:12 PMfun Route.handleRoot() {
get("/") {
log.attach("req.Id", UUID.randomUUID().toString()) {
<http://log.info|log.info>("Inside /")
call.respondText("Hello World!")
}
}
}
val StructuredLoggerAttr = AttributeKey<StructuredLogger>("StructuredLogger")
val PipelineContext<Unit, ApplicationCall>.log get() = this.call.attributes.computeIfAbsent(StructuredLoggerAttr) { StructuredLogger(this.application.log) }
class StructuredLogger(val logger: Logger) {
val attributes = LinkedHashMap<String, String>()
inline fun attach(key: String, value: String, callback: () -> Unit) {
attributes[key] = value
try {
callback()
} finally {
attributes.remove(key)
}
}
fun info(text: String) {
<http://logger.info|logger.info>(text, attributes)
}
}
katokay
06/17/2018, 3:12 PMDeactivated User
06/17/2018, 3:13 PMDeactivated User
06/17/2018, 3:13 PMDeactivated User
06/17/2018, 3:14 PMDeactivated User
06/17/2018, 3:14 PMDeactivated User
06/17/2018, 3:14 PMkatokay
06/17/2018, 3:14 PMkatokay
06/17/2018, 3:15 PMDeactivated User
06/17/2018, 3:15 PMkatokay
06/17/2018, 3:50 PMimport io.ktor.application.ApplicationCall
import io.ktor.application.application
import io.ktor.application.call
import io.ktor.application.log
import io.ktor.pipeline.PipelineContext
import io.ktor.util.AttributeKey
import net.logstash.logback.marker.Markers.appendEntries
import org.slf4j.Logger
val StructuredLoggerAttr = AttributeKey<StructuredLogger>("StructuredLogger")
val PipelineContext<Unit, ApplicationCall>.log get() = this.call.attributes.computeIfAbsent(StructuredLoggerAttr) { StructuredLogger(this.application.log) }
class StructuredLogger(val logger: Logger) {
val attributes = LinkedHashMap<String, String>()
inline fun attach(key: String, value: String, callback: () -> Unit) {
attributes[key] = value
try {
callback()
} finally {
attributes.remove(key)
}
}
fun info(text: String) {
<http://logger.info|logger.info>(appendEntries(attributes), text)
}
}
Deactivated User
06/17/2018, 5:16 PMcompile 'net.logstash.logback:logstash-logback-encoder:5.1'
,
do you know how to configure it to log the JSON to the stdout to illustrate how does it work?katokay
06/17/2018, 5:23 PM<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder" />
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="INFO"/>
</configuration>
Deactivated User
06/17/2018, 5:24 PMkatokay
06/17/2018, 5:24 PMkatokay
06/17/2018, 5:24 PMkatokay
06/17/2018, 5:24 PMkatokay
06/17/2018, 5:24 PMDeactivated User
06/17/2018, 5:28 PMkatokay
06/17/2018, 5:29 PMDeactivated User
06/17/2018, 5:32 PMkatokay
06/17/2018, 6:25 PMkatokay
06/17/2018, 6:26 PM