Java日志体系---JUL源码解析
在软件开发过程中日志记录是必不可少的一部分。作为一个合格的软件开发者(Java),有必要深入了解一下Java的日志体系。本文将详细的介绍我在学习JUL时的一些方法和心得。
一般来说日志分为两种:业务日志和异常日志,使用日志我们希望能达到以下目标:
- 对程序运行情况的记录和监控;
- 在必要时可详细了解程序内部的运行状态;
- 对系统性能的影响尽量小;
个人觉得记录日志最重要的目的在于记录程序运行的信息,以方便分析程序的运行结果和定位问题。
概览(查、看、跑、造、比、买)
梳理了自己学习JUL的过程,并进行总结,以便在学习其他技术时也能使用这种学习方法。
- 查API
查看官方API,对这个技术的整体以及核心类的介绍有所了解。 - 看源码
根据API的介绍查看核心类的代码,去繁存简只研究与日志相关的部分
(例如,本文将忽略安全、多线程、本地化相关的代码 ) - 跑程序
从helloworld开始,将核心功能串联起来。 - 造轮子
梳理清楚逻辑后,关闭浏览器(这时候应该放下资料),打开IDE按照自己的理解实现这个功能。 - 货比三家
研究同种类型技术的实现方式。 - 买定离手
对比不同技术的性能和适用场景,技术选型。
查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的真面目。
看源码
找到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<LogManager.LoggerWeakRef> 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<Handler> handlers =new CopyOnWriteArrayList<>();private volatile boolean useParentHandlers = true;private volatile Filter filter;private volatile Level levelObject;private volatile int levelValue; // current effective level valueprivate 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的namepublic 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) {//获取需要的Loggerreturn demandLogger(name, null, Reflection.getCallerClass());}private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {//初始化LogManagerLogManager manager = LogManager.getLogManager();
// SecurityManager sm = System.getSecurityManager();
// if (sm != null) {
// if (caller.getClassLoader() == null) {
// return manager.demandSystemLogger(name, resourceBundleName);
// }
// }////通过LogManager获取需要的Loggerreturn manager.demandLogger(name, resourceBundleName, caller);}//--------------------Filter的方法----------------------public void setFilter(Filter newFilter) throws SecurityException {//校验权限和初始化LogManagercheckPermission();filter = newFilter;}public Filter getFilter() {return filter;}//--------------------记录日志的方法----------------------// 核心的日志操作方法,handler.publish(record);将LogRecord添加到handlers中public void log(LogRecord record) {//校验Levelif (!isLoggable(record.getLevel())) {return;}//校验FilterFilter 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);//忽略LogBundlelog(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;}//校验Levelpublic 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 handlerhandler.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 loggersif (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<LogManager.LoggerWeakRef> iter = parent.kids.iterator(); iter.hasNext(); ) {ref = iter.next();Logger kid = ref.get();if (kid == this) {// ref is used down below to complete the reparentingiter.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 parentref = 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<LogManager.LoggerWeakRef> kids;LogManager manager;CopyOnWriteArrayList<Handler> handlers;Filter filter;Level levelObject;
Logger中的核心方法:
//-----------------------------获取日志个工厂方法---------------------------------public static Logger getLogger(String name) {//获取需要的Loggerreturn demandLogger(name, null, Reflection.getCallerClass());}private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {//初始化LogManagerLogManager manager = LogManager.getLogManager();
// SecurityManager sm = System.getSecurityManager();
// if (sm != null) {
// if (caller.getClassLoader() == null) {
// return manager.demandSystemLogger(name, resourceBundleName);
// }
// }////通过LogManager获取需要的Loggerreturn 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);//忽略LogBundlelog(lr);}// 核心的日志操作方法,handler.publish(record);将LogRecord添加到handlers中public void log(LogRecord record) {//校验Levelif (!isLoggable(record.getLevel())) {return;}//校验FilterFilter 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 LevelLevel 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<String, List<KnownLevel>> nameToLevels = new HashMap<>();private static Map<Integer, List<KnownLevel>> intToLevels = new HashMap<>();final Level levelObject; // instance of Level class or Level subclassfinal Level mirroredLevel; // mirror of the custom LevelKnownLevel(Level l) {this.levelObject = l;if (l.getClass() == Level.class) {this.mirroredLevel = l;} else {// this mirrored level object is hiddenthis.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 instanceKnownLevel o = new KnownLevel(l);List<KnownLevel> 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<KnownLevel> list = nameToLevels.get(name);if (list != null) {return list.get(0);}return null;}static synchronized KnownLevel findByValue(int value) {List<KnownLevel> 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 parametersif (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<Object,Integer> listenerMap = new HashMap<>();//负责存储监听// LoggerContext for system loggers and user loggersprivate 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 readConfigurationprivate 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<LogManager>() {@Overridepublic 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();}//确保初始化一个全局的LogManagerif (mgr == null) {mgr = new LogManager();}return mgr;}});}
LogManager的核心内部类:
//LoggerContext日志上下文信息包括:Hashtable<String,LoggerWeakRef> namedLoggers和LogNode root;class LoggerContext {// Table of named Loggers that maps names to Loggers.private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();// Tree of named Loggersprivate 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<String> 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<String,LoggerWeakRef> namedLoggers和LogNode root;class LoggerContext {// Table of named Loggers that maps names to Loggers.private final Hashtable<String,LoggerWeakRef> namedLoggers = new Hashtable<>();// Tree of named Loggersprivate 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<String> 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("");}@Overridepublic void log(LogRecord record) {initializeGlobalHandlers();super.log(record);}@Overridepublic void addHandler(Handler h) {initializeGlobalHandlers();super.addHandler(h);}@Overridepublic void removeHandler(Handler h) {initializeGlobalHandlers();super.removeHandler(h);}@OverrideHandler[] 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 onceLogger 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.propertiespublic void readConfiguration(InputStream ins) throws IOException, SecurityException {checkPermission();reset();//关闭原有handlersprops.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<Object,Integer> 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<Object,Integer> 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<Object>() {@Overridepublic 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!
这样整个流程就很清晰了(更详细的代码追踪参考链接: 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)。
接口类日志框架:JCL,Slf4j
日志实现框架:JUL,Log4j,Log4j2,Logback
以上是常用的日志框架,相关的桥接包不再赘述。
买定离手
目前就性能而言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日志体系---JUL源码解析相关推荐
- Java集合之TreeMap源码解析上篇
上期回顾 上期我从树型结构谈到了红黑树的概念以及自平衡的各种变化(指路上期←戳),本期我将会对TreeMap结合红黑树理论进行解读. 首先,我们先来回忆一下红黑树的5条基本规则. 1.结点是红色或者黑 ...
- java arraylist 赋值_ArrayList源码解析,老哥,来一起复习一哈?
点击上方"码农沉思录",选择"设为星标" 优质文章,及时送达 前言 JDK源码解析系列文章,都是基于JDK8分析的,虽然JDK14已经出来,但是JDK8我还不会 ...
- Java并发之Semaphore源码解析
Semaphore 前情提要 在学习本章前,需要先了解ReentrantLock源码解析,ReentrantLock源码解析里介绍的方法有很多是本章的铺垫.下面,我们进入本章正题Semaphore. ...
- Spring的日志模块-spring-jcl源码解析以及Java的日志框架
1. spring-jcl模块结构 可以看到,这个模块的包名是apache的commons.logging,因为这是spring团队对common.logging 进行了重写,所以包名还保留之前的ap ...
- 爆火的Java面试题-kafka源码解析与实战豆瓣
1 基础 为什么 Java 中只有值传递? int 范围?float 范围? hashCode 与 equals,什么关系? String StringBuffer 和 StringBuilder 的 ...
- java日志模块_Java源码初探_logging日志模块实现
一.用途 程序中记录日志,打印到控制台.文件等方式,记录过程可根据日志级别做筛选,日志格式可以自定义. 大概结构如下所示: 简要说明各个模块: (1) LogManager:管理LoggerConte ...
- Java FileReader InputStreamReader类源码解析
FileReader 前面介绍FileInputStream的时候提到过,它是从文件读取字节,如果要从文件读取字符的话可以使用FileReader.FileReader是可以便利读取字符文件的类,构造 ...
- 日志 note_深入源码解析日志框架Log4j2(二)
异步 AsyncAppender log4j2突出于其他日志的优势,异步日志实现.我们先从日志打印看进去.找到Logger,随便找一个log日志的方法. public void debug(fin ...
- 深入Java集合ArrayList的源码解析
现在由大恶人付有杰来从增删改查几个角度轻度解析ArrayList的源码 首先ArrayList的底层数据结构非常简单,就是一个数组. 从源码第115行我们可以得出信息,他的默认数组长度是10. /** ...
- Java集合---Arrays类源码解析
一.Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型: ...
最新文章
- MyEclipse 中文乱码
- Python:安装dlib库
- java可视化模板——java可视化操作步骤
- 【模板】 最小生成树
- myysql 不能远程访问的解决办法
- (转载)windows server 2003的注意事项
- php修改隐藏域非法提交,PHP get、post变量放在表单隐藏域示例
- 南京理工大学计算机专业考研,2020南京理工大学计算机考研初试科目、参考书目、复试详情汇总...
- 官宣了!受疫情影响,程序员可免费领这些!
- 多级联动下拉java,下拉列表多级联动前端实现
- 利用计算机的图形能力来进行设计工作的是,计算机一级MSOffice考试巩固练习题...
- Web App开发入门
- windows如何安装pycharm2022版本?pycharm如何安装汉化语言包{www.423zy.com}
- python库itchat介绍(自动登录微信,自动回复消息)
- 【WebDriver】WebDriverWait 用法代码
- 使用ffmpeg进行音频采样率转换
- 本质矩阵E的内在性质证明
- The Thirty-fifth Of Word-Day
- vscode和sqlite3的环境配置
- php广告联盟,PHPCPS广告联盟系统