Java日志体系---JUL源码解析

在软件开发过程中日志记录是必不可少的一部分。作为一个合格的软件开发者(Java),有必要深入了解一下Java的日志体系。本文将详细的介绍我在学习JUL时的一些方法和心得。

一般来说日志分为两种:业务日志和异常日志,使用日志我们希望能达到以下目标:

  • 对程序运行情况的记录和监控;
  • 在必要时可详细了解程序内部的运行状态;
  • 对系统性能的影响尽量小;

个人觉得记录日志最重要的目的在于记录程序运行的信息,以方便分析程序的运行结果和定位问题。

概览(查、看、跑、造、比、买)

梳理了自己学习JUL的过程,并进行总结,以便在学习其他技术时也能使用这种学习方法。

  1. 查API
    查看官方API,对这个技术的整体以及核心类的介绍有所了解。
  2. 看源码
    根据API的介绍查看核心类的代码,去繁存简只研究与日志相关的部分
    (例如,本文将忽略安全、多线程、本地化相关的代码 )
  3. 跑程序
    从helloworld开始,将核心功能串联起来。
  4. 造轮子
    梳理清楚逻辑后,关闭浏览器(这时候应该放下资料),打开IDE按照自己的理解实现这个功能。
  5. 货比三家
    研究同种类型技术的实现方式。
  6. 买定离手
    对比不同技术的性能和适用场景,技术选型。

查API

软件包 java.util.logging 的描述
提供 JavaTM 2 平台核心日志工具的类和接口。Logging API 的中心目标是支持在客户站点进行软件的维护和服务。
使用日志有 4 个主要目标:
1.由最终用户和系统管理员进行问题诊断。这由简单的常见问题日志组成,可在本地解决或跟踪这些问题,如资源不足、安全失败和简单的配置错误。
2.由现场服务工程师进行问题诊断。现场服务工程师使用的日志信息可以相当复杂和冗长,远超过系统管理员的要求。通常,这样的信息需要特定子系统中的额外日志记录。
3.由开发组织进行问题诊断。在现场出现问题时,必须将捕获的日志信息返回到原开发团队以供诊断。此日志信息可能非常详细,并且相当费解。这样的信息可能包括对特定子系统进行内部执行的详细跟踪。
4.由开发人员进行问题诊断。Logging API 还可以用来帮助调试正在开发的应用程序。这可能包括由目标应用程序产生的日志信息,以及由低级别的库产生的日志信息。但是要注意,虽然这样使用非常合理,但是 Logging API 并不用于代替开发环境中已经存在的调试和解析工具。
此包的关键元素包括
Logger:应用程序进行 logging 调用的主要实体。Logger 对象用来记录特定系统或应用程序组件的日志消息。
LogRecord:用于在 logging 框架和单独的日志处理程序之间传递 logging 请求。
Handler:将 LogRecord 对象导出到各种目的地,包括内存、输出流、控制台、文件和套接字。为此有各种的 Handler 子类。其他 Handler 可能由第三方开发并在核心平台的顶层实现。
Level:定义一组可以用来控制 logging 输出的标准 logging 级别。可以配置程序为某些级别输出 logging,而同时忽略其他输出。
Filter:为所记录的日志提供日志级别控制以外的细粒度控制。Logging API 支持通用的过滤器机制,该机制允许应用程序代码附加任意的过滤器以控制 logging 输出。
Formatter:为格式化 LogRecord 对象提供支持。此包包括的两个格式器 SimpleFormatter 和 XMLFormatter 分别用于格式化纯文本或 XML 中的日志记录。与 Handler 一样,其他 Formatter 可能由第三方开发。
Logging API 提供静态和动态的配置控制。静态控制使现场服务人员可以建立特定的配置,然后重新启动带有新 logging 设置的应用程序。动态控制允许对当前正在运行的系统内的 logging 配置进行更新。API 也允许对不同的系统功能领域启用或禁用 logging。例如,现场服务工程师可能对跟踪所有 AWT 事件感兴趣,但是不会对套接字事件或内存管理感兴趣。

从JDK(java.util.logging)的API中可以看出:

  • Logger是用来记录日志的,
  • LogRecord是用来传递日志的,
  • Handler是用来导出(处理)日志的,
  • Level是用来定义日志级别的(控制日志输出),
  • Filter提供了日志级别之外更细粒度的控制,
  • Formatter是用来格式化LogRecord的,
  • 此外还有一个单一的全局的LogManager维护Logger和管理日志。

