日志
在resources文件夹中创建logback-spring.xml文件这个会在你项目的平级目录创建一个Logs文件夹,根据时间进行区分,并包含错误日志和控制台打印日志
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<conversionRule conversionWord="ip" converterClass="com.util.LogIpConfig" />
<!-- Log file path -->
<property name="log.path" value="../Logs/" />
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!-- <pattern> [%ip]%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>-->
<pattern> [host:%ip]%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- Log file debug output -->
<appender name="fileRolling_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM-dd}/info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--每个文件的大小限制-->
<MaxFileSize>1GB</MaxFileSize>
<!--最多保留10天的文件,10天之前的将被清除-->
<MaxHistory>10</MaxHistory>
<!--该滚动策略日志的总大小,超过的日志会被清除-->
<!-- <totalSizeCap>10GB</totalSizeCap>-->
<!--启动时清理日志文件 此项置灰清理超过保留天数的 也会清理超过总大小的-->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern> [host:%ip]%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n
</pattern>
</encoder>
<!--<filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level>
<onMatch>DENY</onMatch> <onMismatch>NEUTRAL</onMismatch> </filter> -->
</appender>
<!-- Log file error output -->
<appender name="fileRolling_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/%d{yyyy-MM-dd}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--每个文件的大小限制-->
<MaxFileSize>1GB</MaxFileSize>
<!--最多保留10天的文件,10天之前的将被清除-->
<MaxHistory>10</MaxHistory>
<!--该滚动策略日志的总大小,超过的日志会被清除-->
<!-- <totalSizeCap>10GB</totalSizeCap>-->
<!--启动时清理日志文件 此项置灰清理超过保留天数的 也会清理超过总大小的-->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern> [host:%ip]%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n </pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="debug">
<appender-ref ref="console"/>
</root>
<root level="info">
<!-- <appender-ref ref="console"/>-->
<appender-ref ref="fileRolling_info" />
<appender-ref ref="fileRolling_error" />
</root>
<logger name="org.springframework" level="INFO"/>
<logger name="org.mybatis" level="INFO"/>
<logger name="com.test" level="debug"/>
<!-- Enable MDC -->
<contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
<resetJUL>true</resetJUL>
</contextListener>
</configuration>
日志中ip
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class LogIpConfig extends ClassicConverter {
@Override
public String convert(ILoggingEvent event) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return "127.0.0.1";
}
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
String remoteAddr = "";
if (request != null) {
remoteAddr = request.getHeader("X-Forwarded-For");
if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
remoteAddr = request.getHeader("X-Real-IP");
}
if (remoteAddr == null || remoteAddr.trim().isEmpty()) {
remoteAddr = request.getRemoteAddr();
}
}
return remoteAddr;
}
}
在application.yml文件中引入
logging:
config: classpath:logback-spring.xml
全局捕获异常
springboot中通过GlobalExceptionHandler类来实现。通过全局捕获异常可以减少trycatch的使用并且可以通过枚举创建状态码和信息。
通过下方几个文件实现
BaseErrorInfoInterface.java文件
public interface BaseErrorInfoInterface {
/**
* 错误码
*/
int getResultCode();
/**
* 错误描述
*/
String getResultMsg();
}
BusinessException.java文件
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = -4879677283847539655L;
private int errorCode;
private String errorMessage;
private String exceptionMessage;
private Exception exception;
public BusinessException(String errorMessage) {
super();
this.errorMessage = errorMessage;
}
public BusinessException(int errorCode, String errorMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public BusinessException(int errorCode, String errorMessage, Exception exception) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.exception = exception;
}
public BusinessException(String errorMessage, String exceptionMessage) {
super();
this.exceptionMessage = exceptionMessage;
this.errorMessage = errorMessage;
}
public String getExceptionMessage() {
return exceptionMessage;
}
public void setExceptionMessage(String exceptionMessage) {
this.exceptionMessage = exceptionMessage;
}
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public Exception getException() {
return exception;
}
public void setException(Exception exception) {
this.exception = exception;
}
public BusinessException(int errorCode, String errorMessage, String exceptionMessage) {
super();
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.exceptionMessage = exceptionMessage;
}
}
CommonEnum.java文件
public enum CommonEnum implements BaseErrorInfoInterface {
// 数据操作错误定义
SUCCESS(200, "成功!"),
BODY_NOT_MATCH(400,"请求的数据格式不符!"),
SIGNATURE_NOT_MATCH(401,"请求的数字签名不匹配!"),
NOT_FOUND(404, "未找到该资源!"),
INTERNAL_SERVER_ERROR(500, "服务器内部错误!"),
SERVER_BUSY(503,"服务器正忙,请稍后再试!")
;
/** 错误码 */
private int resultCode;
/** 错误描述 */
private String resultMsg;
CommonEnum(int resultCode, String resultMsg) {
this.resultCode = resultCode;
this.resultMsg = resultMsg;
}
@Override
public int getResultCode() {
return resultCode;
}
@Override
public String getResultMsg() {
return resultMsg;
}
}
ErrController.java文件
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ErrController implements ErrorController {
public String getErrorPath() {
// TODO Auto-generated method stub
return "/error";
}
@RequestMapping("/error")
public boolean handlerError() {
throw new BusinessException(-1,"页面不存在");
}
}
GlobalExceptionHandler.java文件
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;
import javax.servlet.http.HttpServletRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理自定义的业务异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public Result bizExceptionHandler(HttpServletRequest req, BusinessException e){
StackTraceElement[] stackTrace = e.getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
sb.append(stackTraceElement.toString()).append("\n");
}
logger.error("未知异常:"+e.getErrorMessage()+"!原因是:"+e);
logger.error("未知异常!錯誤信息是:"+sb.toString());
return Result.error(e.getErrorCode(),e.getErrorMessage());
}
/**
* 处理空指针的异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =NullPointerException.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, NullPointerException e){
StackTraceElement[] stackTrace = e.getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
sb.append(stackTraceElement.toString()).append("\n");
}
logger.error("未知异常!原因是:"+e);
logger.error("未知异常!錯誤信息是:"+sb.toString());
return Result.error(CommonEnum.BODY_NOT_MATCH);
}
/**
* 处理其他异常
* @param req
* @param e
* @return
*/
@ExceptionHandler(value =Exception.class)
@ResponseBody
public Result exceptionHandler(HttpServletRequest req, Exception e){
StackTraceElement[] stackTrace = e.getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElement : stackTrace) {
sb.append(stackTraceElement.toString()).append("\n");
}
logger.error("未知异常!原因是:"+e);
logger.error("未知异常!錯誤信息是:"+sb.toString());
return Result.error(CommonEnum.INTERNAL_SERVER_ERROR);
}
}
MyCustomErrorAttributes.java文件
import org.springframework.boot.web.error.ErrorAttributeOptions;
import org.springframework.boot.web.servlet.error.DefaultErrorAttributes;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.WebRequest;
import java.util.Map;
@Component
public class MyCustomErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) {
Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
return errorAttributes;
}
}
Result.java文件
import com.alibaba.fastjson.JSONObject;
public class Result {
/**
* 响应代码
*/
private int code;
/**
* 响应消息
*/
private String message;
/**
* 响应结果
*/
private Object result;
public Result() {
}
public Result(BaseErrorInfoInterface errorInfo) {
this.code = errorInfo.getResultCode();
this.message = errorInfo.getResultMsg();
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
/**
* 成功
*
* @return
*/
public static Result success() {
return success(null);
}
/**
* 成功
* @param data
* @return
*/
public static Result success(Object data) {
Result rb = new Result();
rb.setCode(CommonEnum.SUCCESS.getResultCode());
rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
rb.setResult(data);
return rb;
}
/**
* 失败
*/
public static Result error(BaseErrorInfoInterface errorInfo) {
Result rb = new Result();
rb.setCode(errorInfo.getResultCode());
rb.setMessage(errorInfo.getResultMsg());
rb.setResult(null);
return rb;
}
/**
* 失败
*/
public static Result error(int code, String message) {
Result rb = new Result();
rb.setCode(code);
rb.setMessage(message);
rb.setResult(null);
return rb;
}
/**
* 失败
*/
public static Result error(String message) {
Result rb = new Result();
rb.setCode(-1);
rb.setMessage(message);
rb.setResult(null);
return rb;
}
@Override
public String toString() {
return JSONObject.toJSONString(this);
}
}
注意:如果使用了全局捕获异常但是你的代码中还是有异常警告
解决办法1:在全局捕获异常中在使用try-catch在包一次这个代码,但是当try-catch捕获异常之后如果不做处理的话全局异常并不会识别到有异常就不会打印错误日志。所以可以将异常上抛或在catch中进行日志打印操作。
public class ExceptionExample {
private static final Logger logger = Logger.getLogger(ExceptionExample.class.getName());
public String test() throws Exception {
try{
//代码块
}
catch(catch (Exception e){
throw new Exception(e);//上抛错误
logger.error("错误信息");//直接打印日志
}
}
}
解决办法2:使用@SneakyThrows注解。这个注解是 Lombok 库中的一个注解,它可以帮助简化在 Java 中处理受检查异常(checked exception)的代码。在使用 @SneakyThrows
注解后,编译器会自动为标记的方法中的受检查异常生成 try-catch 代码,将异常转换为未经检查的异常(unchecked exception)抛出,但是可能会出现一些隐藏的异常处理问题,所以谨慎使用。
public class ExceptionExample {
@SneakyThrows
public String test(){
//代码块
}
}