博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
log4j容器初始化探究
阅读量:6294 次
发布时间:2019-06-22

本文共 19581 字,大约阅读时间需要 65 分钟。

Log4j容器初始化探究

Log4j第一步就是初始化Logger容器Repository,这一章我们来探究Logger容器,从别从独立应用以及servlet容器下启动初始化两方面探究。

1 独立应用

静态初始化,java语言保证静态初始化只被执行一次,静态初始化源码在LogManager中。

时序图:

1043381-20161023092920779-1890180781.png

初始化流程:

第一步: LogManager获取配置文件的URL

第二步: OptionConverter获取Configurator实现类(配置类)

第三步: Configurator读取配置文件内容,配置Logger容器(默认配置Hierarchy)

1.1 LoggManager探究

LogManager获取配置文件的URL

源码:

//只在内部使用,将来版本将变为protected级别。static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";  //将来版本变为private级别public String DEFAULT_CONFIGURATION_KEY="log4j.configuration";//将来版本将变为private级别。用来指定在定义配置类static final public String CONFIGURATOR_CLASS_KEY="log4j.configuratorClass";//将来版本将变为private级别。如果不为空并且不为`false`则直接跳过初始化阶段public static final String DEFAULT_INIT_OVERRIDE_KEY =  "log4j.defaultInitOverride";static private Object guard = null;//Logger容器选择器static private RepositorySelector repositorySelector;static {    //初始化Logger容器为Hierarchy。根节点是RootLogger,默认级别是DEBUG    Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));    //初始化Logger容器选择器,以Hierarchy为Logger容器    repositorySelector = new DefaultRepositorySelector(h);    //获取系统属性log4j.defaultInitOverride    String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,null);    //如果没有设置log4j.defaultInitOverride,或者log4j.defaultInitOverride为false,进入初始化流程,否则跳过初始化    if(override == null || "false".equalsIgnoreCase(override)) {          //读取系统属性log4j.configuration          String configurationOptionStr = OptionConverter.getSystemProperty(DEFAULT_CONFIGURATION_KEY, null);          //读取系统属性log4j.configuratorClass          String configuratorClassName = OptionConverter.getSystemProperty(CONFIGURATOR_CLASS_KEY, null);          URL url = null;          //如果不存在log4j.configuration          if(configurationOptionStr == null) {              //第一步先检查是否有log4j.xml            url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);            //如果没有检查是否有log4j.properties            if(url == null) {              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 {                //如果存在url,则利用URL配置Logger容器                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.初始化Logger容器Hierarchy,设置根节点为RootLogger

2.初始LoggerRepositorySelector(容器选择器)为默认的DefaultRepositorySelector,容器为Hierarchy

3.读取系统属性log4j.defaultInitOverride,如果没有设置或者为false进行初始化,否则跳过初始化

4.读取系统属性log4j.configuration(log4j文件路径配置),如果存在对应的文件,则得到URL.如果没有对应的文件,首先检查是否存在log4j.xml文件,如果存在,得到Log4j配置文件URL,如果不存在log4j.xml,继续检查是否存在log4j.properties文件,如果存在该文件,得到log4j配置文件的URL,否则提示没有发现配置文件。

5.读取系统属性log4j.configuratorClass(自定义Configurator配置类全路径,一般不自定义)

6.调用OptionConverter.selectAndConfigure(url, configuratorClassName,LogManager.getLoggerRepository()),初始化logger容器

1.2 OptionConverter探究

OptionConverter获取Configurator实现类(配置类)

源码:

//利用给定URL配置Logger容器static  public void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {     Configurator configurator = null;     String filename = url.getFile();     //优先检查使用xml文件,并查看是否有自定义的configurator    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 {         configurator = new PropertyConfigurator();     }     configurator.doConfigure(url, hierarchy);}

源码流程解析:

1.如果没有自定义配置类Configurator并且文件的后缀名是xml.配置类设置为org.apache.log4j.xml.DOMConfigurator

2.如果自定义了配置类,根据配置类的全限定名,发射得到配置类实例

3.上面两种情况都没有匹配成功,默认是PropertyConfigurator配置类

4.调用configurator.doConfigure(url,hierarchy),根据配置文件URL,配置logger容器Hierarchy(已经静态化构造了简单的容器,RootLogger是根节点)