JUL主要由这七个核心类或接口组成,再有就是一些子类或者实现类。API中关于这几个类或接口的类和方法的详细介绍这里不再复述。
现在我们已经对JUL有了一个整体的了解,并且对核心类或接口的功能职责也有了初步了解,接下就该结合代码看看JUL的真面目。
Java日志体系---JUL源码解析_第1张图片

看源码

找到rt.jar,打开java.util.logging包查看每个类的源代码,结合JUL流程示意图逐个介绍。
1.Logger类

package java.util.logging;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import sun.reflect.Reflection;
public class Logger {
    //------------------------------属性------------------------------------------
    private String name;//日志名称
    private static final int offValue = Level.OFF.intValue();
    // We keep weak references from parents to children, but strong
    // references from children to parents.
    private volatile Logger parent;    // our nearest parent.
    private ArrayList kids;   // WeakReferences to loggers that have us as parent
    //内部类LoggerBundle相关部分忽略

    private volatile LogManager manager;

    private static final Handler emptyHandlers[] = new Handler[0];
    private final CopyOnWriteArrayList handlers =
            new CopyOnWriteArrayList<>();
    private volatile boolean useParentHandlers = true;

    private volatile Filter filter;

    private volatile Level levelObject;
    private volatile int levelValue;  // current effective level value

    private static final Object treeLock = new Object();
    private final boolean isSystemLogger;

    //------------------------------构造器------------------------------------------
  //只保留一个简单的私有构造器
    private Logger(String name) {
        // The manager field is not initialized here.
        this.name = name;
        this.isSystemLogger = true;
        levelValue = Level.INFO.intValue();//初始化级别INFO
    }
    //------------------------------方法------------------------------------------
    //获取Logger的name
    public String getName() {
        return name;
    }
    // It is called from LoggerContext.addLocalLogger() when the logger
    // is actually added to a LogManager.
    //--------------设置和初始化LogManager,包权限的方法-------------
    void setLogManager(LogManager manager) {
        this.manager = manager;
    }
    private void checkPermission() throws SecurityException {
        if (manager == null) {
            // Complete initialization of the global Logger.
            manager = LogManager.getLogManager();
        }
        manager.checkPermission();
    }

    //-------------------获取Logger的工厂方法----------------------
    public static Logger getLogger(String name) {
        //获取需要的Logger
        return demandLogger(name, null, Reflection.getCallerClass());
    }
    private static Logger demandLogger(String name, String resourceBundleName, Class caller) {
        //初始化LogManager
        LogManager manager = LogManager.getLogManager();
//        SecurityManager sm = System.getSecurityManager();
//        if (sm != null) {
//            if (caller.getClassLoader() == null) {
//                return manager.demandSystemLogger(name, resourceBundleName);
//            }
//        }
        //
        //通过LogManager获取需要的Logger
        return manager.demandLogger(name, resourceBundleName, caller);
    }

    //--------------------Filter的方法----------------------
    public void setFilter(Filter newFilter) throws SecurityException {
        //校验权限和初始化LogManager
        checkPermission();
        filter = newFilter;
    }
    public Filter getFilter() {
        return filter;
    }

    //--------------------记录日志的方法----------------------
    // 核心的日志操作方法,handler.publish(record);将LogRecord添加到handlers中
    public void log(LogRecord record) {
        //校验Level
        if (!isLoggable(record.getLevel())) {
            return;
        }
        //校验Filter
        Filter theFilter = filter;
        if (theFilter != null && !theFilter.isLoggable(record)) {
            return;
        }
        Logger logger = this;
        while (logger != null) {
            final Handler[] loggerHandlers = isSystemLogger
                    ? logger.accessCheckedHandlers()
                    : logger.getHandlers();

            for (Handler handler : loggerHandlers) {
                handler.publish(record);
            }
            final boolean useParentHdls = isSystemLogger
                    ? logger.useParentHandlers
                    : logger.getUseParentHandlers();

            if (!useParentHdls) {
                break;
            }
            logger = isSystemLogger ? logger.parent : logger.getParent();
        }
    }
    private void doLog(LogRecord lr) {
        lr.setLoggerName(name);
        //忽略LogBundle
        log(lr);
    }
    public void log(Level level, String msg) {
        if (!isLoggable(level)) {
            return;
        }
        LogRecord lr = new LogRecord(level, msg);
        doLog(lr);
    }
    public void log(Level level, String msg, Object param1) {
        if (!isLoggable(level)) {
            return;
        }
        LogRecord lr = new LogRecord(level, msg);
        Object params[] = { param1 };
        lr.setParameters(params);
        doLog(lr);
    }
    public void log(Level level, String msg, Object params[]) {
        if (!isLoggable(level)) {
            return;
        }
        LogRecord lr = new LogRecord(level, msg);
        lr.setParameters(params);
        doLog(lr);
    }

