java日志(二):JUL源码分析

jdk自带的log工具java.util.logging的简单分析



加载过程

Logger.getLogger(“className”)加载整个日志的过程简单概括如下

1. 生成LogManager

  • LogManager静态初始化
    • 先读取系统属性”java.util.logging.manager”初始化LogManager
    • 若无此属性,则默认通过构造方法初始化LogManager
  • 通过getLogManager方法获取LogManager(读取longging配置的过程)
    • 加载系统属性”java.util.logging.config.class”,有就直接返回
    • 加载系统属性”java.util.logging.config.file”,有就加载这个配置
    • 若无则默认加载java.home下 “lib/logging.properties”配置文件
    • 返回LogManager

2. 通过LogManager的demandLogger方法返回Logger

  • 若已创建该logger,直接返回该logger
  • 若未创建过,则创建,并添加到LogManager中,返回该logger

源码解析

先上入口

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中,就有这样的配置

  • 修改属性”java.util.logging.manager”,自定义LogManager,使用自己的ClassLoaderLogManager:
# -z 表示判断指定的变量是否存在值
if [ -z "$LOGGING_MANAGER" ]; then
  LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
fi
  • 修改属性”java.util.logging.config.file”,自定义配置文件, 使用自己的%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