log4j源码分析
来源:互联网 发布:推广软件返现金 编辑:程序博客网 时间:2024/06/05 20:44
log4j源码分析
log4j仓库的地址https://github.com/apache/log4j.git
1.0 准备代码
测试配置文件:log4j.properties
log4j.debug=true
log4j.rootLogger = debug,stdout
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %m%n
log4j.logger.test = debug,stdout
测试类
public class LoggerTest {
Logger log = Logger.getLogger(getClass());
@Test
public void testDebug(){
System.out.println(“init”);
log.debug(“debug”);
}
}
1.1 核心类
类图
Level:日志级别;
Appender:日志输出;
Layout:日志格式;
AppenderAttachable:允许附加多个不同的日志输出对象
Category: Logger的父类,提供了日子记录的方法,debug等
Logger:日志记录类,提供记录日志记录对象的方法;
LoggingEvent:日志记录过程中产生的信息的抽象.
1.2 初始化
 时序图
LogManager:管理对象,负责log4j的初始化与实例的获取
DefaultRepositorySelector:实例仓库的选择器,提供了获取实例仓库的方法
LoggerRepository:Logger实例的仓库,提供了获取Logger的方法
LoggerFactory:Logger的工厂提供了创建Logger 的方法
工具类:
OptionHandler提供了所有选项设置之后的激活接口
Configurator提供了解析配置文件并填充LoggerRepository的方法
OptionConverter作为工具类,提供了解析配置文件,创建Configurator对象和实例化对象的方法(通过反射)
1.2.1 LogManager的静态代码完成对Log4j的初始化
public class LogManager { static { //初始化RootLogger并指定的日志级别DEBUG,初始化Log仓库 Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG)); //设置repositorySelector对象 repositorySelector = new DefaultRepositorySelector(h); String override = OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY, null); if (override == null || "false".equalsIgnoreCase(override)) { String configurationOptionStr = OptionConverter.getSystemProperty( DEFAULT_CONFIGURATION_KEY, null); String configuratorClassName = OptionConverter.getSystemProperty( CONFIGURATOR_CLASS_KEY, null); URL url = null; // 优先查找 "log4j.xml" 然后查找"log4j.properties" if (configurationOptionStr == null) { url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE); if (url == null) { // 然后查找"log4j.properties" url = Loader.getResource(DEFAULT_CONFIGURATION_FILE); } } else { try { url = new URL(configurationOptionStr); } catch (MalformedURLException ex) { url = Loader.getResource(configurationOptionStr); } } if (url != null) { LogLog.debug("Using URL [" + url + "] for automatic log4j configuration."); try { // 加载配置文件,创建Configurator对象,配置LoggerRepository对象 OptionConverter.selectAndConfigure(url, configuratorClassName, LogManager.getLoggerRepository()); } catch (NoClassDefFoundError e) { LogLog.warn("Error during default initialization", e); } } else { LogLog.debug("Could not find resource: [" + configurationOptionStr + "]."); } } else { LogLog.debug("Default initialization of overridden by " + DEFAULT_INIT_OVERRIDE_KEY + "property."); } }}
1.2.2 OptionConverter默认创建了PropertyConfigurator
public class OptionConverter { static public void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) { Configurator configurator = null; String filename = url.getFile(); if (clazz == null && filename != null && filename.endsWith(".xml")) { clazz = "org.apache.log4j.xml.DOMConfigurator"; } if (clazz != null) { LogLog.debug("Preferred configurator class: " + clazz); configurator = (Configurator) instantiateByClassName(clazz, Configurator.class, null); if (configurator == null) { LogLog.error("Could not instantiate configurator [" + clazz + "]."); return; } } else { //创建PropertyConfigurator对象 configurator = new PropertyConfigurator(); } //加载资源文件,配置Hierarchy(LoggerRepository)对象 configurator.doConfigure(url, hierarchy); }}
1.2.3通过PropertyConfigurator完成了对配置文件的解析
public class PropertyConfigurator implements Configurator { public void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) { Properties props = new Properties(); LogLog.debug("Reading configuration from URL " + configURL); InputStream istream = null; URLConnection uConn = null; try { uConn = configURL.openConnection(); uConn.setUseCaches(false); istream = uConn.getInputStream(); //加载Properties props.load(istream); } catch (Exception e) { if (e instanceof InterruptedIOException || e instanceof InterruptedException) { Thread.currentThread().interrupt(); } LogLog.error("Could not read configuration file from URL [" + configURL + "].", e); LogLog.error("Ignoring configuration file [" + configURL + "]."); return; } finally { if (istream != null) { try { istream.close(); } catch (InterruptedIOException ignore) { Thread.currentThread().interrupt(); } catch (IOException ignore) { } catch (RuntimeException ignore) { } } } //继续完成对Hierarchy的解析 doConfigure(props, hierarchy); } public void doConfigure(Properties properties, LoggerRepository hierarchy) { repository = hierarchy; String value = properties.getProperty(LogLog.DEBUG_KEY); if (value == null) { value = properties.getProperty("log4j.configDebug"); if (value != null) LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead."); } if (value != null) { LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true)); } String reset = properties.getProperty(RESET_KEY); if (reset != null && OptionConverter.toBoolean(reset, false)) { hierarchy.resetConfiguration(); } String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX, properties); if (thresholdStr != null) { hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr, (Level) Level.ALL)); LogLog.debug("Hierarchy threshold set to [" + hierarchy.getThreshold() + "]."); } //设置RootLogger configureRootCategory(properties, hierarchy); configureLoggerFactory(properties); //设置其他Logger对象 parseCatsAndRenderers(properties, hierarchy); LogLog.debug("Finished configuring."); registry.clear(); } void configureRootCategory(Properties props, LoggerRepository hierarchy) { String effectiveFrefix = ROOT_LOGGER_PREFIX; String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props); if (value == null) { value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props); effectiveFrefix = ROOT_CATEGORY_PREFIX; } if (value == null) LogLog.debug("Could not find root logger information. Is this OK?"); else { //获取RootLogger Logger root = hierarchy.getRootLogger(); synchronized (root) { parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value); } } } void parseCategory(Properties props, Logger logger, String optionKey, String loggerName, String value) { LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value + "]."); StringTokenizer st = new StringTokenizer(value, ","); if (!(value.startsWith(",") || value.equals(""))) { if (!st.hasMoreTokens()) return; String levelStr = st.nextToken(); LogLog.debug("Level token is [" + levelStr + "]."); if (INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) { if (loggerName.equals(INTERNAL_ROOT_NAME)) { LogLog.warn("The root logger cannot be set to null."); } else { logger.setLevel(null); } } else { //设置日志等级 logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG)); } LogLog.debug("Category " + loggerName + " set to " + logger.getLevel()); } //移除所有Appender logger.removeAllAppenders(); Appender appender; String appenderName; while (st.hasMoreTokens()) { appenderName = st.nextToken().trim(); if (appenderName == null || appenderName.equals(",")) continue; LogLog.debug("Parsing appender named \"" + appenderName + "\"."); //解析Appender appender = parseAppender(props, appenderName); if (appender != null) { //将Appender添加到Logger logger.addAppender(appender); } } } Appender parseAppender(Properties props, String appenderName) { Appender appender = registryGet(appenderName); if ((appender != null)) { LogLog.debug("Appender \"" + appenderName + "\" was already parsed."); return appender; } String prefix = APPENDER_PREFIX + appenderName; String layoutPrefix = prefix + ".layout"; //初始化Appender appender = (Appender) OptionConverter.instantiateByKey(props, prefix, org.apache.log4j.Appender.class, null); if (appender == null) { LogLog.error( "Could not instantiate appender named \"" + appenderName + "\"."); return null; } //设置Appender的Name appender.setName(appenderName); if (appender instanceof OptionHandler) { if (appender.requiresLayout()) { //初始化Layout Layout layout = (Layout) OptionConverter.instantiateByKey(props, layoutPrefix, Layout.class, null); if (layout != null) { //设置Appender的Layout appender.setLayout(layout); LogLog.debug("Parsing layout options for \"" + appenderName + "\"."); PropertySetter.setProperties(layout, props, layoutPrefix + "."); LogLog.debug("End of parsing for \"" + appenderName + "\"."); } } final String errorHandlerPrefix = prefix + ".errorhandler"; String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props); if (errorHandlerClass != null) { ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props, errorHandlerPrefix, ErrorHandler.class, null); if (eh != null) { appender.setErrorHandler(eh); LogLog.debug("Parsing errorhandler options for \"" + appenderName + "\"."); parseErrorHandler(eh, errorHandlerPrefix, props, repository); final Properties edited = new Properties(); final String[] keys = new String[]{ errorHandlerPrefix + "." + ROOT_REF, errorHandlerPrefix + "." + LOGGER_REF, errorHandlerPrefix + "." + APPENDER_REF_TAG }; for (Iterator iter = props.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry) iter.next(); int i = 0; for (; i < keys.length; i++) { if (keys[i].equals(entry.getKey())) break; } if (i == keys.length) { edited.put(entry.getKey(), entry.getValue()); } } PropertySetter.setProperties(eh, edited, errorHandlerPrefix + "."); LogLog.debug("End of errorhandler parsing for \"" + appenderName + "\"."); } } //configureOptionHandler((OptionHandler) appender, prefix + ".", props); PropertySetter.setProperties(appender, props, prefix + "."); LogLog.debug("Parsed \"" + appenderName + "\" options."); } parseAppenderFilters(props, appenderName, appender); registryPut(appender); return appender; }}
1.3 运行
1.3.1 Logger类提提供了获取Logger对象的方法
public class Logger extends Category {staticpublic Logger getLogger(Class clazz) { //对应的Logger对象通过LogManager的静态方法获取return LogManager.getLogger(clazz.getName());}}
1.3.2 LogManager的默认RepositorySelector由DefaultRepositorySelector实现,提供了获取LoggerRepository的方法
public class LogManager {publicstatic Logger getLogger(final String name) {return getLoggerRepository().getLogger(name);}staticpublic LoggerRepository getLoggerRepository() {if (repositorySelector == null) {repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());guard = null;Exception ex = new IllegalStateException("Class invariant violation");String msg ="log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload.";if (isLikelySafeScenario(ex)) {LogLog.debug(msg, ex);} else {LogLog.error(msg, ex);}}//DefaultRepositorySelector作为RepositorySelector的默认实现:实现了LoggerRepository的方法 return repositorySelector.getLoggerRepository();}}
1.3.4 DefaultRepositorySelector的getLoggerRepository方法返回了默认的LoggerRepository对象,
public class DefaultRepositorySelector implements RepositorySelector { public LoggerRepository getLoggerRepository() { //LoggerRepository的默认实现:Hierarchy,通过上面LoggerManager的静态初始化方法设置 return repository; } }
1.3.5 Hierarchy持有一个DefaultCategoryFactory对象,并从内部查找Logger对象
public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport { public Logger getLogger(String name, LoggerFactory factory) { CategoryKey key = new CategoryKey(name); Logger logger; synchronized(ht) { //根据key获取对应的Logger对象 Object o = ht.get(key); if(o == null) {//获取不到的时候通过LoggerFactory对象创建logger = factory.makeNewLoggerInstance(name);logger.setHierarchy(this);//缓存这个logger对象ht.put(key, logger);updateParents(logger);return logger; } else if(o instanceof Logger) {return (Logger) o; } else if (o instanceof ProvisionNode) {logger = factory.makeNewLoggerInstance(name);logger.setHierarchy(this);ht.put(key, logger);updateChildren((ProvisionNode) o, logger);updateParents(logger);return logger; } else {return null; } } }}
1.3.5 找不到时由DefaultCategoryFactory负责创建.
class DefaultCategoryFactory implements LoggerFactory {DefaultCategoryFactory() {}public Logger makeNewLoggerInstance(String name) { //直接创建Logger对象return new Logger(name);}}
1.3.6 debug方法在Logger的父类Category中实现
public class Category implements AppenderAttachable { public void debug(Object message) { if (repository.isDisabled(Level.DEBUG_INT)) return; //检查当前的日志级别是否大于配置文件的日志级别 if (Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) { //记录日志 forcedLog(FQCN, Level.DEBUG, message, null); } } public Level getEffectiveLevel() { //循环,获取日志级别,获取不到就检查父类的日志级别 for (Category c = this; c != null; c = c.parent) { if (c.level != null) return c.level; } return null; } protected void forcedLog(String fqcn, Priority level, Object message, Throwable t) { //创建日志对象,并调用callAppenders方法 callAppenders(new LoggingEvent(fqcn, this, level, message, t)); } // public LoggingEvent(String fqnOfCategoryClass, Category logger, Priority level, Object message, Throwable throwable) { this.fqnOfCategoryClass = fqnOfCategoryClass; this.logger = logger; this.categoryName = logger.getName(); this.level = level; this.message = message; if (throwable != null) { this.throwableInfo = new ThrowableInformation(throwable, logger); } timeStamp = System.currentTimeMillis(); } public void callAppenders(LoggingEvent event) { int writes = 0; //循环调用当前类及父类 for (Category c = this; c != null; c = c.parent) { synchronized (c) { if (c.aai != null) { //调用AppenderAttachable类的 writes += c.aai.appendLoopOnAppenders(event); } if (!c.additive) { break; } } } if (writes == 0) { repository.emitNoAppenderWarning(this); } }}
1.3.7 AppenderAttachable:将日志记录到不同的Appender对象
public class AppenderAttachableImpl implements AppenderAttachable { public int appendLoopOnAppenders(LoggingEvent event) { int size = 0; Appender appender; if (appenderList != null) { size = appenderList.size(); for (int i = 0; i < size; i++) {//循环 appender = (Appender) appenderList.elementAt(i); //调用不同的Appender对象的Appender方法,将日志记录到不同的地方 appender.doAppend(event); } } return size; }}
1.3.8 AppenderSkeleton:实现Appender类,并提供抽放方法append,由不同的子类实现
public abstract class AppenderSkeleton implements Appender, OptionHandler { synchronized void doAppend(LoggingEvent event) { if (closed) { LogLog.error("Attempted to append to closed appender named [" + name + "]."); return; } if (!isAsSevereAsThreshold(event.getLevel())) { return; } Filter f = this.headFilter; FILTER_LOOP: while (f != null) { switch (f.decide(event)) { case Filter.DENY: return; case Filter.ACCEPT: break FILTER_LOOP; case Filter.NEUTRAL: f = f.getNext(); } } //对应的Append方法由子类实现 this.append(event); }}
1.3.9 WriterAppender
public class WriterAppender extends AppenderSkeleton { public void append(LoggingEvent event) { if (!checkEntryConditions()) { return; } subAppend(event); } protected void subAppend(LoggingEvent event) { this.qw.write(this.layout.format(event)); if (layout.ignoresThrowable()) { String[] s = event.getThrowableStrRep(); if (s != null) { int len = s.length; for (int i = 0; i < len; i++) { this.qw.write(s[i]); this.qw.write(Layout.LINE_SEP); } } } if (shouldFlush(event)) { this.qw.flush(); } }}
1.3.10 ConsoleAppender:Appender的一个实现
public class ConsoleAppender extends WriterAppender {public static final String SYSTEM_OUT = "System.out";public static final String SYSTEM_ERR = "System.err";protected String target;public void activateOptions() { //设置默认的输出对象:System.out,System.errif(this.target.equals("System.out")) {this.setWriter(new OutputStreamWriter(System.out));} else {this.setWriter(new OutputStreamWriter(System.err));}}}
- Log4J源码分析
- log4j源码分析
- log4j源码简要分析 | 读取配置文件
- Log4j源码分析及配置拓展
- log4j源码
- log4j 从配置到源码分析和测试(1)
- log4j分析
- flume源码分析--Log4j日志直接发送到Flume过程分析(二)
- flume源码分析--Log4j日志直接发送到Flume过程分析(三)
- log4j源码解析
- log4j源码解析
- Log4j源码阅读一
- Log4j源码阅读二
- Log4j源码三
- log4j源码阅读四
- Log4j AsyncAppender 源码
- Log4j源码浅析
- Log4j源码实现
- MySQL 性能优化方案
- 2017软院金山WPS补题 L
- [学习]Linux (sys)Log
- 纪录知识库-redis
- 关于软件架构的重要性
- log4j源码分析
- Hibernate 使用Intellij IDEA自动生成.hbm.xml文件
- php 中操作字符串的一些常用函数
- Eclipse常用快捷键
- 145.自定义控件动画图
- IETF
- VIM
- 十个利用矩阵乘法解决的经典题目
- 关联jar包的源代码