    //------------------日志级别相关-----------------------
    public void setLevel(Level newLevel) throws SecurityException {
        checkPermission();
        synchronized (treeLock) {
            levelObject = newLevel;
            updateEffectiveLevel();
        }
    }
    final boolean isLevelInitialized() {
        return levelObject != null;
    }
    public Level getLevel() {
        return levelObject;
    }
    //校验Level
    public boolean isLoggable(Level level) {
        if (level.intValue() < levelValue || levelValue == offValue) {
            return false;
        }
        return true;
    }
    //------------------Handler相关-----------------------
    public void addHandler(Handler handler) throws SecurityException {
        // Check for null handler
        handler.getClass();
        checkPermission();
        handlers.add(handler);
    }
    public void removeHandler(Handler handler) throws SecurityException {
        checkPermission();
        if (handler == null) {
            return;
        }
        handlers.remove(handler);
    }
    public Handler[] getHandlers() {
        return accessCheckedHandlers();
    }
    Handler[] accessCheckedHandlers() {
        return handlers.toArray(emptyHandlers);
    }

    //------------------父级日志相关-----------------------
    public Logger getParent() {
        return parent;
    }
    public void setParent(Logger parent) {
        if (parent == null) {
            throw new NullPointerException();
        }
        // check permission for all loggers, including anonymous loggers
        if (manager == null) {
            manager = LogManager.getLogManager();
        }
        manager.checkPermission();
        doSetParent(parent);
    }
    private void doSetParent(Logger newParent) {
        synchronized (treeLock) {
            // Remove ourself from any previous parent.
            LogManager.LoggerWeakRef ref = null;
            if (parent != null) {
                // assert parent.kids != null;
                for (Iterator iter = parent.kids.iterator(); iter.hasNext(); ) {
                    ref = iter.next();
                    Logger kid =  ref.get();
                    if (kid == this) {
                        // ref is used down below to complete the reparenting
                        iter.remove();
                        break;
                    } else {
                        ref = null;
                    }
                }
                // We have now removed ourself from our parents' kids.
            }
            // Set our new parent.
            parent = newParent;
            if (parent.kids == null) {
                parent.kids = new ArrayList<>(2);
            }
            if (ref == null) {
                // we didn't have a previous parent
                ref = manager.new LoggerWeakRef(this);
            }
            ref.setParentRef(new WeakReference<>(parent));
            parent.kids.add(ref);
            // As a result of the reparenting, the effective level
            // may have changed for us and our children.
            updateEffectiveLevel();
        }
    }
    private void updateEffectiveLevel() {
        // Figure out our current effective level.
        int newLevelValue;
        if (levelObject != null) {
            newLevelValue = levelObject.intValue();
        } else {
            if (parent != null) {
                newLevelValue = parent.levelValue;
            } else {
                // This may happen during initialization.
                newLevelValue = Level.INFO.intValue();
            }
        }
        // If our effective value hasn't changed, we're done.
        if (levelValue == newLevelValue) {
            return;
        }
        levelValue = newLevelValue;
        // Recursively update the level on each of our kids.
        if (kids != null) {
            for (int i = 0; i < kids.size(); i++) {
                LogManager.LoggerWeakRef ref = kids.get(i);
                Logger kid =  ref.get();
                if (kid != null) {
                    kid.updateEffectiveLevel();
                }
            }
        }
    }
}

Logger中的核心属性

 	String name;
 	Logger parent;
 	ArrayList kids;
	LogManager manager;
	CopyOnWriteArrayList handlers;
	Filter filter;
 	Level levelObject;

Logger中的核心方法

//-----------------------------获取日志个工厂方法---------------------------------
   public static Logger getLogger(String name) {
        //获取需要的Logger
        return demandLogger(name, null, Reflection.getCallerClass());
    }
    private static Logger demandLogger(String name, String resourceBundleName, Class caller) {
        //初始化LogManager
        LogManager manager = LogManager.getLogManager();
//        SecurityManager sm = System.getSecurityManager();
//        if (sm != null) {
//            if (caller.getClassLoader() == null) {
//                return manager.demandSystemLogger(name, resourceBundleName);
//            }
//        }
        //
        //通过LogManager获取需要的Logger
        return manager.demandLogger(name, resourceBundleName, caller);
    }

