ktor icon indicating copy to clipboard operation
ktor copied to clipboard

Cannot log status code with MDC

Open windoze opened this issue 6 years ago • 12 comments

Code snippet:

fun Application.module() {
    install(CallLogging) {
        mdc("status_code") {
            it.response.status()?.toString()
        }
    }
    routing {
        get("/") {
            call.respondText("OK", contentType = ContentType.Text.Plain)
        }
    }
}

The MDC value of "status_code" is always null.

The reason is CallLogging feature evaluates all MDC values before the call gets processed. In CallLogging.kt:

            if (feature.mdcEntries.isNotEmpty()) {
                pipeline.intercept(loggingPhase) {
                    val mdc = feature.setupMdc(call)
                    withContext(MDCSurvivalElement(mdc)) {
                        try {
                            proceed()
                            feature.logSuccess(call)
                        } finally {
                            feature.cleanupMdc()
                        }
                    }
                }
            }

Simply calling feature.setupMdc(call) after proceed() again got the issue fixed, but I'm not sure it's the correct way.

windoze avatar Sep 24 '18 20:09 windoze

Unfortunately it is not going to work properly as we should fill all MDC values before processing to pass them through all coroutines. The second reason is that we don't know how many times and when we should evaluate lambdas. Adding it after proceed() is too ad-hoc.

cy6erGn0m avatar Sep 25 '18 13:09 cy6erGn0m

The other question is what for? Why do you need to add status code to MDC? CallLogging feature logs all status codes at trace log level. I simply don't see any reasons to print status code with every log message

cy6erGn0m avatar Sep 25 '18 13:09 cy6erGn0m

Hi, @cy6erGn0m, I'was trying to make Ktor work with Logstash, which supports log in JSON format, so user can add additional key/values along with normal log messages. I checked out the CallLogging code but didn't find any appropriate places to add K/V except MDC, hence the question. If this is going nowhere, maybe a new LogstashCallLogging feature from scratch is needed, looks like some minor modification though.

windoze avatar Sep 26 '18 03:09 windoze

Why it should be MDC? Can't logstash parse regular log messages?

cy6erGn0m avatar Sep 26 '18 15:09 cy6erGn0m

It's not about MDC actually, what I wanted is to log in JSON format and add some customized properties, with Fluentd/Elasticsearch I can dig into logs easily. Logback has a Logstash encoder (net.logstash.logback:logstash-logback-encoder), which can output logs in JSON format and add properties via log.info("message", kv(someKey, someValue)), but I cannot find anywhere to customize format and content with CallLogging. The encoder also supports MDC, which looks like an alternative but turned out it didn't work as I expected.

Given the situation, I guess I should write another CallLogging feature from scratch.

windoze avatar Sep 26 '18 15:09 windoze

@windoze what about using format?

install(CallLogging) {
    format { "status: ${it.response.status()}" }
}

AndreasVolkmann avatar Sep 29 '19 09:09 AndreasVolkmann

@AndreasVolkmann no it doesn't work, what I was trying to do is to log in JSON format with additional customizable properties/values, but seems Ktor's CallLogging cannot do.

windoze avatar Oct 12 '19 13:10 windoze

I came across this issue whilst debugging why status codes weren't getting added to our Datadog traces. Using MDC tags with explicit values makes log analysis less flaky. They also support a set of common tags for HTTP properties, documented here: https://docs.datadoghq.com/logs/processing/attributes_naming_convention/#common-attributes

lopcode avatar Feb 08 '20 22:02 lopcode

Had a bit of time to look at this. In the end, I've copied the CallLogging feature and added a postEvaluated option to MDC entries. "Post evaluated" entries are set to null to begin with so their lifecycle and cleanup is still handled "for free". Then, the entry is reevaluated after the proceed() call.

mdc("http.status_code", postEvaluated = true) {
    it.response.status()?.value?.toString()
}

Not the prettiest, but works great and hopefully a useful hint to anyone needing this feature.

lopcode avatar Mar 28 '20 22:03 lopcode

@CarrotCodes is that code in a place you could share. I know one of our teams has run into that issue for tracking request timings with status.

Would it be worth a PR?

impatient avatar Jun 19 '20 15:06 impatient

Thanks for reminding me about this @impatient. I've submitted a pull request for further discussion: https://github.com/ktorio/ktor/pull/1957

lopcode avatar Jun 19 '20 17:06 lopcode

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

oleg-larshin avatar Aug 10 '20 15:08 oleg-larshin