slf4j即Simple logging facade for Java,其作用类似于JDBC,作为一个日志抽象层,它允许你在后台使用任意一个日志类库,比如log4j。如果是在编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。常用的日志类库有log4j、logback等,本文就来深入了解一下log4j。
开发代码中,我们只需要在src下放上log4j.xml或log4j.properties,log4j就能自动找到该配置文件,web工程在spring环境下,我们还可以在web.xml中自定义上述配置文件的路径。
- <context-param>
- <param-name>log4jConfigLocation</param-name>
- <param-value>WEB-INF/log4j.properties</param-value>
- </context-param>
- <context-param>
- <param-name>log4jRefreshInterval</param-name>
- <param-value>6000</param-value>
- </context-param>
- <listener>
- <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
- </listener>
那么log4j默认是怎么加载配置文件的呢?每一个Logger实例在log里是怎么安排存放的呢?
Log4j由三个重要的组成构成:日志记录器(Loggers),输出端(Appenders)和日志格式化器(Layout)。
1.日志记录器(Loggers):控制要输出哪些日志记录语句,对日志信息进行级别限制。
2.输出端(Appenders):指定了日志将打印到控制台还是文件中。
3.日志格式化器(Layout):控制日志信息的显示格式。
log4j初始化
从架构上我们可以看出logger的实现是从logManager来具体完成的,因此初始化在logManager的static块中,如下:
- static {
- // By default we use a DefaultRepositorySelector which always returns 'h'.
- Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
- repositorySelector = new DefaultRepositorySelector(h);
- /** Search for the properties file log4j.properties in the CLASSPATH. */
- String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
- null);
- // if there is no default init override, then get the resource
- // specified by the user or the default config file.
- 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;
- // if the user has not specified the log4j.configuration
- // property, we search first for the file "log4j.xml" and then
- // "log4j.properties"
- if(configurationOptionStr == null) {
- url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
- if(url == null) {
- url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
- }
- } else {
- try {
- url = new URL(configurationOptionStr);
- } catch (MalformedURLException ex) {
- // so, resource is not a URL:
- // attempt to get the resource from the class path
- url = Loader.getResource(configurationOptionStr);
- }
- }
- // If we have a non-null url, then delegate the rest of the
- // configuration to the OptionConverter.selectAndConfigure
- // method.
- if(url != null) {
- LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
- try {
- 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.");
- }
- }
让我们深入进去看看是怎么初始化的?
第一步:创建一个默认的RepositorySelector,RepositorySelector在logManager中使用,它的实现类对特定的应用上下文提供了一个LoggerRespository。LoggerRespository的实现类负责追踪应用上下文。ResponsitorySelector提供了一个方法:
public LoggerRepository getLoggerRepository();
LoggerRepository从字面上理解,它是一个Logger的容器,它会创建并缓存Logger实例,从而具有相同名字的Logger实例不会多次创建,以提高性能。它的这种特性有点类似Spring的IOC概念。Log4J支持两种配置文件:properties文件和xml文件。Configurator解析配置文件,并将解析后的信息添加到LoggerRepository中。LogManager最终将LoggerRepository和Configurator整合在一起。
LoggerRepository同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由Logger的Name来决定,其中以’.’分隔。
除了作为一个Logger容器,它还有一个Threshold属性,用于过滤所有在Threshold级别以下的日志。以及其他和Logger操作相关的方法和属性。
- public interface LoggerRepository {
- public void addHierarchyEventListener(HierarchyEventListener listener);
- boolean isDisabled(int level);
- public void setThreshold(Level level);
- public void setThreshold(String val);
- public void emitNoAppenderWarning(Category cat);
- public Level getThreshold();
- public Logger getLogger(String name);
- public Logger getLogger(String name, LoggerFactory factory);
- public Logger getRootLogger();
- public abstract Logger exists(String name);
- public abstract void shutdown();
- public Enumeration getCurrentLoggers();
- public abstract void fireAddAppenderEvent(Category logger, Appender appender);
- public abstract void resetConfiguration();
- }
Hierarchy类
Hierarchy是Log4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现,因而首先从这个方法开始。Hierarchy中用一个Hashtable来存储所有Logger实例,它以CategoryKey作为key,Logger作为value,其中CategoryKey是对Logger中Name字符串的封装,之所以要引入这个类是出于性能考虑,因为它会缓存Name字符串的hash code,这样在查找过程中计算hash code时就可以直接取得而不用每次都计算。
第二步:从classpath路径查找log4j.properties属性配置文件。
1. 判断配置文件有没有重写?
- /** Search for the properties file log4j.properties in the CLASSPATH. */
- String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,null);
- //public static final String DEFAULT_INIT_OVERRIDE_KEY = "log4j.defaultInitOverride";
- publicstatic String getSystemProperty(String key, String def) {
- try {
- return System.getProperty(key, def);
- } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
- LogLog.debug("Was not allowed to read system property \""+key+"\".");
- return def;
- }
- }
2. override为null或者为false时没有重写,然后从系统属性中查找key:log4j.configuration和log4j.configuratorClass属性。如果没有设置log4j.configuration属性,就查找log4j.xml,然后查找log4j.properties文件。使用方法:
- if(configurationOptionStr == null) {
- url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
- if(url == null) {
- url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
- }
使用java2的线程上下文类加载器来查找资源,如果查找失败,则使用加载该类(loader)的类加载器来加载资源。
3.如果设置了log4j.configuration属性,则使用url形式读取,如果资源不是url,则从classpath获取资源:
- try {
- url = new URL(configurationOptionStr);
- } catch (MalformedURLException ex) {
- // so, resource is not a URL:
- // attempt to get the resource from the class path
- url = Loader.getResource(configurationOptionStr);
- }
4. 如果log4j.configuration属性为url形式:
- // If we have a non-null url, then delegate the rest of the
- // configuration to the OptionConverter.selectAndConfigure
- // method.
- if(url != null) {
- LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
- try {
- OptionConverter.selectAndConfigure(url, configuratorClassName,
- LogManager.getLoggerRepository());
- } catch (NoClassDefFoundError e) {
- LogLog.warn("Error during default initialization", e);
- }
- } else {
- LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
- }
原文链接:[http://wely.iteye.com/blog/2287667]