//-----------------------------记录日志的相关方法--------------------------------------
    public void log(Level level, String msg) {
        if (!isLoggable(level)) {
            return;
        }
        LogRecord lr = new LogRecord(level, msg);
        doLog(lr);
    }
  private void doLog(LogRecord lr) {
        lr.setLoggerName(name);
        //忽略LogBundle
        log(lr);
    }
    // 核心的日志操作方法,handler.publish(record);将LogRecord添加到handlers中
    public void log(LogRecord record) {
        //校验Level
        if (!isLoggable(record.getLevel())) {
            return;
        }
        //校验Filter
        Filter theFilter = filter;
        if (theFilter != null && !theFilter.isLoggable(record)) {
            return;
        }
        Logger logger = this;
        while (logger != null) {
            final Handler[] loggerHandlers = isSystemLogger
                    ? logger.accessCheckedHandlers()
                    : logger.getHandlers();

            for (Handler handler : loggerHandlers) {
                handler.publish(record);
            }
            final boolean useParentHdls = isSystemLogger
                    ? logger.useParentHandlers
                    : logger.getUseParentHandlers();

            if (!useParentHdls) {
                break;
            }
            logger = isSystemLogger ? logger.parent : logger.getParent();
        }
    }

梳理代码中的核心部分(忽略了安全、线程、ResourceBundle等部分),限于篇幅以下只列出核心的属性和方法。
Logger类中是JUL体系的核心,主要包括几个重要的属性和两个核心的方法,阅读起来不太复杂。而LogManager内容较多,放在最后介绍。
2.Level类
Level中的核心属性

	String name;
	int value;
	String resourceBundleName;
	Level OFF;
	Level WARNING;
	Level INFO;
	Level ALL;

Level中的核心方法:通过name获取Level

//-----------------------------核心方法------------------------------
    static Level findLevel(String name) {
        if (name == null) {
            throw new NullPointerException();
        }
        KnownLevel level;
        // Look for a known Level with the given non-localized name.
        level = KnownLevel.findByName(name);
        if (level != null) {
            return level.mirroredLevel;
        }
        try {
            int x = Integer.parseInt(name);
            level = KnownLevel.findByValue(x);
            if (level == null) {
                // add new Level
                Level levelObject = new Level(name, x);
                level = KnownLevel.findByValue(x);
            }
            return level.mirroredLevel;
        } catch (NumberFormatException ex) {
            // Not an integer.
            // Drop through.
        }
//        level = KnownLevel.findByLocalizedLevelName(name);
//        if (level != null) {
//            return level.mirroredLevel;
//        }
        return null;
    }

Level中的核心内部类:KnownLevel,日志级别的容器

static final class KnownLevel {
        private static Map> nameToLevels = new HashMap<>();
        private static Map> intToLevels = new HashMap<>();
        final Level levelObject;     // instance of Level class or Level subclass
        final Level mirroredLevel;   // mirror of the custom Level
        KnownLevel(Level l) {
            this.levelObject = l;
            if (l.getClass() == Level.class) {
                this.mirroredLevel = l;
            } else {
                // this mirrored level object is hidden
                this.mirroredLevel = new Level(l.name, l.value, l.resourceBundleName, false);
            }
        }
        static synchronized void add(Level l) {
            // the mirroredLevel object is always added to the list
            // before the custom Level instance
            KnownLevel o = new KnownLevel(l);
            List list = nameToLevels.get(l.name);
            if (list == null) {
                list = new ArrayList<>();
                nameToLevels.put(l.name, list);
            }
            list.add(o);
            list = intToLevels.get(l.value);
            if (list == null) {
                list = new ArrayList<>();
                intToLevels.put(l.value, list);
            }
            list.add(o);
        }
        static synchronized KnownLevel findByName(String name) {
            List list = nameToLevels.get(name);
            if (list != null) {
                return list.get(0);
            }
            return null;
        }
        static synchronized KnownLevel findByValue(int value) {
            List list = intToLevels.get(value);
            if (list != null) {
                return list.get(0);
            }
            return null;
        }
    }

Level类中属性和方法不多,重点在KnownLevel这个内部容器类。
3.LogRecord类
LogRecord的核心属性 :

	private static final AtomicLong globalSequenceNumber=new AtomicLong(0);
	private long sequenceNumber;
	private Level level;
	private String message;
	private String loggerName;
	private transient Object parameters[];
	private String sourceClassName;
	private String sourceMethodName;

LogRecord的构造器:

 public LogRecord(Level level, String message){
        this.level = level;
        this.message=message;
        this.sequenceNumber=globalSequenceNumber.getAndIncrement();
    }

