java - Logback 不会在 Spring Boot 的 @ExceptionHandler 中记录 ConstraintViolationException
问题描述
我正在对我的 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
在@ExceptionHandler
我的@ControllerAdvice
. 没有显示错误,并且输出了 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
要这样做呢?
更新:
我ExceptionHandlerExceptionResolver
按照保护程序的建议进行了调查,发现日志来自AbstractHandlerExceptionResolver
's logException()
。我的自定义记录器类的方法在之前被调用logException()
,但它仍然不打印任何东西。可能是因为它是一个ConstraintViolationException
包含该字段的字段constraintViolations
并且记录器不知道如何处理这个?
如果我不想要 Spring 日志,我想有一种setWarnLogCategory
方法可以关闭。我只是不知道怎么做。logException
in的 javadocsAbstractHandlerExceptionResolver
表明有一个属性,但我不知道如何设置它。
解决方案
更新:
问题在于该类中的方法subAppend(E event)
是OutputStreamAppender
编码的父级ConsoleAppender
和编码行:
byte[] byteArray = this.encoder.encode(event);
编码器尝试序列化ConstraintViolationException
异常并且杰克逊失败并出现错误:HV000116: getParameterIndex() may only be invoked for nodes of type ElementKind.PARAMETER.
并且由于编码的结果是空字节数组,以防出现异常,这就是为什么控制台中没有记录任何内容的原因。见下文:
我现在没有快速解决方法。
旧提案:
我建议doResolveHandlerMethodException
在ExceptionHandlerExceptionResolver
类中调试方法,您会看到只有一个地方 Spring Boot 记录器可以记录具有警告级别的消息,并且该记录器仅在调用异常处理程序方法期间发生某些事情时才起作用(例如:不正确的类型处理程序方法中的参数等)。您将看到未调用处理程序方法的原因。
请注意类ConstraintViolationException
可以位于两个不同包中的情况:
- javax.validation.ConstraintViolationException
- org.hibernate.exception.ConstraintViolationException
当然,在我们的例子中,我们应该使用ConstraintViolationException
from javax.validation
package
推荐阅读
- laravel - 我的 Laravel 应用程序加载时间很慢
- visual-studio-code - VS code 无法激活某些设置和功能
- amazon-web-services - AWS 中的“每个账户每个区域”是什么意思
- amazon-web-services - 如何添加新的 Prometheus 目标
- ios - 从 Xcode 11 获取主从应用程序模板?
- javascript - 使用 Bixolon 热敏收据打印机 javascript SDK 以英语以外的其他语言打印
- linux - ffplay不能播放mp4视频,但是mpv可以
- git - 在 VS2019 中,从项目中排除了拉取的文件
- react-native - 不同导航器中的反应导航深度链接
- linux - 如何编写用于获取公共 IP 地址的 Bash 脚本,当一个命令失败时,它必须执行另一个命令