Skip to content

JsonError needs an easy way to convert the whole thing to a string #1059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
distinctdan opened this issue Mar 18, 2025 · 2 comments
Open
Labels
type: enhancement New feature or request

Comments

@distinctdan
Copy link

Feature description

I'm trying to log the error when my server returns a 400. This has turned out to be difficult because JsonError doesn't have a human readable toString() function. Instead, it just returns the message and ignores all the embedded errors, which is what I actually care about. I didn't even expect to have to deal with a response object here in RequestFilter, I expected to be able get the response body as a string. I'm not sure having a response object is bad, it just makes it hard to log the response. I could serialize it, but then I'm double serializing, which is inefficient.

@ojebari-ma
Copy link
Member

Could you please provide a reproducible example?

@distinctdan
Copy link
Author

Here's my example, I'm using a @RequestFilter to log all requests. The bug happens on any 400, where res.body.toString() will only print the top level message like "Bad Request", and won't show what actually caused the request to fail. To work around this, I'm using objectMapper to serialize the body to a string before logging.

@ServerFilter(ServerFilter.MATCH_ALL_PATTERN)
class LoggingServerFilter @Inject constructor(private val objectMapper: ObjectMapper) : Ordered {
    private val LOG = LoggerFactory.getLogger("HTTP")
    private val nextTraceId = AtomicLong(1)

    override fun getOrder(): Int {
        return Ordered.HIGHEST_PRECEDENCE
    }

    @RequestFilter
    // IMPORTANT: Execute on BLOCKING executor to avoid blocking the main Netty event loop and stalling the server.
    @ExecuteOn(TaskExecutors.BLOCKING)
    fun logRequest(req: HttpRequest<*>, continuation: FilterContinuation<MutableHttpResponse<*>>) {
        // Log the request
        val traceId = nextTraceId.getAndIncrement()
        LOG.info("REQ ${req.methodName} ${req.uri} traceId=$traceId")

        // Run the request and log the response
        var res: MutableHttpResponse<*>
        val duration = measureTime {
            res = continuation.proceed()
        }
        // For 400 Bad Requests, log the response so we can see why it failed. This doesn't appear to significantly affect
        // performance; local testing times appear identical. The body is JsonError, which unfortunately
        // doesn't have a human-readable "toString" method, so we're using objectMapper to convert it back to JSON.
        val errorBody = if (res.status == HttpStatus.BAD_REQUEST) {
            val body = res.body.getOrNull()
            val bodyString = objectMapper.writeValueAsString(body)
            " rsp='${bodyString}'"
        } else ""
        LOG.info("RSP ${res.status.code} ${req.methodName} ${req.uri} time=${duration.inWholeMilliseconds}ms traceId=${traceId}${errorBody}")
    }
}

@ojebari-ma ojebari-ma added type: enhancement New feature or request and removed status: awaiting feedback labels Mar 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants