上篇文章初步分析了下Log4j加载时候的源码,那log4j 是如果使用这些配置文件去记录日志的勒
那就记录下通过log4j的源码来拆解下log4j记录日志的步骤
我们使用log4j日志记录log的时候,开始会生成一个Logger类的对象如下
private static final Logger logger = Logger.getLogger(getClass());
这里初始化一个logger对象,Logger可以进去看到继承了Category,里面可以看到debug,info,error ,log等等的方法,拿info举例
public void info(Object message) {
if (!this.repository.isDisabled(20000)) {
if (Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) {
this.forcedLog(FQCN, Level.INFO, message, (Throwable)null);
}
}
}
如上为info的内容,首先判断repository
那这个repository是哪里来的,isDisabled的里面传的参数为什么是20000,为什么INFO不能写Debug的日志
这里我们就要回到Log4j加载参数的过程,上次分析log4j加载参数的过程只是看了加载配置文件的这部分,忽略了repository的内容,所以还是回去看log4j加载参数的过程
回到LogManger的加载过程
static {
Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
repositorySelector = new DefaultRepositorySelector(h);
String override = OptionConverter.getSystemProperty("log4j.defaultInitOverride", (String)null);
if (override == null || "false".equalsIgnoreCase(override)) {
String configurationOptionStr = OptionConverter.getSystemProperty("log4j.configuration", (String)null);
String configuratorClassName = OptionConverter.getSystemProperty("log4j.configuratorClass", (String)null);
URL url = null;
if (configurationOptionStr == null) {
url = Loader.getResource("log4j.xml");
if (url == null) {
url = Loader.getResource("log4j.properties");
}
} else {
try {
url = new URL(configurationOptionStr);
} catch (MalformedURLException var5) {
url = Loader.getResource(configurationOptionStr);
}
}
if (url != null) {
LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");
OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());
} else {
LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");
}
}
}
首先会初始化一个Hierarchy的对象,这个Hierarchy实现了LoggerRepository接口,所以repository这个对象就是由Hierarchy而来,这个在后面调用的时候会用到,看看Hierarchy的初始化过程
public Hierarchy(Logger root) {
this.root = root;
this.setThreshold(Level.ALL);
this.root.setHierarchy(this);
this.rendererMap = new RendererMap();
this.defaultFactory = new DefaultCategoryFactory();
}
在setThreshod(Level.ALL)方法中 这里默认设定了所有log4j的log等级的,跟到Level对象中,可以发现
public static final Level OFF = new Level(2147483647, "OFF", 0);
public static final Level FATAL = new Level(50000, "FATAL", 0);
public static final Level ERROR = new Level(40000, "ERROR", 3);
public static final Level WARN = new Level(30000, "WARN", 4);
public static final Level INFO = new Level(20000, "INFO", 6);
public static final Level DEBUG = new Level(10000, "DEBUG", 7);
public static final Level TRACE = new Level(5000, "TRACE", 7);
public static final Level ALL = new Level(-2147483648, "ALL", 7);
可以发现INFO 定义的值为20000,所以到这里我们就解决了之前的一个问题,
为什么在执行logger.info时候里面执行的repository.isDisable方法里面传的值为什么是20000
isDisable的执行方法就是判断
之前提到了Hierarchy 实现了LoggerRepository接口,所以isDisable的具体执行过程请在Hierarchy
类中查看如下
public boolean isDisabled(int level) {
return this.thresholdInt > level;
}
这里返回的是一个值的对比结果,thresholdInt的值在初始Hierarchy时候通过setThreshold设定的,可以自行去看
public void info(Object message) {
if (!this.repository.isDisabled(20000)) {
if (Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) {
this.forcedLog(FQCN, Level.INFO, message, (Throwable)null);
}
}
}
当判断repository.isDisabled返回false的时候,会去判断log等级是否为INFO,
判断的过程中会执行getEffectiveLevel的方法,这个方法在Categroy类中,logger继承了这个类
RootLogger又继承了Logger这个类,为什么要说这个关系,因为getEffectiveLevel这段代码中逻辑如下
public Level getEffectiveLevel() {
for(Category c = this; c != null; c = c.parent) {
if (c.level != null) {
return c.level;
}
}
return null;
}
这段的逻辑是他会去找最初的RootLogger 去获取RootLogger里面的设置的log等级信息,看下这张截图就可以很明显
而RootLogger中的消息等级在初始化LoggManager的过程将配置文件中的Root下的Level设定的值给设置到RootLogger中,当设定的为Level等级和实际要输出的Level等级不一致的时候,就不会进行记录,实际测试也是这样
设定RootLogger中的Level的值代码如下
protected void parseRoot(Element rootElement) {
Logger root = this.repository.getRootLogger();
synchronized(root){}
try {
this.parseChildrenOfLoggerElement(rootElement, root, true);
} catch (Throwable var5) {
throw var5;
}
}
在parseChildrenOfLoggerElement方法中可以看到有个parseLevel的方法
所以info方法只能写定义为INFO的日志
关于写日志的过程,后面会继续深挖学习,加油学习!