問(wèn)題
在springboot項(xiàng)目中, 配置logback.xml文件, 將info及以上等級(jí)的日志信息保存到info.log文件中, 將error等級(jí)的日志保存到error.log文件中. 項(xiàng)目啟動(dòng)后, 兩個(gè)日志文件已經(jīng)生成, 但是項(xiàng)目運(yùn)行過(guò)程中拋出異常, 異常信息并沒(méi)有輸出到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" />
<!--輸出到控制臺(tái)-->
<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>
問(wèn)題分析
在配置文件中, 分好日志類(lèi)型刨秆,日志也會(huì)按級(jí)別輸出相應(yīng)的文件。前提是我們自己調(diào)用了logger.info(), logger.error()等日志輸出方法。而RuntimeException是非jdk檢測(cè)異常, 我們不可能每次try{}catch(){}后使用logger.error(e.getMessage()), 而且這樣輸出的異常信息也沒(méi)有辦法把錯(cuò)誤的堆棧信息全部打印出來(lái). 這樣的話, 我們只能每次到控制臺(tái)上查看異常日志, 非常不方便.
解決方案
方案一
使用全局異常處理方案, 對(duì)所有異常的堆棧信息使用logger.error()進(jìn)行打印
代碼如下:
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)); // 記錄錯(cuò)誤信息
String msg = e.getMessage();
if (msg == null || msg.equals("")) {
msg = "服務(wù)器出錯(cuò)";
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("message", msg);
return jsonObject;
}
}
缺點(diǎn): 采用全局異常處理, 只能攔截controller層的異常, 而service/dao的異常則不能捕獲, 指標(biāo)不治本
方案二
我們知道, 異常信息的打印流是System.err(...), 若要將打印流的異常信息輸出到文件中中, 則只能將系統(tǒng)打印流重定向至文件中, 如下:
System.setErr(new PrintStream(new FileOutputStream(file)));
缺點(diǎn): 重定向打印流到文件中, 則意味著該打印流只能輸出到文件, 而控制臺(tái)中則不會(huì)打印異常信息