java - Logback 不会在 Spring Boot 的 @ExceptionHandler 中记录 ConstraintViolationException

标签 java spring-boot exception logback

我正在对我的 Spring Boot Web 服务中的查询参数进行一些验证。在本例中,它是一个与正则表达式 [0-9]{3} 不匹配的参数。所以在服务方法中,有一个验证:

@Pattern(regexp="[0-9]{3}") @Valid @RequestParam(value = "AngivelseFrekvensForholdUnderkontoArtKode", required = false) String angivelseFrekvensForholdUnderkontoArtKode

(angivelseFrekvensForholdUnderkontoArtKode 只是查询参数的名称)

我正在开发一个日志管理器,它基本上只使用 logback 和 slf4j 打印日志消息。

我的日志管理器类中有一个writeInternalError(exception),它在被告知时很好地记录了一个异常:

public void writeInternalError(Exception exception) {
    logger.error(exception.getClass().getName(), kv("LogType", exception), kv("LogMessage", exception));
}

除非 ConstraintViolationException 被我的 @ControllerAdvice 中的 @ExceptionHandler 捕获。没有显示任何错误,并且输出了 Spring 日志而不是我预期的自定义日志。当我调试时,logger.error() 似乎已执行且未显示任何错误。

我做了一个快速修复方法,我手动提取异常信息,但我想对所有异常使用相同的日志记录方法:

public void writeTracelog(Exception exception) {
    logger.error(exception.getClass().getName(), kv("LogType", "exception"), kv("ErrorMessage", exception.getMessage()), kv("StackTrace", exception.getStackTrace()));
}

我得到的预期和意外日志是:

// The Spring log message shown instead of my custom error message:
{
    "@timestamp": "2021-06-10T12:13:40.730+02:00",
    "@version": "1",
    "message": "Resolved [javax.validation.ConstraintViolationException: call29f0dab4A3094a30A1cdE29c01f28af8.angivelseFrekvensForholdUnderkontoArtKode: must match \"[0-9]{3}\"]",
    "logger_name": "org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver",
    "thread_name": "http-nio-8082-exec-1",
    "level": "WARN",
    "level_value": 30000
}

// How the log is supposed to look like
{
    "@timestamp": "2021-06-10T14:35:18.257+02:00",
    "@version": "1",
    "message": "javax.validation.ConstraintViolationException",
    "logger_name": "ClsLogManager",
    "thread_name": "http-nio-8082-exec-1",
    "level": "ERROR",
    "level_value": 40000,
    "LogType": "exception",
    "LogMessage": {
        "cause": null,
        "stackTrace": [...],
        "constraintViolations": null,
        "message": "call29f0dab4A3094a30A1cdE29c01f28af8.angivelseFrekvensForholdUnderkontoArtKode: must match \"[0-9]",
        "suppressed": [],
        "localizedMessage": "call29f0dab4A3094a30A1cdE29c01f28af8.angivelseFrekvensForholdUnderkontoArtKode: must match \"[0-9]"
    }
}

当我用任何其他异常调用 writeInternalError() 时,日志会很好地输出。我尝试了不同的日志记录方式来查看哪些有效,哪些无效,正如您在 @ControllerAdvice

的处理程序中看到的那样
@ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(ConstraintViolationException.class)
    protected ResponseEntity<Object> handleConflict(ConstraintViolationException ex, HttpServletRequest request) {
        ...
        // Get the invalid parameter from the ConstraintViolationException

        if (invalidParameter.equalsIgnoreCase("angivelseFrekvensForholdUnderkontoArtKode")) {
            errorMessage = setErrorMessage(request, "422.9", HttpStatus.UNPROCESSABLE_ENTITY.value(), invalidValue);
            clsLogManager.writeTracelog(ex); // Outputs customized unwanted log
            clsLogManager.writeInternalError(new ConstraintViolationException(null)); // Outputs exception in the format I want
            clsLogManager.writeInternalError(ex); // Outputs nothing
            responseEntity = writeToAuditlog(request, inputHeaders, errorMessage); // Outputs an info log as it supposed to

            return responseEntity; // Outputs the ExceptionHandlerExceptionResolver message after the return
        }
        // Do something else in case of another error
    }
}

看起来记录器无法处理异常,但为什么它不告诉我原因,如果是这样,为什么 ExceptionHandlerExceptionResolver 会处理异常?

更新:

我查看了 ExceptionHandlerExceptionResolversaver建议,发现日志来自AbstractHandlerExceptionResolverlogException()。我的自定义记录器类的方法在 logException() 之前被调用,但它仍然不打印任何内容。会不会是因为它是一个包含字段 constraintViolationsConstraintViolationException 而记录器不知道如何处理它?<​​/p>

有一个 setWarnLogCategory 方法,如果我不想使用 Spring 日志,我想我可以将其关闭。我就是不知道怎么办。 AbstractHandlerExceptionResolver 中的 logException 的 javadocs 表明有一个属性,但我不知道如何设置它。

最佳答案

更新:

问题出在 OutputStreamAppender 中的方法 subAppend(E event) 中,该类是 ConsoleAppender 的父级并且在编码行中: byte[] byteArray = this.encoder.encode(event); 编码器尝试序列化 ConstraintViolationException 异常,jackson 失败并出现错误:HV000116: getParameterIndex() may only be invoked for type nodes of type ElementKind.PARAMETER.

enter image description here

并且编码的结果是异常情况下的空字节数组,这就是控制台中没有任何记录的原因。见下文:

enter image description here

我现在没有快速解决这个问题的办法。

旧提案:

我建议在 ExceptionHandlerExceptionResolver 类中调试方法 doResolveHandlerMethodException ,你可以看到只有一个地方 spring boot logger 可以记录警告级别的消息并且该记录器将工作仅在调用异常处理程序方法期间发生某些情况(例如:处理程序方法中的参数类型不正确等)。您将看到未调用处理程序方法的原因。 enter image description here

请注意当类 ConstraintViolationException 可以位于两个不同的包中时的情况:

  • javax.validation.ConstraintViolationException
  • org.hibernate.exception.ConstraintViolationException

当然,在我们的例子中,我们应该使用 javax.validation 包中的 ConstraintViolationException

关于java - Logback 不会在 Spring Boot 的 @ExceptionHandler 中记录 ConstraintViolationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67922344/

相关文章:

java - 从上下文映射资源

Dockerizing Spring boot 应用程序以进行 Kubernetes 部署

spring-boot - @SpringBootTest : pre-test logging set to debug by default?

java - 如何避免以下代码中的Java.util.IllegalStateException?

C++ 迭代器异常安全

flutter - 错误 : SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints

java - 读取超时属性有什么作用?

java - 在 Jboss Wildfly 中访问 Resteasy 服务方法时抛出 401 未经授权的错误

spring - 获取我的 REST API 文档的 Spring Boot Swagger 配置有什么问题?为什么我无法访问文档?

java - 有没有一种工具可以查看我的 Java 代码,并建议一个好的包结构?