LogRecord类相对简单,值得关注的是属性和构造器。
4.Filter接口(函数式接口)

public boolean isLoggable(LogRecord record);

主要是验证LogReocord是否能够继续传递。

5.Handler抽象类: 处理LogRecord并输出

Handler的核心属性:

	private static final int offValue = Level.OFF.intValue();
	private final LogManager manager = LogManager.getLogManager();
	private volatile Filter filter;
	private volatile Formatter formatter;
	private volatile Level logLevel = Level.ALL;

Handler的核心方法:

	//---------------------抽象方法------------------------
 	public abstract void publish(LogRecord record);
    public abstract void flush();
    public abstract void close() throws SecurityException;
	//--------------------主要方法--------------------------
	 public boolean isLoggable(LogRecord record) {
        final int levelValue = getLevel().intValue();
        if (record.getLevel().intValue() < levelValue || levelValue == offValue) {
            return false;
        }
        final Filter filter = getFilter();
        if (filter == null) {
            return true;
        }
        return filter.isLoggable(record);
    }
   

Handler是抽象类,JUL中提供的子类有MemoryHandler, StreamHandler。
6.Formatter抽象类

Formatter的核心方法:

	public abstract String format(LogRecord record);

    public synchronized String formatMessage(LogRecord record) {
        String format = record.getMessage();
        // Do the formatting.
        try {
            Object parameters[] = record.getParameters();
            if (parameters == null || parameters.length == 0) {
                // No parameters.  Just return format string.
                return format;
            }
            // Is it a java.text style format?
            // Ideally we could match with
            // Pattern.compile("\\{\\d").matcher(format).find())
            // However the cost is 14% higher, so we cheaply check for
            // 1 of the first 4 parameters
            if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 ||
                    format.indexOf("{2") >=0|| format.indexOf("{3") >=0) {
                return java.text.MessageFormat.format(format, parameters);
            }
            return format;

        } catch (Exception ex) {
            // Formatting failed: use localized format string.
            return format;
        }
    }

Formatter抽象类JUL提供的子类有SimpleFormatter, XMLFormatter。

7.LogManager类

LogManager的核心属性:

    //---------------------------核心属性--------------------------------------
    private static final LogManager manager;// 全局的LogManager对象
    private volatile Properties props = new Properties(); //配置
    private final static Level defaultLevel = Level.INFO;//默认日志级别
    private final Map listenerMap = new HashMap<>();//负责存储监听
    // LoggerContext for system loggers and user loggers
    private final LoggerContext userContext = new LoggerContext();
    private volatile Logger rootLogger;//根日志
    private volatile boolean readPrimordialConfiguration;//读取原始配置
    // Have we initialized global (root) handlers yet?
    // This gets set to false in readConfiguration
    private boolean initializedGlobalHandlers = true;
    // True if JVM death is imminent and the exit hook has been called.
    private boolean deathImminent;

LogManager的静态代码块:

//---------------------------------静态代码块----------------------------------------
    static {
        manager = AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public LogManager run() {
                LogManager mgr = null;
                String cname = null;
                try {
                    cname = System.getProperty("java.util.logging.manager");
                    if (cname != null) {
                        try {
                            Class clz = ClassLoader.getSystemClassLoader()
                                    .loadClass(cname);
                            mgr = (LogManager) clz.newInstance();
                        } catch (ClassNotFoundException ex) {
                            Class clz = Thread.currentThread()
                                    .getContextClassLoader().loadClass(cname);
                            mgr = (LogManager) clz.newInstance();
                        }
                    }
                } catch (Exception ex) {
                    System.err.println("Could not load Logmanager \"" + cname + "\"");
                    ex.printStackTrace();
                }
                //确保初始化一个全局的LogManager
                if (mgr == null) {
                    mgr = new LogManager();
                }
                return mgr;
            }
        });
    }

