精弘网络 Java 后端微课 - EP3 异常捕获与日志记录
接口日志记录
@Slf4j
@Component
public class AccessLogFilter implements Filter {
static {
AnsiOutput.setEnabled(AnsiOutput.Enabled.ALWAYS);
}
private static AnsiColor getStatusColor(int status) {
return switch (status / 100) {
case 2 -> AnsiColor.GREEN;
case 4 -> AnsiColor.YELLOW;
case 5 -> AnsiColor.RED;
default -> AnsiColor.DEFAULT;
};
}
private static AnsiColor getMethodColor(String method) {
return switch (method) {
case "GET" -> AnsiColor.BLUE;
case "POST" -> AnsiColor.CYAN;
case "PUT" -> AnsiColor.YELLOW;
case "DELETE" -> AnsiColor.RED;
default -> AnsiColor.MAGENTA;
};
}
private static String getRemoteAddr(HttpServletRequest request) {
String ip = request.getHeader("x-forwarded-for");
return ip == null ? request.getRemoteAddr() : ip;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!(request instanceof HttpServletRequest req) || !(response instanceof HttpServletResponse res)) {
chain.doFilter(request, response);
return;
}
Instant start = Instant.now();
chain.doFilter(request, response);
Instant end = Instant.now();
long durationMs = Duration.between(start, end).toMillis();
int status = res.getStatus();
String method = req.getMethod();
String uri = req.getRequestURI();
String query = req.getQueryString();
String ip = getRemoteAddr(req);
// 彩色状态码
String colorStatus = AnsiOutput.toString(
getStatusColor(status),
status,
AnsiColor.DEFAULT
);
// 彩色方法
String colorMethod = AnsiOutput.toString(
getMethodColor(method),
method,
AnsiColor.DEFAULT
);
log.info("{} | {}ms | {} {} | IP: {}",
colorStatus,
durationMs,
String.format("%-6s", colorMethod),
uri + (query != null ? "?" + query : ""),
ip
);
}
}
自定义错误类型与错误码聚合
@Getter
public class ApiException extends RuntimeException {
private final Integer errorCode;
private final String errorMsg;
public ApiException(Integer errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public ApiException(Integer errorCode, String errorMsg, Throwable cause) {
super(errorMsg, cause);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
}
@Getter
public enum ExceptionEnum {
INVALID_PARAMETER(200000, "参数错误"),
RESOURCE_NOT_FOUND(200001, "资源不存在"),
WRONG_USERNAME_OR_PASSWORD(200002, "用户名或密码错误"),
PERMISSION_NOT_ALLOWED(200003, "权限不足"),
NOT_FOUND_ERROR(200404, HttpStatus.NOT_FOUND.getReasonPhrase()),
SERVER_ERROR(200500, "系统错误, 请稍后重试"),
;
private final Integer errorCode;
private final String errorMsg;
ExceptionEnum(Integer errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
}
错误捕获
@ControllerAdvice
@Order(1000)
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public AjaxResult<Object> handleGlobalException(Exception e) {
HandlerUtils.logException(e);
return AjaxResult.fail(ExceptionEnum.SERVER_ERROR);
}
}
常见参数校验异常:
MethodArgumentNotValidException
参数校验错误JsonMappingException
Json解析失败HttpMessageNotReadableException
Json格式错误ServletRequestBindingException
Query参数错误
常见 404 异常:
NoResourceFoundException
路径不存在HttpRequestMethodNotSupportedException
请求方法不支持
课外拓展
这边列出一些可以课外研究研究的方面,以便你对 Spring 框架有更深的理解。
注意:这些教程可能与最新的 Spring 版本有所出入,有疑问可以多上网上搜搜,或者问 Qwen、Deepseek 等大语言模型。
精弘网络 Java 后端微课 - EP3 异常捕获与日志记录
https://blog.sugarmgp.icu/2025/08/13/summer-lessons-ep3/