【基于 jdk 1.8】
【基本使用】
import java.util.logging.Logger;
public class UserController {
private static Logger log = Logger.getLogger(Test.class.getName());
public void login(){
//设置最低log级别
//log.setLevel(Level.FINEST);
log.info("in userController login");
}
}
jdk logger的配置路径
{jdk_home}/jre/lib/logging.properties
从配置可以知道
【默认配置】
1、默认处理器handlers是一个ConsoleHandler,控制台输出
2、默认所有log最低级别是INFO,ConsoleHandler的level和formatter配置
3、最下面可以配置具体包的log最低级别 level (下面有分析为什么这样配置)
com.xyz.foo.level = SEVERE
只有log.info()和log.warning()快捷方法,其他级别的log需要调用log.log(), info()与warning底层也是自动调用的log()方法
import java.util.logging.Level;
import java.util.logging.Logger;
public class UserController {
private static Logger log = Logger.getLogger(Test.class.getName());
public void login(){
//等同于log.info("in userController login");
log.log(Level.INFO,"in userController login");
}
}
jdk的logger的所有级别
SEVERE (highest value)
WARNING
INFO
CONFIG
FINE
FINER
FINEST (lowest value)
【核心流程概述】
1、jdk的logging系统是由一个LogManager管理的,
2、LogManager内部类LoggerContext管理各个context下不同name的logger实例对象(LoggerContext的namedLoggers [hashtable] 快速判断同名实例,LoggerContext的root [LogNode]【树形保存logger】,保证类包下的log层次结构,和继承父类log的级别level和处理handler)
3、每一个logger实例会有多个handlers,我们获取的是某一个name的logger实例
4、每一次的log.info()都是创建new一个LogRecord对象,然后迭代调用logger的loggerHandlers去处理LogRecord (handler.publish(record))
【简单调用模拟】【可以做个自己log包装类或logutil】
//log的ConsoleHandler模拟,同样相同的输出
import java.util.logging.*;
public class Test {
public static void main(String[] args) throws Exception{
//以下相当于log.info("test info log");
LogRecord lr = new LogRecord(Level.INFO,"test info log");
//设置调用类的名字
lr.setSourceClassName(Test.class.getName());
//ConsoleHandler的默认格式化,参看配置文件logging.properties
String msg = new SimpleFormatter().format(lr);
//设置xml格式log
//msg = new XMLFormatter().format(lr);
System.out.println(msg);
}
}
【原理分析】
Logger log = Logger.getLogger(Test.class.getName());
getLogger()是使用LogManager.java通过参数获取一个Logger.java实例,相同name会取得统一对象
import java.util.logging.Logger;
public class Test {
private static Logger log = Logger.getLogger(Test.class.getName());
public static void main(String[] args) throws Exception{
Logger log = Logger.getLogger(Test.class.getName());
Logger log2 = Logger.getLogger("Test");
Logger log3 = Logger.getLogger("Test2");
System.out.println("同 名logger:" + (log == log2) );
System.out.println("不同名logger:"+ (log2 == log3) );
}
}
获取Logger.java实例,LogManager.demandLogger(),
Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
//根据name获取实例,同名会返回相同实例
Logger result = getLogger(name);
if (result == null) {
...新建处理
}
return result;
}
上面的新建会new logger,调用LoggerContext的addLocallogger()管理logger
//代码片段,大致调用过程
Logger newLogger = new Logger(name, resourceBundleName, caller, this, false);
addLogger(newLogger);
cx.addLocalLogger(logger);
//这里读取配置,加入新logger
synchronized boolean addLocalLogger(Logger logger, boolean addDefaultLoggersIfNeeded)
//放入LoggerContext的namedLoggers(hashtable)管理
namedLoggers.put(name, ref);
//读取logging.properties配置,这里就可以看出jdk默认配置的原理,比如加到配置下面的自定义包级别
Level level = owner.getLevelProperty(name + ".level", null);
getLogger(name)是防止同一name多个实例具体可以看java.util.logging.LogManager#getLogger (里面是调用java.util.logging.LogManager.LoggerContext#findLogger LoggerContext的 namedLoggers [hashtable])
一个logger实例就这样处理完了
log.info()调用过程比较简单
java.util.logging.Logger#info(java.lang.String)
java.util.logging.Logger#log(java.util.logging.Level, java.lang.String)
//对LogRecord设置一些属性
java.util.logging.Logger#doLog(java.util.logging.LogRecord)
//最终log处理
java.util.logging.Logger#log(java.util.logging.LogRecord)