LogManager的核心内部类:

 //LoggerContext日志上下文信息包括:Hashtable namedLoggers和LogNode root;
    class LoggerContext {
        // Table of named Loggers that maps names to Loggers.
        private final Hashtable namedLoggers = new Hashtable<>();
        // Tree of named Loggers
        private final LogNode root;
        private LoggerContext() {
            this.root = new LogNode(null, this);
        }
        final LogManager getOwner() {
            return LogManager.this;
        }
        final Logger getRootLogger() {
            return getOwner().rootLogger;
        }
        synchronized Logger findLogger(String name) {
            // ensure that this context is properly initialized before looking for loggers.
            LoggerWeakRef ref = namedLoggers.get(name);
            if (ref == null) {
                return null;
            }
            Logger logger = ref.get();
            return logger;
        }
        synchronized void removeLoggerRef(String name, LoggerWeakRef ref) {
            namedLoggers.remove(name, ref);
        }
        synchronized Enumeration getLoggerNames() {
            return namedLoggers.keys();
        }
        LogNode getNode(String name) {
            if (name == null || name.equals("")) {
                return root;
            }
            LogNode node = root;
            while (name.length() > 0) {
                int ix = name.indexOf(".");
                String head;
                if (ix > 0) {
                    head = name.substring(0, ix);
                    name = name.substring(ix + 1);
                } else {
                    head = name;
                    name = "";
                }
                if (node.children == null) {
                    node.children = new HashMap<>();
                }
                LogNode child = node.children.get(head);
                if (child == null) {
                    child = new LogNode(node, this);
                    node.children.put(head, child);
                }
                node = child;
            }
            return node;
        }
    }
 //LoggerContext日志上下文信息包括:Hashtable namedLoggers和LogNode root;
    class LoggerContext {
        // Table of named Loggers that maps names to Loggers.
        private final Hashtable namedLoggers = new Hashtable<>();
        // Tree of named Loggers
        private final LogNode root;
        private LoggerContext() {
            this.root = new LogNode(null, this);
        }
        final LogManager getOwner() {
            return LogManager.this;
        }
        final Logger getRootLogger() {
            return getOwner().rootLogger;
        }
        synchronized Logger findLogger(String name) {
            // ensure that this context is properly initialized before looking for loggers.
            LoggerWeakRef ref = namedLoggers.get(name);
            if (ref == null) {
                return null;
            }
            Logger logger = ref.get();
            return logger;
        }
        synchronized void removeLoggerRef(String name, LoggerWeakRef ref) {
            namedLoggers.remove(name, ref);
        }
        synchronized Enumeration getLoggerNames() {
            return namedLoggers.keys();
        }
        LogNode getNode(String name) {
            if (name == null || name.equals("")) {
                return root;
            }
            LogNode node = root;
            while (name.length() > 0) {
                int ix = name.indexOf(".");
                String head;
                if (ix > 0) {
                    head = name.substring(0, ix);
                    name = name.substring(ix + 1);
                } else {
                    head = name;
                    name = "";
                }
                if (node.children == null) {
                    node.children = new HashMap<>();
                }
                LogNode child = node.children.get(head);
                if (child == null) {
                    child = new LogNode(node, this);
                    node.children.put(head, child);
                }
                node = child;
            }
            return node;
        }
    }
  //根日志类
    private final class RootLogger extends Logger {
        private RootLogger() {
            super("");
        }
        @Override
        public void log(LogRecord record) {
            initializeGlobalHandlers();
            super.log(record);
        }
        @Override
        public void addHandler(Handler h) {
            initializeGlobalHandlers();
            super.addHandler(h);
        }
        @Override
        public void removeHandler(Handler h) {
            initializeGlobalHandlers();
            super.removeHandler(h);
        }
        @Override
        Handler[] accessCheckedHandlers() {
            initializeGlobalHandlers();
            return super.accessCheckedHandlers();
        }
    }
//配置改变监听和配置改变事件
    private static class Beans {
        private static final Class propertyChangeListenerClass =
                getClass("java.beans.PropertyChangeListener");
        private static final Class propertyChangeEventClass =
                getClass("java.beans.PropertyChangeEvent");
        private static final Method propertyChangeMethod =
                getMethod(propertyChangeListenerClass,
                        "propertyChange",
                        propertyChangeEventClass);
        private static final Constructor propertyEventCtor =
                getConstructor(propertyChangeEventClass,
                        Object.class,
                        String.class,
                        Object.class,
                        Object.class);
        private static Class getClass(String name) {
            try {
                return Class.forName(name, true, Beans.class.getClassLoader());
            } catch (ClassNotFoundException e) {
                return null;
            }
        }
        private static Constructor getConstructor(Class c, Class... types) {
            try {
                return (c == null) ? null : c.getDeclaredConstructor(types);
            } catch (NoSuchMethodException x) {
                throw new AssertionError(x);
            }
        }
        private static Method getMethod(Class c, String name, Class... types) {
            try {
                return (c == null) ? null : c.getMethod(name, types);
            } catch (NoSuchMethodException e) {
                throw new AssertionError(e);
            }
        }
        /**
         * Returns {@code true} if java.beans is present.
         */
        static boolean isBeansPresent() {
            return propertyChangeListenerClass != null &&
                    propertyChangeEventClass != null;
        }
        static Object newPropertyChangeEvent(Object source, String prop,
                                             Object oldValue, Object newValue)
        {
            try {
                return propertyEventCtor.newInstance(source, prop, oldValue, newValue);
            } catch (InstantiationException | IllegalAccessException x) {
                throw new AssertionError(x);
            } catch (InvocationTargetException x) {
                Throwable cause = x.getCause();
                if (cause instanceof Error)
                    throw (Error)cause;
                if (cause instanceof RuntimeException)
                    throw (RuntimeException)cause;
                throw new AssertionError(x);
            }
        }

        static void invokePropertyChange(Object listener, Object ev) {
            try {
                propertyChangeMethod.invoke(listener, ev);
            } catch (IllegalAccessException x) {
                throw new AssertionError(x);
            } catch (InvocationTargetException x) {
                Throwable cause = x.getCause();
                if (cause instanceof Error)
                    throw (Error)cause;
                if (cause instanceof RuntimeException)
                    throw (RuntimeException)cause;
                throw new AssertionError(x);
            }
        }
    }

