LoggerRepository从字面上理解,它是一个Logger的容器,它会创建并缓存Logger实例,从而具有相同名字的Logger实例不会多次创建,以提高性能。它的这种特性有点类似Spring的IOC概念。Log4J支持两种配置文件:properties文件和xml文件。Configurator解析配置文件,并将解析后的信息添加到LoggerRepository中。LogManager最终将LoggerRepository和Configurator整合在一起。
LoggerRepository接口
LoggerRepository是一个Logger的容器,它负责创建、缓存Logger实例,同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由Logger的Name来决定,其中以’.’分隔。
除了做为一个Logger容器,它还有一个Threshold属性,用于过滤所有在Threshold级别以下的日志。以及其他和Logger操作相关的方法和属性。
LoggerRepository的接口定义如下:
1 public interface LoggerRepository { 2 public void addHierarchyEventListener(HierarchyEventListener listener); 3 boolean isDisabled(int level); 4 public void setThreshold(Level level); 5 public void setThreshold(String val); 6 public void emitNoAppenderWarning(Category cat); 7 public Level getThreshold(); 8 public Logger getLogger(String name); 9 public Logger getLogger(String name, LoggerFactory factory); 10 public Logger getRootLogger(); 11 public abstract Logger exists(String name); 12 public abstract void shutdown(); 13 public Enumeration getCurrentLoggers(); 14 public abstract void fireAddAppenderEvent(Category logger, Appender appender); 15 public abstract void resetConfiguration(); 16 }
Hierarchy类
Hierarchy是Log4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现,因而首先从这个方法开始。
Hierarchy中用一个Hashtable来存储所有Logger实例,它以CategoryKey作为key,Logger作为value,其中CategoryKey是对Logger中Name字符串的封装,之所以要引入这个类是出于性能考虑,因为它会缓存Name字符串的hash code,这样在查找过程中计算hash code时就可以直接取得而不用每次都计算。
1 class CategoryKey { 2 String name; 3 int hashCache; 4 5 CategoryKey(String name) { 6 this.name = name; 7 hashCache = name.hashCode(); 8 } 9 final public int hashCode() { 10 return hashCache; 11 } 12 final public boolean equals(Object rArg) { 13 if (this == rArg) 14 return true; 15 if (rArg != null && CategoryKey.class == rArg.getClass()) 16 return name.equals(((CategoryKey) rArg).name); 17 else 18 return false; 19 } 20 }
getLogger()方法中有一个重载函数提供LoggerFactory接口,它用于没有在LoggerRepository中找到Logger实例时创建相应的Logger实例,默认实现直接创建一个Logger
1 public interface ThrowableRendererSupport { 2 ThrowableRenderer getThrowableRenderer(); 3 void setThrowableRenderer(ThrowableRenderer renderer); 4 }
实例,用户可以通过自定义LoggerFactory实现创建自己的Logger实例。
1 public interface LoggerFactory { 2 public Logger makeNewLoggerInstance(String name); 3 } 4 class DefaultCategoryFactory implements LoggerFactory { 5 public Logger makeNewLoggerInstance(String name) { 6 return new Logger(name); 7 } 8 }
getLogger()方法首先根据传入name创建CategoryKey实例,而后从缓存ht字段中查找:
1. 如果找到对应的Logger实例,则直接返回该实例。
2. 如果没有找到任何实例,则使用LoggerFactory创建新的Logger实例,并将该实例缓存到ht集合中,同时更新新创建Logger实例的parent属性。更新parent属性最简单的做法是从后往前以’.’为分隔符截取字符串,使用截取后的字符串从ht集合中查找是否存在Logger实例,如果存在,则新创建的Logger实例的parent即为找到的实例,若在整个遍历过程中都没有找到相应的parent实例,则其parent实例为root。然而如果一个“x.y.z.w”Logger起初的parent设置为root,而后出现“x.y.z”Logger实例,那么就需要更新“x.y.z.w”Logger的parent为“x.y.z”Logger实例,此时就会遇到一个如何找到在集合中已经存在的“x.y.z”Logger实例子节点的问题。当然一种简单的做法是遍历ht集合中所有实例,判断那个实例是不是“x.y.z”Logger实例的子节点,是则更新其parent节点。由于每次的遍历会引起一些性能问题,因而Log4J使用ProvisionNode事先将所有的可能相关的子节点保存起来,并将ProvisionNode实例添加到ht集合中,这样只要找到对应的ProvisionNode实例,就可以找到所有相关的子节点了。比如对“x.y.z.w”Logger实例,它会产生三个ProvisionNode实例(当然如果相应的实例已经存在,则直接添加而无需创建,另外,如果相应节点已经是Logger实例,那么将“x.y.z.w”Logger实例的parent直接指向它即可):ProvisionNode(“x”), ProvisionNode(“x.y”), ProvisionNode(“x.y.z”),他们都存储了“x.y.z.w”Logger实例作为其子节点。
1 class ProvisionNode extends Vector { 2 ProvisionNode(Logger logger) { 3 super(); 4 this.addElement(logger); 5 } 6 } 7 final private void updateParents(Logger cat) { 8 String name = cat.name; 9 int length = name.length(); 10 boolean parentFound = false; 11 // if name = "x.y.z.w", loop thourgh "x.y.z", "x.y" and "x" 12 for (int i = name.lastIndexOf('.', length - 1); i >= 0; i = name 13 .lastIndexOf('.', i - 1)) { 14 String substr = name.substring(0, i); 15 CategoryKey key = new CategoryKey(substr); 16 Object o = ht.get(key); 17 if (o == null) { 18 ProvisionNode pn = new ProvisionNode(cat); 19 ht.put(key, pn); 20 } else if (o instanceof Category) { 21 parentFound = true; 22 cat.parent = (Category) o; 23 break; // no need to update the ancestors of the closest 24 // ancestor 25 } else if (o instanceof ProvisionNode) { 26 ((ProvisionNode) o).addElement(cat); 27 } else { 28 Exception e = new IllegalStateException( 29 "unexpected object type " + o.getClass() + " in ht."); 30 e.printStackTrace(); 31 } 32 } 33 // If we could not find any existing parents, then link with root. 34 if (!parentFound) 35 cat.parent = root; 36 }
3. 如果找到的是ProvisionNode实例,首先使用factory创建新的Logger实例,将该实例添加到ht集合中,然后更新找到的ProvisionNode内部所有Logger的parent字段以及新创建Logger的parent字段。更新过程中需要注意ProvisionNode中的Logger实例已经指向了正确的parent了,所以只要更新那些ProvisionNode中Logger实例指向的parent比新创建的Logger本身层次要高的那些parent属性。比如开始插入“x.y.z”Logger实例,而后插入“x.y.z.w”Logger实例,此时ProvisionNode(“x”)认为“x.y.z”Logger实例和“x.y.z.w”Logger实例都是它的子节点,而后插入“x”Logger实例,那么只需要更新“x.y.z”Logger的父节点为“x”Logger实例即可,而不用更新“x.y.z.w”Logger实例的父节点。
1 final private void updateChildren(ProvisionNode pn, Logger logger) { 2 final int last = pn.size(); 3 for (int i = 0; i < last; i++) { 4 Logger l = (Logger) pn.elementAt(i); 5 // Unless this child already points to a correct (lower) parent, 6 // make cat.parent point to l.parent and l.parent to cat. 7 if (!l.parent.name.startsWith(logger.name)) { 8 logger.parent = l.parent; 9 l.parent = logger; 10 } 11 } 12 }
综合起来,getLogger()方法的实现代码如下:
1 public Logger getLogger(String name, LoggerFactory factory) { 2 CategoryKey key = new CategoryKey(name); 3 Logger logger; 4 synchronized (ht) { 5 Object o = ht.get(key); 6 if (o == null) { 7 logger = factory.makeNewLoggerInstance(name); 8 logger.setHierarchy(this); 9 ht.put(key, logger); 10 updateParents(logger); 11 return logger; 12 } else if (o instanceof Logger) { 13 return (Logger) o; 14 } else if (o instanceof ProvisionNode) { 15 logger = factory.makeNewLoggerInstance(name); 16 logger.setHierarchy(this); 17 ht.put(key, logger); 18 updateChildren((ProvisionNode) o, logger); 19 updateParents(logger); 20 return logger; 21 } else { 22 // It should be impossible to arrive here 23 return null; // but let's keep the compiler happy. 24 } 25 } 26 }
其他的方法实现则比较简单,对LoggerRepository来说,它也可以像其注册HierarchyEventListener监听器,每当向一个Logger添加或删除Appender,该监听器就会触发。
1 public interface HierarchyEventListener { 2 public void addAppenderEvent(Category cat, Appender appender); 3 public void removeAppenderEvent(Category cat, Appender appender); 4 } 5 private Vector listeners; 6 public void addHierarchyEventListener(HierarchyEventListener listener) { 7 if (listeners.contains(listener)) { 8 LogLog.warn("Ignoring attempt to add an existent listener."); 9 } else { 10 listeners.addElement(listener); 11 } 12 } 13 public void fireAddAppenderEvent(Category logger, Appender appender) { 14 if (listeners != null) { 15 int size = listeners.size(); 16 HierarchyEventListener listener; 17 for (int i = 0; i < size; i++) { 18 listener = (HierarchyEventListener) listeners.elementAt(i); 19 listener.addAppenderEvent(logger, appender); 20 } 21 } 22 } 23 void fireRemoveAppenderEvent(Category logger, Appender appender) { 24 if (listeners != null) { 25 int size = listeners.size(); 26 HierarchyEventListener listener; 27 for (int i = 0; i < size; i++) { 28 listener = (HierarchyEventListener) listeners.elementAt(i); 29 listener.removeAppenderEvent(logger, appender); 30 } 31 } 32 }
Hierarchy中保存了threshold字段,用户可以设置threshold。而对root实例,它在够着Hierarchy时就被指定了。getCurrentLoggers()方法将ht集合中所有的Logger实例取出。shutdown()方法遍历所有Logger实例以及root实例,调用所有附加其上的Appender的close()方法,并将所有Appender实例从Logger中移除,最后触发AppenderRemove事件。resetConfiguration()方法将root字段初始化、调用shutdown()方法移除Logger中的所有Appender、初始化所有Logger实例当不将其从LoggerRepository中移除、清楚rendererMap和throwableRender中的数据。
RendererSupport接口
RendererSupport接口支持用户为不同的类设置相应的ObjectRender实例,从而可以从被渲染的类中或许更多的信息而不是默认的调用其toString()方法。
1 public interface RendererSupport { 2 public RendererMap getRendererMap(); 3 public void setRenderer(Class renderedClass, ObjectRenderer renderer); 4 } 5 Hierarchy类实现了RenderedSupprt接口,而且它的实现也很简单: 6 RendererMap rendererMap; 7 public Hierarchy(Logger root) { 8 9 rendererMap = new RendererMap(); 10 11 } 12 public void addRenderer(Class classToRender, ObjectRenderer or) { 13 rendererMap.put(classToRender, or); 14 } 15 public RendererMap getRendererMap() { 16 return rendererMap; 17 } 18 public void setRenderer(Class renderedClass, ObjectRenderer renderer) { 19 rendererMap.put(renderedClass, renderer); 20 }
在RendererMap类实现中,它使用Hastable保存被渲染的类实例和相应的ObjectRender实例,在查找一个类是否存在注册的渲染类时,如果它本身没有找到,需要向上尝试其父类和接口是否有注册相应的ObjectRender类,如果都没有找到,则返回默认的ObjectRender。
1 public ObjectRenderer get(Class clazz) { 2 ObjectRenderer r = null; 3 for (Class c = clazz; c != null; c = c.getSuperclass()) { 4 r = (ObjectRenderer) map.get(c); 5 if (r != null) { 6 return r; 7 } 8 r = searchInterfaces(c); 9 if (r != null) 10 return r; 11 } 12 return defaultRenderer; 13 } 14 ObjectRenderer searchInterfaces(Class c) { 15 ObjectRenderer r = (ObjectRenderer) map.get(c); 16 if (r != null) { 17 return r; 18 } else { 19 Class[] ia = c.getInterfaces(); 20 for (int i = 0; i < ia.length; i++) { 21 r = searchInterfaces(ia[i]); 22 if (r != null) 23 return r; 24 } 25 } 26 return null; 27 } 28 public ObjectRenderer getDefaultRenderer() { 29 return defaultRenderer; 30 } 31 public void put(Class clazz, ObjectRenderer or) { 32 map.put(clazz, or); 33 }
ThrowableRendererSupport接口
ThrowableRendererSupport接口用于支持设置和获取ThrowableRenderer,从而用户可以自定义对Throwable对象的渲染。
1 public interface ThrowableRendererSupport { 2 ThrowableRenderer getThrowableRenderer(); 3 void setThrowableRenderer(ThrowableRenderer renderer); 4 }
Hierarchy类以属性的方式实现了该接口,因而每个Hierarchy实例只能有一个全局的ThrowableRenderer,而不能像ObjectRender那样为不同的类定义不同的render。当时这种设计也是合理的,因为对Throwable的渲染最主要的就是其栈的渲染,其他的没什么大的不同,而且对栈渲染方式保持相同的格式会比较好。
1 private ThrowableRenderer throwableRenderer = null; 2 public Hierarchy(Logger root) { 3 4 defaultFactory = new DefaultCategoryFactory(); 5 6 } 7 public void setThrowableRenderer(final ThrowableRenderer renderer) { 8 throwableRenderer = renderer; 9 } 10 public ThrowableRenderer getThrowableRenderer() { 11 return throwableRenderer; 12 }
Configurator接口
Configurator接口用于定义对配置文件的解析。在Log4J中配置文件解析出来的所有信息都可以放在LoggerRepository中,因而Configurator接口的定义非常简单。
1 public interface Configurator { 2 public static final String INHERITED = "inherited"; 3 public static final String NULL = "null"; 4 void doConfigure(URL url, LoggerRepository repository); 5 }
Log4J支持两种文件形式的配置文件:properties文件和xml文件,他们风别对应PropertyConfigurator类和DOMConfigurator类。
PropertyConfigurator类
PropertyConfigurator类解析properties文件的中的配置信息,可以设置log4j.debug为true以打开Log4J内部的日志信息;另外PropertyConfigurator还支持Linux风格的变量,即所有${variable}形式的变量都会被系统中对应的属性或配置文件内部定义的属性替换(先查找系统中的属性,后查找配置文件内部定义的属性);但是PropertyConfigurator不支持一些Log4J中的高级功能,如自定义ErrorHandler和定义AsyncAppender等。
Configurator中最重要的方法是doConfigure()方法,在PropertyConfigurator实现中,首先将配置文件对应的URL读取成Properties对象:
1 public void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) { 2 Properties props = new Properties(); 3 4 uConn = configURL.openConnection(); 5 uConn.setUseCaches(false); 6 istream = uConn.getInputStream(); 7 props.load(istream); 8 9 doConfigure(props, hierarchy); 10 }
而后检查是否设置了log4j.debug、log4j.reset、log4j.threshold等属性,如果有则做相应的设置。这里通过OptionConverter.findAndSubst()方法实现属性的查找和变量信息的替换。
1 public void doConfigure(Properties properties, LoggerRepository hierarchy) { 2 repository = hierarchy; 3 String value = properties.getProperty(LogLog.DEBUG_KEY); 4 if (value == null) { 5 value = properties.getProperty("log4j.configDebug"); 6 if (value != null) 7 LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead."); 8 } 9 if (value != null) { 10 LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true)); 11 } 12 String reset = properties.getProperty(RESET_KEY); 13 if (reset != null && OptionConverter.toBoolean(reset, false)) { 14 hierarchy.resetConfiguration(); 15 } 16 String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX, 17 properties); 18 if (thresholdStr != null) { 19 hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr, 20 (Level) Level.ALL)); 21 LogLog.debug("Hierarchy threshold set to [" 22 + hierarchy.getThreshold() + "]."); 23 } 24 25 }
然后分三步解析配置信息:
1. 解析Root Logger配置
首先找到log4j.rootLogger的值,它以逗号’,’分隔,其中第一个值时root的Level信息,之后是要添加到root的Appender名字。对Level信息,直接设置给root就行。对Appender名字,继续解析
1 void parseCategory(Properties props, Logger logger, String optionKey, 2 String loggerName, String value) { 3 LogLog.debug("Parsing for [" + loggerName + "] with value=[" + value 4 + "]."); 5 StringTokenizer st = new StringTokenizer(value, ","); 6 if (!(value.startsWith(",") || value.equals(""))) { 7 if (!st.hasMoreTokens()) 8 return; 9 String levelStr = st.nextToken(); 10 LogLog.debug("Level token is [" + levelStr + "]."); 11 if (INHERITED.equalsIgnoreCase(levelStr) 12 || NULL.equalsIgnoreCase(levelStr)) { 13 if (loggerName.equals(INTERNAL_ROOT_NAME)) { 14 LogLog.warn("The root logger cannot be set to null."); 15 } else { 16 logger.setLevel(null); 17 } 18 } else { 19 logger.setLevel(OptionConverter.toLevel(levelStr, 20 (Level) Level.DEBUG)); 21 } 22 LogLog.debug("Category " + loggerName + " set to " 23 + logger.getLevel()); 24 } 25 logger.removeAllAppenders(); 26 Appender appender; 27 String appenderName; 28 while (st.hasMoreTokens()) { 29 appenderName = st.nextToken().trim(); 30 if (appenderName == null || appenderName.equals(",")) 31 continue; 32 LogLog.debug("Parsing appender named \"" + appenderName + "\"."); 33 appender = parseAppender(props, appenderName); 34 if (appender != null) { 35 logger.addAppender(appender); 36 } 37 } 38 }
相同的Appender可以添加到不同的Logger中,因而PropertyConfigurator对Appender做了缓存,如果能从缓存中找到相应的Appender类,则直接返回找到的Appender。
而后解析以下键值名以及对应类的属性信息:
log4j.appender.appenderName=…
log4j.appender.appenderName.layout=…
log4j.appender.appenderName.errorhandler=…
log4j.appender.appenderName.filter.filterKey.name=…
1 Appender parseAppender(Properties props, String appenderName) { 2 Appender appender = registryGet(appenderName); 3 if ((appender != null)) { 4 LogLog.debug("Appender \"" + appenderName 5 + "\" was already parsed."); 6 return appender; 7 } 8 String prefix = APPENDER_PREFIX + appenderName; 9 String layoutPrefix = prefix + ".layout"; 10 appender = (Appender) OptionConverter.instantiateByKey(props, prefix, 11 org.apache.log4j.Appender.class, null); 12 if (appender == null) { 13 LogLog.error("Could not instantiate appender named \"" 14 + appenderName + "\"."); 15 return null; 16 } 17 appender.setName(appenderName); 18 if (appender instanceof OptionHandler) { 19 if (appender.requiresLayout()) { 20 Layout layout = (Layout) OptionConverter.instantiateByKey( 21 props, layoutPrefix, Layout.class, null); 22 if (layout != null) { 23 appender.setLayout(layout); 24 LogLog.debug("Parsing layout options for \"" + appenderName 25 + "\"."); 26 PropertySetter.setProperties(layout, props, layoutPrefix 27 + "."); 28 LogLog.debug("End of parsing for \"" + appenderName + "\"."); 29 } 30 } 31 final String errorHandlerPrefix = prefix + ".errorhandler"; 32 String errorHandlerClass = OptionConverter.findAndSubst( 33 errorHandlerPrefix, props); 34 if (errorHandlerClass != null) { 35 ErrorHandler eh = (ErrorHandler) OptionConverter 36 .instantiateByKey(props, errorHandlerPrefix, 37 ErrorHandler.class, null); 38 if (eh != null) { 39 appender.setErrorHandler(eh); 40 LogLog.debug("Parsing errorhandler options for \"" 41 + appenderName + "\"."); 42 parseErrorHandler(eh, errorHandlerPrefix, props, repository); 43 final Properties edited = new Properties(); 44 final String[] keys = new String[] { 45 errorHandlerPrefix + "." + ROOT_REF, 46 errorHandlerPrefix + "." + LOGGER_REF, 47 errorHandlerPrefix + "." + APPENDER_REF_TAG }; 48 for (Iterator iter = props.entrySet().iterator(); iter 49 .hasNext();) { 50 Map.Entry entry = (Map.Entry) iter.next(); 51 int i = 0; 52 for (; i < keys.length; i++) { 53 if (keys[i].equals(entry.getKey())) 54 break; 55 } 56 if (i == keys.length) { 57 edited.put(entry.getKey(), entry.getValue()); 58 } 59 } 60 PropertySetter.setProperties(eh, edited, errorHandlerPrefix 61 + "."); 62 LogLog.debug("End of errorhandler parsing for \"" 63 + appenderName + "\"."); 64 } 65 } 66 PropertySetter.setProperties(appender, props, prefix + "."); 67 LogLog.debug("Parsed \"" + appenderName + "\" options."); 68 } 69 parseAppenderFilters(props, appenderName, appender); 70 registryPut(appender); 71 return appender; 72 }
2. 解析LoggerFactory配置
查找log4j.loggerFactory的值,保存创建的LoggerFactory实例,使用log4j.loggerFactory.propName的方式设置LoggerFactory实例的属性。
1 protected void configureLoggerFactory(Properties props) { 2 String factoryClassName = OptionConverter.findAndSubst( 3 LOGGER_FACTORY_KEY, props); 4 if (factoryClassName != null) { 5 LogLog.debug("Setting category factory to [" + factoryClassName 6 + "]."); 7 loggerFactory = (LoggerFactory) OptionConverter 8 .instantiateByClassName(factoryClassName, 9 LoggerFactory.class, loggerFactory); 10 PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX 11 + "."); 12 } 13 }
3. 解析非Root Logger和ObjectRender配置
解析log4j.logger.、log4j.renderer.、log4j.throwableRenderer.等信息。
另外,PropertyConfigurator还通过PropertyWatchLog类支持每个一段时间检查一次,如果发现配置文件有改动,则自动重新加载配置信息。
DOMConfigurator类
DOMConfigurator使用DOM解析所有Log4J配置文件中的元素,并根据DOM中的元素查找对应的RootLogger、Logger、Appender、Layout等模块。另外DOMConfigurator也支持每隔一段时间检查文件是否有修改,若有,则重新载入新修改后的配置文件。这里DOMConfigurator的实现方式和PropertyConfigurator的实现方式类似,不再详细介绍。
LogManager类
LogManager将Configurator和LoggerRepository整合在一起,它在初始化的时候找到Log4J配置文件,并且将其解析到LoggerRepository中。1 static { 2 Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG)); 3 repositorySelector = new DefaultRepositorySelector(h); 4 String override = OptionConverter.getSystemProperty( 5 DEFAULT_INIT_OVERRIDE_KEY, null); 6 if (override == null || "false".equalsIgnoreCase(override)) { 7 String configurationOptionStr = OptionConverter.getSystemProperty( 8 DEFAULT_CONFIGURATION_KEY, null); 9 String configuratorClassName = OptionConverter.getSystemProperty( 10 CONFIGURATOR_CLASS_KEY, null); 11 URL url = null; 12 if (configurationOptionStr == null) { 13 url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE); 14 if (url == null) { 15 url = Loader.getResource(DEFAULT_CONFIGURATION_FILE); 16 } 17 } else { 18 try { 19 url = new URL(configurationOptionStr); 20 } catch (MalformedURLException ex) { 21 url = Loader.getResource(configurationOptionStr); 22 } 23 } 24 if (url != null) { 25 LogLog.debug("Using URL [" + url 26 + "] for automatic log4j configuration."); 27 try { 28 OptionConverter.selectAndConfigure(url, 29 configuratorClassName, 30 LogManager.getLoggerRepository()); 31 } catch (NoClassDefFoundError e) { 32 LogLog.warn("Error during default initialization", e); 33 } 34 } else { 35 LogLog.debug("Could not find resource: [" 36 + configurationOptionStr + "]."); 37 } 38 } else { 39 LogLog.debug("Default initialization of overridden by " 40 + DEFAULT_INIT_OVERRIDE_KEY + "property."); 41 } 42 }
相关推荐
《深入解析Log4j 2.11.0源码》 Log4j,作为Java领域最常用的日志框架之一,其2.11.0版本的发布为开发者提供了更加强大、高效和灵活的日志处理能力。源码包的获取,对于开发者深入理解其工作原理、定制化需求以及...
Log4j包括三个核心部分:配置器(Configurator)、日志记录器(Logger)和布局器(Layout)。配置器用于设置日志级别和输出目的地,日志记录器负责实际的日志记录,而布局器则决定了日志信息的格式。 **SLF4J与Log4...
《深入理解Log4j-1.2.16:核心组件与应用实践》 日志在软件开发中扮演着至关重要的角色,它记录了程序运行时的详细信息,帮助开发者定位和解决各种问题。Log4j是Apache组织推出的一款开源的日志记录框架,自发布...
**Log4j简介** Log4j是Apache组织提供的一款开源日志记录框架,它为Java应用程序提供了灵活且强大的日志处理能力。Log4j的主要目标是简化...通过阅读`log4j知识.doc`文档,可以深入学习Log4j的详细用法和最佳实践。
通过深入研究Apache Log4j 2.10.0的源码,开发者不仅可以了解日志框架的内部工作原理,还能学习到设计一个高性能、可扩展的日志系统的最佳实践。这有助于在实际开发中更好地利用Log4j 2的功能,解决日志管理中的各种...
在本文中,我们将深入探讨Log4j 2的核心概念和主要功能。 **1. 性能优化** Log4j 2通过引入新的日志事件处理机制和Async Appenders,极大地提高了日志记录的性能。Async Appenders利用Java的并发库,实现了非阻塞的...
理解Log4j2的配置和使用是每个Java开发者必备的技能,这对于日后的故障排查和系统维护至关重要。在实际应用中,你可以根据需求调整配置,例如增加日志过滤规则、添加更多的日志输出目标,或者改变日志格式等。
《Apache Log4j 2.5 源码解析与应用探索》 Apache Log4j 是Java领域中广泛使用的日志记录框架,尤其在...通过对源码的深入学习,我们可以更有效地利用Log4j来满足复杂项目的需求,同时提高日志系统的稳定性和安全性。
5. **Configurator**:用于配置log4j的设置,如通过`log4j.properties`或`log4j.xml`文件。 **使用步骤** 1. **添加依赖**:在项目中引入log4j的jar包。 2. **配置log4j**:编写`log4j.properties`或`log4j.xml`...
它包含了日志API(Log4j API)、日志实现(Log4j Core)和配置工具(Log4j Configurator),三者共同构建了一个完整的日志框架。 二、Log4j 2.13.3新特性 1. **Async Logger**:这是Log4j 2的一大亮点,通过使用...
4. Configurator(配置器):Configurator负责根据配置文件(通常是log4j.properties或log4j.xml)设置log4j的运行环境,如定义日志级别、Appender和Layout。 三、源代码剖析 1. 日志级别管理:在`org.apache.log4...
5. **Configurator**: 用于配置Log4j的行为,通常通过XML配置文件(log4j.properties或log4j.xml)完成。Demo中可能包含一个示例配置文件,展示如何设置logger的级别、appender及其布局。 **使用步骤** 1. **添加...
- **Configurator**:负责读取和应用配置,初始化Log4j。 3. **配置示例** - **XML配置**: ```xml [%t] %-5level %logger{36} - %msg%n"/> ``` - **JSON配置**: ```json { ...
Log4j的主要组件有Configurator(配置器)、Appender(输出目的地)、Layout(格式化策略)和Filter(过滤规则)。通过`log4j.properties`或`log4j.xml`配置文件,我们可以定制Log4j的行为。 将SLF4J与Log4j结合...
Log4j包含三个主要部分:配置器(Configurator)、日志级别(Logging Levels)和日志输出目的地(Appenders)。 1. **配置器(Configurator)**:它是Log4j的核心,负责读取配置文件并设置日志行为。通常,我们使用...
本文将深入探讨log4j 1.2.14版本的源代码,帮助读者理解其内部工作机制,提升对日志处理的理解和应用。 1. **log4j简介** log4j是Apache组织提供的一个用于日志记录的开源项目,它提供了灵活的日志记录方式,支持...
6. **Configurator**: 配置器负责读取Log4J的配置文件(通常是log4j.properties或log4j.xml),并根据配置信息设置Logger、Appender、Layout等组件的属性。 7. **Hierarchy**: Log4J的日志系统采用层次结构,使得...
6. **Configurator**:Log4j的配置通常通过XML或.properties文件完成,Configurator类负责读取这些配置并应用到Log4j环境中。 **Log4j配置文件** 在"压缩包子文件的文件名称列表"中没有具体的配置文件,但通常来说...
5. **Configurator**: 配置器,用于读取并解析Log4j的配置文件,设置Logger、Level和Appender等。 **二、Log4j的使用步骤** 1. **引入依赖**: 在项目中添加Log4j的JAR包,如果是Maven项目,可以在pom.xml中添加...
Log4j由三部分组成:配置器(Configurator)、日志记录器(Logger)和输出处理器(Appender)。配置器负责设置日志行为,例如定义日志级别、输出格式和目标位置。日志记录器是用于创建和管理日志事件的对象,它可以...