1.3 Configurator探究(以ProptertyConfigurator为例)

Configurator读取配置文件内容,配置Logger容器

1.3.1 doConfigure(URL,LoggerRepository)

源码:

//从URL中读取配置文件,配置Logger容器Hierarchypublicvoid 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();         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) {             }         }     }     doConfigure(props, hierarchy);}

源码流程解析:

1.文件URL读取文件内容,赋值给Properties

2.调用doConfigure(properties,hierarchy)配置logger容器

1.3.2 doConfigure(Properties , LoggerRepository)

源码:

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));     }     // if log4j.reset=true then     // reset hierarchy     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()+"].");     }      configureRootCategory(properties, hierarchy);     configureLoggerFactory(properties);     parseCatsAndRenderers(properties, hierarchy);     LogLog.debug("Finished configuring.");     // We don't want to hold references to appenders preventing their     // garbage collection.     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 {      Logger root = hierarchy.getRootLogger();      synchronized(root) {        parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);      }    }  }protected void configureLoggerFactory(Properties props) {    String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY,props);    if(factoryClassName != null) {      LogLog.debug("Setting category factory to ["+factoryClassName+"].");      loggerFactory = (LoggerFactory)OptionConverter.instantiateByClassName(factoryClassName,LoggerFactory.class,loggerFactory);      PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX + ".");    }  }protected void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {     Enumeration enumeration = props.propertyNames();     while(enumeration.hasMoreElements()) {         String key = (String) enumeration.nextElement();         if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {            String loggerName = null;            if(key.startsWith(CATEGORY_PREFIX)) {                 loggerName = key.substring(CATEGORY_PREFIX.length());            } else if(key.startsWith(LOGGER_PREFIX)) {                 loggerName = key.substring(LOGGER_PREFIX.length());            }            String value = OptionConverter.findAndSubst(key, props);            Logger logger = hierarchy.getLogger(loggerName, loggerFactory);            synchronized(logger) {                 parseCategory(props, logger, key, loggerName, value);                 parseAdditivityForLogger(props, logger, loggerName);            }         } else if(key.startsWith(RENDERER_PREFIX)) {            String renderedClass = key.substring(RENDERER_PREFIX.length());            String renderingClass = OptionConverter.findAndSubst(key, props);            if(hierarchy instanceof RendererSupport) {                 RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass, renderingClass);            }         } else if (key.equals(THROWABLE_RENDERER_PREFIX)) {             if (hierarchy instanceof ThrowableRendererSupport) {                 ThrowableRenderer tr = (ThrowableRenderer) OptionConverter.instantiateByKey(props, THROWABLE_RENDERER_PREFIX, org.apache.log4j.spi.ThrowableRenderer.class, null);                 if(tr == null) {                     LogLog.error( "Could not instantiate throwableRenderer.");                 } else {                     PropertySetter setter = new PropertySetter(tr);                     setter.setProperties(props, THROWABLE_RENDERER_PREFIX + ".");                     ((ThrowableRendererSupport) hierarchy).setThrowableRenderer(tr);                 }             }         }     } }

源码流程解析:

1.获取log4j.debug(log4j内部是否debug打印日志),如果为ture打印,false不打印。如果没有设置,尝试读取log4j.configdebug(已经废弃,用logdebug取代)

2.读取log4j.reset,如果设置为true,重置logger容器

3.读取log4j.threshold,设置logger容器总阀值,低于阀值将不打印日志。如果没有配置,默认设置为最低级别Level.ALL

4.调用configureRootCategory\(Properties, LoggerRepository\),配置RootLogger.RootLogger级别不能设置为空或者inherit.解析设置RootLogger的Appenders和Filters.

5.调用configureLoggerFactory(Properties props),配置Logger工厂类LoggerFactory.

6.调用parseCatsAndRenderers(Properties, LoggerRepository),配置Logger以及Renderer

2 Web应用

最常用的就是与Spring集成,这里主要将和Spring集成以及启动流程.其实web应用初始化log4j流程就是,容器启动的时候,首先找到Log4j配置文件,然后调用log4j API进行log4j初始化配置(同上)

2.1 搭建web环境

第一步:加入依赖

4.2.4.RELEASE
1.2.17
junit
junit
4.4
test
javax.servlet
servlet-api
2.5
provided
javax.servlet
jsp-api
2.0
provided
org.springframework
spring-webmvc
${spring.version}
log4j
log4j
${log4j.version}

