实际开发中,需要对各种异常进行处理,并记录日志,常见的异常包括以下几种:
- 系统错误,最常见的,系统未处理的
- 404,请求url不存在
- 参数错误,请求的参数不符合校验规则
在springBoot中采用controllerAdvice来进行公共异常的拦截。先设置系统的日志,用于记录异常信息。
logback设置
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<!-- appender -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} - %m%n
</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 系统日志配置 -->
<appender name="sysLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/sys.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/sys-%d{yyyyMMdd}.log
</FileNamePattern>
<maxHistory>365</maxHistory>
</rollingPolicy>
<encoder>
<ImmediateFlush>true</ImmediateFlush>
<charset>utf-8</charset>
<Pattern>[%d{yyyyMMdd HH:mm:ss}] - %m%n</Pattern>
</encoder>
</appender>
<appender name="daoLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/dao.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/dao-%d{yyyyMMdd}.log
</FileNamePattern>
<maxHistory>365</maxHistory>
</rollingPolicy>
<encoder>
<ImmediateFlush>true</ImmediateFlush>
<charset>utf-8</charset>
<Pattern>[%d{yyyyMMdd HH:mm:ss}] [%-5level] [%-30logger{0}][%-3L]
[SeqId:%X{SeqId}] - %m%n
</Pattern>
</encoder>
</appender>
<appender name="serviceLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/service.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/service-%d{yyyyMMdd}.log
</FileNamePattern>
<maxHistory>365</maxHistory>
</rollingPolicy>
<encoder>
<ImmediateFlush>true</ImmediateFlush>
<charset>utf-8</charset>
<Pattern>[%d{yyyyMMdd HH:mm:ss}] - %m%n</Pattern>
</encoder>
</appender>
<appender name="controllerLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/controller.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/controller-%d{yyyyMMdd}.log
</FileNamePattern>
<maxHistory>365</maxHistory>
</rollingPolicy>
<encoder>
<ImmediateFlush>true</ImmediateFlush>
<charset>utf-8</charset>
<Pattern>[%d{yyyyMMdd HH:mm:ss}]- %m%n</Pattern>
</encoder>
</appender>
<appender name="operationLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/operation.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/operation-%d{yyyyMMdd}.log
</FileNamePattern>
<maxHistory>365</maxHistory>
</rollingPolicy>
<encoder>
<ImmediateFlush>true</ImmediateFlush>
<charset>utf-8</charset>
<Pattern>%m%n</Pattern>
</encoder>
</appender>
<appender name="performanceLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/performance.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${LOG_PATH}/%d{yyyyMMdd}/performance-%d{yyyyMMdd}.log
</FileNamePattern>
<maxHistory>365</maxHistory>
</rollingPolicy>
<encoder>
<ImmediateFlush>true</ImmediateFlush>
<charset>utf-8</charset>
<Pattern>[%d{yyyyMMdd HH:mm:ss}] - %m%n</Pattern>
</encoder>
</appender>
<!-- additivity为false不向root控制台输出 -->
<logger name="sysLog" additivity="false" level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="sysLogAppender"/>
</logger>
<logger name="daoLog" additivity="false" level="ERROR">
<appender-ref ref="stdout"/>
<appender-ref ref="daoLogAppender"/>
</logger>
<logger name="serviceLog" additivity="false" level="ERROR">
<appender-ref ref="stdout"/>
<appender-ref ref="serviceLogAppender"/>
</logger>
<logger name="controllerLog" additivity="false" level="INFO">
<appender-ref ref="stdout"/>
<appender-ref ref="controllerLogAppender"/>
</logger>
<logger name="operationLog" additivity="false" level="INFO">
<appender-ref ref="stdout"/>
<appender-ref ref="operationLogAppender"/>
</logger>
<logger name="performanceLog" additivity="false" level="INFO">
<appender-ref ref="stdout"/>
<appender-ref ref="performanceLogAppender"/>
</logger>
<!-- root 默认日志配置 -->
<root level="info">
<appender-ref ref="stdout"/>
<appender-ref ref="sysLogAppender"/>
</root>
<logger name="org.springframework" level="INFO"/>
</configuration>
和正常的logback日志日志没有什么不同,把日志分为以下几类:
- sys:记录工程启动和停止的日志
- controller:记录controller层的异常
- service:service层异常,主要是用AOP来记录
- dao:dao层的异常,主要是用AOP来记录
- performace:系统性能监测记录,也是用AOP来记录
- operation:操作记录,主要记录接口请求记录
controllerAdvice
@ControllerAdvice
public class ApiControllerAdvice {
/**
* 定义日志处理
*/
private static Logger logger = LoggerFactory.getLogger("controllerLog");
/**
* 系统异常处理,比如:404,500
*
* @param request request
* @param response response
* @param e 异常
* @return JsonResult结构
* @throws Exception 异常
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public JsonResult defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) throws Exception {
logger.error("系统异常", e);
logException(request);
if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
response.setStatus(HttpStatus.NOT_FOUND.value());
return new JsonResult(false, GlobalReturnCode.SYSTEM_PATH_NOEXIST);
} else {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return new JsonResult(false, GlobalReturnCode.SYSTEM_ERROR);
}
}
/**
* 取得header的所有属性
*
* @param headers 请求的headers
* @param request request
* @return 把header拼成字符串
*/
private String getHeaderValue(Enumeration<String> headers, HttpServletRequest request) {
String str = "";
while (headers.hasMoreElements()) {
String header = headers.nextElement();
str += header + "=" + request.getHeader(header) + "&";
}
return str;
}
/**
* 打印所有异常信息
*
* @param request request
*/
private void logException(HttpServletRequest request) {
try {
logger.error("请求路径:" + request.getServletPath());
logger.error("请求参数:" + request.getParameterMap().toString());
logger.error("请求header:" + getHeaderValue(request.getHeaderNames(), request));
logger.error("请求body:" + StringUtil.getBodyString(request.getReader()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
这里先记录404错误和500错误,如果接口请求有异常,直接返回对应的json串
404错误
为了使springBoot能拦截404错误,需要在application.yml中增加如下配置:
spring:
mvc:
throw-exception-if-no-handler-found: true
resources:
add-mappings: false
如果不加上面配置,404是无法获取拦截的。
如果没有配置异常拦截,直接请求springboot会返回如下错误:

配置后返回如下信息:

500错误
为了整体拦截系统异常,需要定制返回的500错误,避免返回异常给调用端。如下图所示:

网友评论