问题
在springboot项目中, 配置logback.xml文件, 将info及以上等级的日志信息保存到info.log文件中, 将error等级的日志保存到error.log文件中. 项目启动后, 两个日志文件已经生成, 但是项目运行过程中抛出异常, 异常信息并没有输出到error.log中.
logback.xml配置
logback.xml配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback</contextName>
<property name="log.path" value="/Users/tengjun/Documents/log" />
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>-->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--输出到info文件-->
<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.info.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--输出到error文件-->
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.error.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>error</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="info" />
<appender-ref ref="error" />
</root>
</configuration>
问题分析
在配置文件中, 分好日志类型,日志也会按级别输出相应的文件。前提是我们自己调用了logger.info(), logger.error()等日志输出方法。而RuntimeException是非jdk检测异常, 我们不可能每次try{}catch(){}后使用logger.error(e.getMessage()), 而且这样输出的异常信息也没有办法把错误的堆栈信息全部打印出来. 这样的话, 我们只能每次到控制台上查看异常日志, 非常不方便.
解决方案
方案一
使用全局异常处理方案, 对所有异常的堆栈信息使用logger.error()进行打印
代码如下:
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ResponseBody
@ExceptionHandler(Exception.class)
public Object handleException(Exception e) {
logger.error(ExceptionUtils.getFullStackTrace(e)); // 记录错误信息
String msg = e.getMessage();
if (msg == null || msg.equals("")) {
msg = "服务器出错";
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("message", msg);
return jsonObject;
}
}
缺点: 采用全局异常处理, 只能拦截controller层的异常, 而service/dao的异常则不能捕获, 指标不治本
方案二
我们知道, 异常信息的打印流是System.err(...), 若要将打印流的异常信息输出到文件中中, 则只能将系统打印流重定向至文件中, 如下:
System.setErr(new PrintStream(new FileOutputStream(file)));
缺点: 重定向打印流到文件中, 则意味着该打印流只能输出到文件, 而控制台中则不会打印异常信息