LogManager的核心方法:

 	public static LogManager getLogManager() {
        if (manager != null) {
            //确保初始化
            manager.ensureLogManagerInitialized();
        }
        return manager;
    }
   Logger demandLogger(String name, String resourceBundleName, Class caller) {
        Logger result = getLogger(name);
        if (result == null) {
            // only allocate the new logger once
            Logger newLogger = new Logger(name);
            do {
                if (addLogger(newLogger)) {
                    // We successfully added the new Logger that we
                    // created above so return it without refetching.
                    return newLogger;
                }
                result = getLogger(name);
            } while (result == null);
        }
        return result;
    }
//读取配置
    public void readConfiguration() throws IOException, SecurityException {
        checkPermission();
        // if a configuration class is specified, load it and use it.
        //加载日志配置类
        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);
                // keep going and useful config file.
            }
        }
        //加载日志配置文件
        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();//获取规范路径
        }
        try (final InputStream in = new FileInputStream(fname)) {
            final BufferedInputStream bin = new BufferedInputStream(in);
            readConfiguration(bin);
        }
    }
    //读取配置文件jre_home/logging.properties
    public void readConfiguration(InputStream ins) throws IOException, SecurityException {
        checkPermission();
        reset();//关闭原有handlers
        props.load(ins);
        // Instantiate new configuration objects.
        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);
                // ex.printStackTrace();
            }
        }
        //给现有的日志设置最新的日志级别
        setLevelsOnExistingLoggers();
        Map listeners = null;
        synchronized (listenerMap) {
            if (!listenerMap.isEmpty())
                listeners = new HashMap<>(listenerMap);
        }
        if (listeners != null) {
            assert Beans.isBeansPresent();
            //修改配置的监听事件
            Object ev = Beans.newPropertyChangeEvent(LogManager.class, null, null, null);
            for (Map.Entry entry : listeners.entrySet()) {
                Object listener = entry.getKey();
                int count = entry.getValue().intValue();
                for (int i = 0; i < count; i++) {
                    Beans.invokePropertyChange(listener, ev);
                }
            }
        }
        // Note that we need to reinitialize global handles when they are first referenced.
        synchronized (this) {
            initializedGlobalHandlers = false;
        }
    }
	//------------------------------添加日志------------------------------
    public boolean addLogger(Logger logger) {
        final String name = logger.getName();
        if (name == null) {
            throw new NullPointerException();
        }
        loadLoggerHandlers(logger, name, name + ".handlers");
        return true;
    }
     //添加日志处理器
    private void loadLoggerHandlers(final Logger logger, final String name,
                                    final String handlersPropertyName)
    {
        AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Object run() {
                String names[] = parseClassNames(handlersPropertyName);
                for (int i = 0; i < names.length; i++) {
                    String word = names[i];
                    try {
                        Class clz = ClassLoader.getSystemClassLoader().loadClass(word);
                        Handler hdl = (Handler) clz.newInstance();
                        String levs = getProperty(word + ".level");
                        if (levs != null) {
                            Level l = Level.findLevel(levs);
                            if (l != null) {
                                hdl.setLevel(l);
                            } else {
                                // Probably a bad level. Drop through.
                                System.err.println("Can't set level for " + word);
                            }
                        }
                        logger.addHandler(hdl);
                    } catch (Exception ex) {
                        System.err.println("Can't load log handler \"" + word + "\"");
                        System.err.println("" + ex);
                        ex.printStackTrace();
                    }
                }
                return null;
            }
        });
    }

