我正在对我的 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
会处理异常?
更新:
我查看了 ExceptionHandlerExceptionResolver
为 saver建议,发现日志来自AbstractHandlerExceptionResolver
的logException()
。我的自定义记录器类的方法在 logException()
之前被调用,但它仍然不打印任何内容。会不会是因为它是一个包含字段 constraintViolations
的 ConstraintViolationException
而记录器不知道如何处理它?</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.
并且编码的结果是异常情况下的空字节数组,这就是控制台中没有任何记录的原因。见下文:
我现在没有快速解决这个问题的办法。
旧提案:
我建议在 ExceptionHandlerExceptionResolver
类中调试方法 doResolveHandlerMethodException
,你可以看到只有一个地方 spring boot logger 可以记录警告级别的消息并且该记录器将工作仅在调用异常处理程序方法期间发生某些情况(例如:处理程序方法中的参数类型不正确等)。您将看到未调用处理程序方法的原因。
请注意当类 ConstraintViolationException
可以位于两个不同的包中时的情况:
- javax.validation.ConstraintViolationException
- org.hibernate.exception.ConstraintViolationException
当然,在我们的例子中,我们应该使用 javax.validation
包中的 ConstraintViolationException
关于java - Logback 不会在 Spring Boot 的 @ExceptionHandler 中记录 ConstraintViolationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67922344/