背景
业务凌晨收到磁盘IO告警,每秒内IO操作时长平均占用比例大于90%
分析
查看磁盘IO监控指标
发现有一个操作,该操作导致大量的磁盘读写IO。
cp catalina.out catalina.out-2021-12-18
了解背后的原因发现,业务日志大量输出到了tomcat的catalina.out中,期间有收到磁盘满的告警,因此才做了这样的一个日志轮转:先拷贝一份,然后旧的日志清空。
分析结果:业务日志写入tomcat catalina.out中,导致磁盘占满,所以做了日志轮转策略,导致凌晨IO操作频繁。
为什么业务日志会输出到tomcat catalina.out中
1、首先了解什么样的日志会输出到catalina.out中?
catalina.out主要记录运行的一些信息,尤其是一些异常错误日志信息。
- tomcat运行日志
- catalina.out其实是tomcat的标准输出(stdout)和标准出错(stderr)
- 在tomcat的启动脚本里指定,如果没有修改的话stdout和stderr会重定向到这里。所以我们在应用里使用System.out、System.err打印的日志都会输出到这里。
- 如果我们在应用里使用其他的日志框架,配置了向Console输出的,则也会在这里出现。以logback为例,如果配置ch.qos.logback.core.ConsoleAppender则会输出到catalina.out里。
2、查看线上的log4j配置文件,发现并没有配置org.apache.log4j.ConsoleAppender,只有测试环境配置了
3、询问运维,目前预发都是什么样的业务日志,发现都是SQL打印日志,询问同事发现他们的版本中有日志打印开关,但是预发线上均已经关闭,但是运维依然可以看到SQL日志还在打印。查看MyBatis的SQL日志配置,发现MyBatis配置开启了SQL日志打印。xml配置文件中配置了如下内容:
<setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl" />
4、查看StdOutImpl源码,发现方法中均有标准输出以及标准输错,去掉后问题得以解决。
public class StdOutImpl implements Log {
public StdOutImpl(String clazz) {
// Do Nothing
}
@Override
public boolean isDebugEnabled() {
return true;
}
@Override
public boolean isTraceEnabled() {
return true;
}
@Override
public void error(String s, Throwable e) {
System.err.println(s);
e.printStackTrace(System.err);
}
@Override
public void error(String s) {
System.err.println(s);
}
@Override
public void debug(String s) {
System.out.println(s);
}
@Override
public void trace(String s) {
System.out.println(s);
}
@Override
public void warn(String s) {
System.out.println(s);
}
}
Mybatis 日志源码分析
日志加载部分
Log接口
Log接口比较简单,源码如下:
public interface Log {
// 是否启用debug
boolean isDebugEnabled();
// 是否启用debug
boolean isTraceEnabled();
// 错误日志级别输出方法
void error(String s, Throwable e);
//重载错误日志级别输出方法
void error(String s);
//debug日志级别输出方法
void debug(String s);
//trace日志级别输出方法
void trace(String s);
//warn日志级别输出方法
void warn(String s);
}
关于Log的实现类较多,参考org.apache.ibatis.logging包下的实现类,