LogManager类内容比较多,梳理起来比较麻烦。核心有几点:LoggerContext,LoggerWeakRef,LogNode内部类记录、维护日志信息。
JUL中核心的类我们已经熟悉了,其中SimpleFormatter和StreamHandler相对简单不再赘述。

跑程序

API,源码都已经看过了,代码中不太明白的一定有不少,看完真是让人头大。不要方,下面才是重点,通过一个简单的HelloWorld程序将流程给捋顺了。

public class Test {
    public static void main(String[] args){
        Logger logger=Logger.getLogger("com.blue.Test");
        logger.log(Level.INFO,"MESSAGE:{0},{1}!",new Object[]{"HELLO","JUL"});
    }
}

运行结果:二月 15, 2019 4:59:19 下午 com.blue.Test main 信息: MESSAGE:HELLO,JUL!
Java日志体系---JUL源码解析_第2张图片
这样整个流程就很清晰了(更详细的代码追踪参考链接: https://pan.baidu.com/s/1Yxph2Y-yvwcs8D5p67kJAw 提取码: n9as)。

  • 示例中没有使用自定义的配置类和配置文件,采用JDK默认配置,路径jre/lib/logging.properties
    查看此文件:默认只使用java.util.logging.ConsoleHandler,
    而ConsoleHandler的默认级别为INFO,采用java.util.logging.SimpleFormatter,输出到System.err
   public ConsoleHandler() {
        sealed = false;
        configure();
        setOutputStream(System.err);
        sealed = true;
    }

造轮子

跑完程序,虽然逻辑会更加清晰,但还差点意思,我们需要更进一步——造个轮子。仿照JUL写一套日志实现(结合流程图和简化后的代码)。
限于篇幅原因代码不在此处展示,如感兴趣请访问(百度网盘链接: https://pan.baidu.com/s/1Vlqcs8mZxrbijCa1Hp-AIw 提取码: 4m7p)或JUL的简单日志实现。
注:①示例代码中配置文件读取部分可能需要调整
②如果封装成jar包时,读取jar内部的配置文件也有问题,建议将读取配置部分做适当修改。

货比三家

本文的重点是介绍JUL的源码学习,其他JAVA日志的信息只做简要介绍。

  • Jul (Java Util Logging),自Java1.4以来的官方日志实现。
  • Log4j Apache Log4j是一个基于Java的日志记录工具。它是由Ceki Gülcü首创的,现在则是Apache软件基金会的一个项目。 Log4j是几种Java日志框架之一。
  • Log4j 2 Apache Log4j 2是apache开发的一款Log4j的升级产品。
  • Logback 一套日志组件的实现(Slf4j阵营)。
  • Commons Logging Apache基金会所属的项目,是一套Java日志接口,之前叫Jakarta Commons Logging,后更名为Commons Logging。
  • Slf4j 类似于Commons Logging,是一套简易Java日志门面,本身并无日志的实现。(Simple Logging Facade for Java,缩写Slf4j)。

接口类日志框架JCLSlf4j
日志实现框架JULLog4jLog4j2Logback
以上是常用的日志框架,相关的桥接包不再赘述。

买定离手

目前就性能而言Log4j2更胜一筹,其次是Logback。
JUL和Log4j不推荐使用。
目前本人对 Log4j2和Logback都只是限于简单配置使用,以后深入研究后会补充此部分的内容。

总结

第一次研究源码花费的时间还是比较长的,这篇文章断断续续写了两周。一直想研究Java源码,却不知从何开始,这次在学习日志知识的时候选择分析JUL的源码,收获还是很大的。限于篇幅和精力问题后面部分介绍的过于简单,想要深入了解的可以浏览下面的参考资料。
整体来说JUL的核心是Logger类和LogManager类,其他都相对简单。

  • Logger包括Handler[],LogManager,Level,Filter,name
  • LogManager包括Properties,LogContext
  • LogRecord包括 Level,message,loggerName,sourceMethodName,sourceClassName
  • Handler包括Filter,Formatter,Handler相关实现类
  • Filter主要是验证LogRecord:isLoggable(LogRecord record)
  • Formatter和相关实现类
  • Level包括name,value,定义的几个默认级别

简单的概括就是七个类(接口),两条线(获取Logger读取配置,记录日志并输出)。

参考资料

Spring Boot(十)Logback和Log4j2集成与日志发展史
Log4j、Log4j 2、Logback、SFL4J、JUL、JCL的比较
Asynchronous Loggers for Low-Latency Logging
Java日志
Java常用日志框架介绍
Log日志规范

你可能感兴趣的:(java)