All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.javalin.core.util.LogUtil.kt Maven / Gradle / Ivy

The newest version!
/*
 * Javalin - https://javalin.io
 * Copyright 2017 David Åse
 * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
 */

package io.javalin.core.util

import io.javalin.Context
import io.javalin.core.HandlerType
import io.javalin.core.PathMatcher
import io.javalin.websocket.WsHandler
import io.javalin.websocket.WsSession
import org.slf4j.LoggerFactory
import java.util.*

object LogUtil {

    private val log = LoggerFactory.getLogger(LogUtil::class.java)

    fun logRequestAndResponse(ctx: Context, matcher: PathMatcher) = try {
        val type = HandlerType.fromServletRequest(ctx.req)
        val requestUri = ctx.req.requestURI
        val executionTimeMs = Formatter(Locale.US).format("%.2f", executionTimeMs(ctx))
        val gzipped = ctx.res.getHeader(Header.CONTENT_ENCODING) == "gzip"
        val staticFile = ctx.req.getAttribute("handled-as-static-file") == true
        with(ctx) {
            val allMatching = (matcher.findEntries(HandlerType.BEFORE, requestUri) + matcher.findEntries(type, requestUri) + matcher.findEntries(HandlerType.AFTER, requestUri)).map { it.type.name + "=" + it.path }
            val resBody = resultStream()?.apply { reset() }?.bufferedReader()?.use { it.readText() } ?: ""
            val resHeaders = res.headerNames.asSequence().map { it to res.getHeader(it) }.toMap()
            log.info("""JAVALIN REQUEST DEBUG LOG:
                        |Request: ${method()} [${path()}]
                        |    Matching endpoint-handlers: $allMatching
                        |    Headers: ${headerMap()}
                        |    Cookies: ${cookieMap()}
                        |    Body: ${if (isMultipart()) "Multipart data ..." else body()}
                        |    QueryString: ${queryString()}
                        |    QueryParams: ${queryParamMap().mapValues { (_, v) -> v.toString() }}
                        |    FormParams: ${formParamMap().mapValues { (_, v) -> v.toString() }}
                        |Response: [${status()}], execution took $executionTimeMs ms
                        |    Headers: $resHeaders
                        |    ${resBody(resBody, gzipped, staticFile)}
                        |----------------------------------------------------------------------------------""".trimMargin())
        }
    } catch (e: Exception) {
        log.info("An exception occurred while logging debug-info", e)
    }

    private fun resBody(resBody: String, gzipped: Boolean, staticFile: Boolean) = when {
        staticFile -> "Body is a static file (not logged)"
        resBody.isNotEmpty() && gzipped -> "Body is gzipped (${resBody.length} bytes, not logged)"
        resBody.isNotEmpty() && !gzipped -> "Body is ${resBody.length} bytes (starts on next line):\n    $resBody"
        else -> "No body was set"
    }

    fun startTimer(ctx: Context) = ctx.attribute("javalin-request-log-start-time", System.nanoTime())

    fun executionTimeMs(ctx: Context) = (System.nanoTime() - ctx.attribute("javalin-request-log-start-time")!!) / 1000000f

    @JvmStatic
    fun wsDebugLogger(ws: WsHandler) {
        ws.onConnect { s -> s.logEvent("onConnect") }
        ws.onMessage { s, msg -> s.logEvent("onMessage (String)", "Message (next line):\n$msg") }
        ws.onMessage { s, msg, offset, length -> s.logEvent("onMessage (Binary)", "Offset: $offset, Length: $length\nMessage (next line):\n$msg") }
        ws.onClose { s, statusCode, reason -> s.logEvent("onClose", "StatusCode: $statusCode\nReason: ${reason ?: "No reason was provided"}") }
        ws.onError { s, throwable -> s.logEvent("onError", "Throwable:  ${throwable ?: "No throwable was provided"}") }
    }

    private fun WsSession.logEvent(event: String, additionalInfo: String = "") {
        log.info("""JAVALIN WEBSOCKET DEBUG LOG
                |WebSocket Event: $event
                |Session Id: ${this.id}
                |Host: ${this.host()}
                |Matched Path: ${this.matchedPath()}
                |PathParams: ${this.pathParamMap()}
                |QueryParams: ${if (this.queryString() != null) this.queryParamMap().mapValues { (_, v) -> v.toString() }.toString() else "No query string was provided"}
                |$additionalInfo
                |----------------------------------------------------------------------------------""".trimMargin())
    }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy