jdk自带的log工具java.util.logging
的简单分析
Logger.getLogger(“className”)加载整个日志的过程简单概括如下
先上入口
import java.util.logging.Logger;
public class JULTest {
private static final Logger log = Logger.getLogger(JULTest.class.getName()); // 从这里开始
public static void main(String[] args) {
log.info(log.getClass().getName());
}
}
从Logger.getLogger(XXX.class.getName())
开始,进入java.util.logging.Logger
源码中可以看到
public static Logger getLogger(String name) {
// 直接调用了另一个方法
return demandLogger(name, null, Reflection.getCallerClass());
}
private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
LogManager manager = LogManager.getLogManager(); // 第一步就是生成LogManager
SecurityManager sm = System.getSecurityManager();
if (sm != null && !LoggerHelper.disableCallerCheck) {
if (caller.getClassLoader() == null) {
return manager.demandSystemLogger(name, resourceBundleName);
}
}
return manager.demandLogger(name, resourceBundleName, caller); // 最后通过这个方法返回Logger
}
接下来看看LogManager.getLogManager()
,不过在此之前,首先要注意,LogManager
有一个静态初始化方法:
static {
// AccessController.doPrivileged()方法表示执行一些特权内容,跟Linux中的登陆有点类似。
// Linux中的login程序必须访问password文件从而获得用户授权信息,但是用户不能随意的访问password文件。
// 所以,login程序比较特殊,它不管被哪个用户所调用,都具有root的权限,而不是要登陆的用户权限。
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
String cname = null;
try {
cname = System.getProperty("java.util.logging.manager");
// 没有设置过,cname得到的为null
if (cname != null) { // 这里就不执行了
// 如果设置过,则会加载设置的LogManager
try {
Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
manager = (LogManager) clz.newInstance();
} catch (ClassNotFoundException ex) {
Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
manager = (LogManager) clz.newInstance();
}
}
} catch (Exception ex) {
System.err.println("Could not load Logmanager \"" + cname + "\"");
ex.printStackTrace();
}
if (manager == null) {
// 在这里才真正创建了Logmanager, 并赋值给LogManager的类成员manager
manager = new LogManager();
}
// 生成一个根Logger,添加到manager里
manager.rootLogger = manager.new RootLogger();
manager.addLogger(manager.rootLogger);
manager.systemContext.addLocalLogger(manager.rootLogger, false);
manager.userContext.addLocalLogger(manager.rootLogger, false);
Logger.global.setLogManager(manager);
manager.addLogger(Logger.global);
manager.systemContext.addLocalLogger(Logger.global, false);
manager.userContext.addLocalLogger(Logger.global, false);
return null;
}
});
}
LogManager
初始化方法执行完了之后,再来看LogManager.getLogManager()
public static LogManager getLogManager() {
if (manager != null) { // manager为类变量,只有一份。已经初始化了,不会为空
manager.readPrimordialConfiguration(); // 读取原始配置
}
return manager;
}
// 下面主要是加载logging的属性
private void readPrimordialConfiguration() {
if (!readPrimordialConfiguration) { // LogManager的私有变量,刚开始为false
synchronized (this) {
if (!readPrimordialConfiguration) {
// 如果 System.in/out/err 为 null, 说明我们还在启动阶段
if (System.out == null) {
return;
}
readPrimordialConfiguration = true; // 设置为已读取过配置文件
try {
// 又是特权函数
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
public Void run() throws Exception {
readConfiguration(); // 读取配置
// Platform loggers 开始代理 java.util.logging.Logger
sun.util.logging.PlatformLogger.redirectPlatformLoggers();
return null;
}
});
} catch (Exception ex) {
}
}
}
}
}
// 重新初始化 logging properties,重新读取 logging 的配置
// 先依此尝试加载系统属性"ava.util.logging.config.file"和""java.util.logging.config.class"
// 若找不到则加载(即默认加载) java_home 下面的 "lib/logging.properties"配置文件(可以在 jre/lib 目录下面查看其内容)
public void readConfiguration() throws IOException, SecurityException {
checkPermission();
String cname = System.getProperty("java.util.logging.config.class");
if (cname != null) {
try {
try {
Class clz = ClassLoader.getSystemClassLoader().loadClass(cname);
clz.newInstance();
return;
} catch (ClassNotFoundException ex) {
Class clz = Thread.currentThread().getContextClassLoader().loadClass(cname);
clz.newInstance();
return;
}
} catch (Exception ex) {
System.err.println("Logging configuration class \"" + cname + "\" failed");
System.err.println("" + ex);
}
}
String fname = System.getProperty("java.util.logging.config.file");
if (fname == null) {
fname = System.getProperty("java.home"); // 这个必须有
if (fname == null) {
throw new Error("Can't find java.home ??");
}
File f = new File(fname, "lib");
f = new File(f, "logging.properties");
fname = f.getCanonicalPath();
}
InputStream in = new FileInputStream(fname);
BufferedInputStream bin = new BufferedInputStream(in);
try {
readConfiguration(bin);
} finally {
if (in != null) {
in.close();
}
}
}
public void readConfiguration(InputStream ins) throws IOException, SecurityException {
checkPermission();
reset();
props.load(ins);
String names[] = parseClassNames("config");
for (int i = 0; i < names.length; i++) {
String word = names[i];
try {
Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
clz.newInstance();
} catch (Exception ex) {
System.err.println("Can't load config class \"" + word + "\"");
System.err.println("" + ex);
}
}
// 设置logManager中已有logger的级别
setLevelsOnExistingLoggers();
// 使用 PropertyChangeSupport 的 firePropertyChange 方法对属性文件的变动来做处理
changes.firePropertyChange(null, null, null);
synchronized (this) {
initializedGlobalHandlers = false;
}
}
LogManager.getLogManager
分析完了,再看看mananer是怎么返回Logger的
Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
// 如果给定name的logger已经创建过了,则返回原来创建的,保证同name的logger只有一个
Logger result = getLogger(name);
if (result == null) {
// 只有之前没创建过的logger才会被新创建,然后添加到logManager中。
Logger newLogger = new Logger(name, resourceBundleName, caller, false);
do {
if (addLogger(newLogger)) { // 在这里才真正加载一个新创建的logger
return newLogger;
}
result = getLogger(name);
} while (result == null);
}
return result;
}
凡是有加载系统属性的地方,就提供了用户自定义的接口,比如上面的”java.util.logging.manager”、 “java.util.logging.config.class”、”java.util.logging.config.file”,用户可以自己设置这些系统属性来进行自定义。
比如在tomcat的启动文件catalina.sh
中,就有这样的配置
LogManager
,使用自己的ClassLoaderLogManager
:# -z 表示判断指定的变量是否存在值
if [ -z "$LOGGING_MANAGER" ]; then
LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
fi
%CATALINA_BASE%\conf\logging.properties
文件:if [ -z "$LOGGING_CONFIG" ]; then
if [ -r "$CATALINA_BASE"/conf/logging.properties ]; then
LOGGING_CONFIG="-Djava.util.logging.config.file=$CATALINA_BASE/conf/logging.properties"
else
LOGGING_CONFIG="-Dnop"
fi
fi