第二步:在web.xml中加入Log4jConfigListener

org.springframework.web.util.Log4jConfigListener

第三步:在resources文件夹下加入log4j.xml或者log4j.properties

### 设置###log4j.rootLogger = debug,stdout,D,Elog4j.threshold= debug## log4j内部是否debuglog4j.debug= false### 配置自己的log工厂类log4j.loggerFactory=com.log.log4j.configure.MyLoggerFactory### 输出信息到控制抬 ###log4j.appender.stdout = org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target = System.outlog4j.appender.stdout.Threshold = warnlog4j.appender.stdout.layout = org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n### 输出DEBUG 级别以上的日志到=/data/applogs/log/logtopic/app.log ###log4j.appender.D = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.D.File = /data/applogs/log/logtopic/app.loglog4j.appender.D.Append = truelog4j.appender.D.Threshold = DEBUGlog4j.appender.D.layout = org.apache.log4j.PatternLayoutlog4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n### 输出ERROR 级别以上的日志到=/data/applogs/log/log4jLearning/error.log ###log4j.appender.E = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.E.File =/data/applogs/log/logtopic/error.loglog4j.appender.E.Append = truelog4j.appender.E.Threshold = ERRORlog4j.appender.E.layout = org.apache.log4j.PatternLayoutlog4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

第四步:编写ServletDemo并配置

ServletDemo代码:

public class Log4jServletDemo extends HttpServlet {    public static final Logger LOGGER = Logger.getLogger(Log4jServletDemo.class);    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        service(req, resp);    }    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{        service(req, resp);    }    @Override    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{        LOGGER.debug("Log4jServletDemo Info Level");        LOGGER.info("Log4jServletDemo Info Level");        LOGGER.warn("Log4jServletDemo Info Level");        LOGGER.error("Log4jServletDemo Info Level");        req.getRequestDispatcher("/index.jsp").forward(req, resp);    }}

web.xml中配置:

servletDemo
com.log.log4j.web.Log4jServletDemo
servletDemo
/demo
2.2 log4jweb初始化

初始化时序图:

1043381-20161023093033685-1842057062.png

初始化流程:

1.tomcat容器加载Log4jConfigListener

2.Log4jConfigListener把初始化Log4j的工作为委托给Log4jWebConfigurer

3.Log4jWebConfigurer获取配置文件路径。然后再委托给Log4jConfigurer

4.Log4jConfigurer调用Log4j框架的DomConfigurator.configure(url)或者PropertyConfigurator.configure(url)初始化配置Log4j,这样就走到了上面独立应用初始化Log4j的过程

2.3 源码探究
2.3.1 Log4jConfigListener

源码:

public class Log4jConfigListener implements ServletContextListener {     @Override     public void contextInitialized(ServletContextEvent event) {        Log4jWebConfigurer.initLogging(event.getServletContext());     }     @Override     public void contextDestroyed(ServletContextEvent event) {           Log4jWebConfigurer.shutdownLogging(event.getServletContext());     }}

源码流程解析:

1.调用contextInitialized(ServletContextEvent)初始化Log4j

2.委托给Log4jWebConfigurer初始化Log4j

2.3.2 Log4jWebConfigurer

源码:

public static void initLogging(ServletContext servletContext) {     // 首先检查是否暴露系统属性,默认是暴露    if (exposeWebAppRoot(servletContext)) {         WebUtils.setWebAppRootSystemProperty(servletContext);     }     //得到自定义的log4j配置文件位置    String location = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);     if (location != null) {         // 主要是获取log4j配置文件的真实路径        try {             // Resolve property placeholders before potentially resolving a real path.             location = ServletContextPropertyUtils.resolvePlaceholders(location, servletContext);            // 判断是否是资源路径,以classpath:" or "file:"开头             if (!ResourceUtils.isUrl(location)) {                 // 获取配置文件的真实路径                location = WebUtils.getRealPath(servletContext, location); }                 // Write log message to server log.                 servletContext.log("Initializing log4j from [" + location + "]");                 // 读取 log4jRefreshInterval 属性                String intervalString = servletContext.getInitParameter(REFRESH_INTERVAL_PARAM);                 if (StringUtils.hasText(intervalString)) {                     try {                         long refreshInterval = Long.parseLong(intervalString);                         //配置log4j并启动一个监控线程                        org.springframework.util.Log4jConfigurer.initLogging(location, refreshInterval);                     } catch (NumberFormatException ex) {                         throw new IllegalArgumentException("Invalid 'log4jRefreshInterval' parameter: " + ex.getMessage());   }                 } else {                     //配置log4j                    org.springframework.util.Log4jConfigurer.initLogging(location);                 }         } catch (FileNotFoundException ex) {             throw new IllegalArgumentException("Invalid 'log4jConfigLocation' parameter: " + ex.getMessage());         }     }}//设置WebAppRoot属性public static void setWebAppRootSystemProperty(ServletContext servletContext) throws IllegalStateException {       Assert.notNull(servletContext, "ServletContext must not be null");        String root = servletContext.getRealPath("/");        if (root == null) {             throw new IllegalStateException( "Cannot set web app root system property when WAR file is not expanded");         }        String param = servletContext.getInitParameter(WEB_APP_ROOT_KEY_PARAM);        String key = (param != null ? param : DEFAULT_WEB_APP_ROOT_KEY);        String oldValue = System.getProperty(key);        if (oldValue != null && !StringUtils.pathEquals(oldValue, root)) {             throw new IllegalStateException( "Web app root system property already set to different value: '" + key + "' = [" + oldValue + "] instead of [" + root + "] - " + "Choose unique values for the 'webAppRootKey' context-param in your web.xml files!");         }        System.setProperty(key, root);        servletContext.log("Set web app root system property: '" + key + "' = [" + root + "]");}

源码流程解析

1.exposeWebAppRoot判断是否暴露WebAppRoot,默认是暴露.可以自定义,如下配置

log4jExposeWebAppRoot
true

2.如果暴露,将设置系统属性为 webapp.root = servletContext.getRealPath("/")(项目部署根路径),也可以自定义webAppRootKey,如下

webAppRootKey
logtopic.root

这样就会设置系统属性 logtopic.root = servletContext.getRealPath("/"),再配置文件中就可以用${logtopic.root}代替部署根路径

3.String location = servletContext.getInitParameter(CONFIG_LOCATION_PARAM)获取Log4j自定义配置路径,如果不为空解析得到真实路径location = WebUtils.getRealPath(servletContext, location)如下配置

log4jConfigLocation
classpath:log4j.properties

配置有两种情况

- `classpath`开头,找到项目类路径,最后用ClassLoader加载,所以不要用"/"开头
log4jConfigLocation
classpath:log4j.properties
  • file开头,配置文件具体位置
log4jConfigLocation
file:///Users/lh/Desktop/log4j.properties

4.读取log4jRefreshInterval属性,表示每隔一段时间,会重新读取配置文件,重新配置Log4j,自动检测更新。会单独启动一个线程来监控定时监控,单位是(ms).配置如下:

log4jRefreshInterval
2000

5.最后调用log4j自身的API进行配置

if (resolvedLocation.toLowerCase().endsWith(XML_FILE_EXTENSION)) {     DOMConfigurator.configure(url);}else {     PropertyConfigurator.configure(url);}

转载于:https://www.cnblogs.com/beginmind/p/5989036.html

你可能感兴趣的文章
VC中怎么读取.txt文件
查看>>
如何清理mac系统垃圾
查看>>
企业中最佳虚拟机软件应用程序—Parallels Deskto
查看>>
Nginx配置文件详细说明
查看>>
怎么用Navicat Premium图标编辑器创建表
查看>>
Spring配置文件(2)配置方式
查看>>
MariaDB/Mysql 批量插入 批量更新
查看>>
ItelliJ IDEA开发工具使用—创建一个web项目
查看>>
solr-4.10.4部署到tomcat6
查看>>
切片键(Shard Keys)
查看>>
淘宝API-类目
查看>>
virtualbox 笔记
查看>>
Git 常用命令
查看>>
驰骋工作流引擎三种项目集成开发模式
查看>>
SUSE11修改主机名方法
查看>>
jdk6.0 + Tomcat6.0的简单jsp,Servlet,javabean的调试
查看>>
Android:apk签名
查看>>
2(2).选择排序_冒泡(双向循环链表)
查看>>
MySQL 索引 BST树、B树、B+树、B*树
查看>>
微信支付
查看>>