前言:
关于项目生产中产生的日志有很多种处理方式,有些小伙伴习惯放在数据库中,有些小伙伴习惯放在日志文件中等等,笔者觉得日志应该分类处理,比如像 error 这种日志应该放在数据库中便于查看,而普通日志 info 这些应该在日志文件中记录(当然这只是笔者个人的处理方式而已 0_0)。
环境:
spring boot:2.2.1
jdk:1.8
log4j:2.11.1
项目源码:https://github.com/XGLLHZ/springboot-frame.git
正文:
1、创建日志实体:
@Data
@Accessors(chain = true)
@TableName("sys_log")
public class LogEntity extends BaseEntityUtil {
@TableId(type = IdType.AUTO)
private Integer id; //主键
private String className; //类名称
private String methodName; //方法名称
private String exceptionName; //异常类型
private String errorMsg; //错误信息
private String stackTrace; //堆栈跟踪信息
private Integer deleteFlag; //删除状态:0:未删除;1:已删除
private Timestamp createTime; //创建时间
private Timestamp updateTime; //修改时间
}
然创建对应的日志记录表。
2、编写日志记录处理类:
@Component
public class LogAppenderConfig extends UnsynchronizedAppenderBase<ILoggingEvent> {
private static final Logger log = LoggerFactory.getLogger(LogAppenderConfig.class);
@Autowired
LogMapper logMapper;
/**
* LogAppenderConfig 初始化(服务启动时初始化)
*/
@PostConstruct
public void init() {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
ThresholdFilter filter = new ThresholdFilter();
filter.setLevel("ERROR");
filter.setContext(context);
filter.start();
this.addFilter(filter);
this.setContext(context);
context.getLogger("ROOT").addAppender(LogAppenderConfig.this);
super.start();
}
/**
* error 日志写入数据库
* @param iLoggingEvent
*/
@Override
protected void append(ILoggingEvent iLoggingEvent) {
IThrowableProxy iThrowableProxy = iLoggingEvent.getThrowableProxy();
LogEntity logEntity = new LogEntity();
//异常信息
logEntity.setErrorMsg(iLoggingEvent.getMessage());
//异常产生时间
logEntity.setCreateTime(new Timestamp(iLoggingEvent.getTimeStamp()));
if (iLoggingEvent.getCallerData() != null && iLoggingEvent.getCallerData().length > 0) {
StackTraceElement stackTraceElement = iLoggingEvent.getCallerData()[0];
//异常产生类名称
logEntity.setClassName(stackTraceElement.getClassName());
//异常产生方法名
logEntity.setMethodName(stackTraceElement.getMethodName());
}
if (iThrowableProxy != null) {
//异常类型
logEntity.setExceptionName(iThrowableProxy.getClassName());
//异常堆栈跟踪信息
logEntity.setStackTrace(Arrays.toString(iThrowableProxy.getStackTraceElementProxyArray()));
}
try {
logMapper.insert(logEntity);
} catch (Exception e) {
log.error(e.toString(), e);
}
}
}
这个类是我们自己定义的负责记录日志事件工具类,相当于 logback 中的 Appender,ILoggingEvent 可以从堆栈信息中取到文件名、类名、时间等信息,它是可以序列化的对象。我们可以从这个类中拿到关于异常的类名、异常类型、时间等信息。然后创建初始化 LogAppenderConfig 的方法 init(),加上 @PostConstruct 注解,意为在服务启动时执行此方法,也就是在服务启动时初始化 LogAppenderConfig 实例。然后实现 UnsynchronizedAppenderBase 接口中的 append() 方法, 在这个方法中将日志写入数据库(异常日志)。
3、日志收集:
@RestController
@RequestMapping("/web")
public class WebTestController {
private static final Logger log = LoggerFactory.getLogger(WebTestController.class);
@RequestMapping("/test")
public String test() {
String s = null;
try {
s.charAt(0); //在搞一个空指针异常
} catch (Exception e) {
log.error(e.toString(), e);
}
return "Hello Web!";
}
}
我们可以创建一个接口自定义一个异常来测试。在实际开发中,我们在会产生异常的地方加入日志记录,如果产生异常,则会将异常信息记录到数据库。 实际上正确的做法是利用切面编程来统一管理日志,然后在放入数据库。
4、测试:
通过测试我们发现在控制已经出现了空指针异常,查看数据库,异常信息已经被记录。
注:关于 info 等小级别的日志,我们可以将其记录在日志文件中,比如在项目部署时将日志挂载到宿主机的文件中即可。