`

【飞天奔月出品】剖析logback4:spring LogbackConfigListener

    博客分类:
  • log
阅读更多

看过我前面几个帖子的伙伴也许知道, logback会自动加载配置文件

 

那为毛,官方还会提供spring

ch.qos.logback.ext.spring.web.LogbackConfigListener 呢?

参见 

https://github.com/qos-ch/logback-extensions/blob/master/spring/src/main/java/ch/qos/logback/ext/spring/web/LogbackConfigListener.java

 

maven 引用 

 

 

    <dependency>
      <groupId>org.logback-extensions</groupId>
      <artifactId>logback-ext-spring</artifactId>
      <version>0.1.2</version>
      <scope>compile</scope>
    </dependency>

 

 

此外,号外下  LogbackConfigListener  和spring-web-${version}.jar 里面的org.springframework.web.util.Log4jConfigListener  的作者是同一个人  : Juergen Hoeller

 



 

 



 

 

 

大家对于spring 可能只知道spring 之父 Rod Johnson,而Juergen Hoeller 也是牛人,Juergen Hoeller是Spring框架的联合创始人,自2003年2月Rod的Interface21将Spring框架开源起,他就是最活跃的Spring开发者。Juergen是一名经验丰富的咨询师,擅长于Web应用、事务管理、O/R映射技术以及轻量级远程调用。

 

大家有兴趣的话,可以在这里看到他的专访

http://www.infoq.com/cn/interviews/hoeller-spring31-32-cn

 

spring4是他主导的

https://spring.io/team/jhoeller

 

好吧,我们回到正题, "为毛官方还会提供spring ch.qos.logback.ext.spring.web.LogbackConfigListener 呢?"

 

在这里我们可以找到说明

https://github.com/qos-ch/logback-extensions/wiki/Spring

 

 

官方给出了两点理由:

  • 通过web.xml ServletContextListener or Servlet for Spring-based web applications初始化 Intialize Logback .
  • 使用 Spring configuration (e.g. applicationContext.xml and Spring Java config) 配置Logback components (Appenders, Layouts, 等) .

注意: ch.qos.logback.ext.spring.web.LogbackConfigListener 需要 spring 3.1.1+版本

 

看了下usage 感觉没有什么用

 

比如 ,可以在spring applicationContext.xml bean 里面 配置 相关的 logback appender 

 

 

<beans ...>
    ...
    <bean id="consoleAppender" class="ch.qos.logback.core.ConsoleAppender" init-method="start" destroy-method="stop">
        <property name="context" value="#{ T(org.slf4j.LoggerFactory).getILoggerFactory() }"/>
        <property name="encoder">
            <bean class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" init-method="start" destroy-method="stop">
                <property name="context" value="#{ T(org.slf4j.LoggerFactory).getILoggerFactory() }"/>
                <property name="pattern" value="%date %-5level [%thread] %logger{36} %m%n"/>
            </bean>
        </property>
    </bean>
    ...
</beans>

 

不过 usage 实用的(适合我用的)没什么, 但是我们还是追追源码,瞅瞅有啥好玩的

 

 

 

public class LogbackConfigListener implements ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent event) {
        WebLogbackConfigurer.shutdownLogging(event.getServletContext());
    }

    @Override
    public void contextInitialized(ServletContextEvent event) {
        WebLogbackConfigurer.initLogging(event.getServletContext());
    }
}

 

ch.qos.logback.ext.spring.web.LogbackConfigListener 就是个普通的  ServletContextListener

核心代码 ,调用的   ch.qos.logback.ext.spring.web.WebLogbackConfigurer.initLogging(ServletContext)

 

 

 

 public static void initLogging(ServletContext servletContext) {
        // Expose the web app root system property.
        if (exposeWebAppRoot(servletContext)) {
            WebUtils.setWebAppRootSystemProperty(servletContext);
        }

        // Only perform custom Logback initialization in case of a config file.
        String location = servletContext.getInitParameter(CONFIG_LOCATION_PARAM);
        if (location != null) {
            // Perform actual Logback initialization; else rely on Logback's default initialization.
            try {
                // Resolve system property placeholders before potentially resolving real path.
                location = SystemPropertyUtils.resolvePlaceholders(location);
                // Return a URL (e.g. "classpath:" or "file:") as-is;
                // consider a plain file path as relative to the web application root directory.
                if (!ResourceUtils.isUrl(location)) {
                    location = WebUtils.getRealPath(servletContext, location);
                }

                // Write log message to server log.
                servletContext.log("Initializing Logback from [" + location + "]");

                // Initialize
                LogbackConfigurer.initLogging(location);
            } catch (FileNotFoundException ex) {
                throw new IllegalArgumentException("Invalid 'logbackConfigLocation' parameter: " + ex.getMessage());
            } catch (JoranException e) {
                throw new RuntimeException("Unexpected error while configuring logback", e);
            }
        }

        //If SLF4J's java.util.logging bridge is available in the classpath, install it. This will direct any messages
        //from the Java Logging framework into SLF4J. When logging is terminated, the bridge will need to be uninstalled
        try {
            Class<?> julBridge = ClassUtils.forName("org.slf4j.bridge.SLF4JBridgeHandler", ClassUtils.getDefaultClassLoader());
            
            Method removeHandlers = ReflectionUtils.findMethod(julBridge, "removeHandlersForRootLogger");
            if (removeHandlers != null) {
                servletContext.log("Removing all previous handlers for JUL to SLF4J bridge");
                ReflectionUtils.invokeMethod(removeHandlers, null);
            }
            
            Method install = ReflectionUtils.findMethod(julBridge, "install");
            if (install != null) {
                servletContext.log("Installing JUL to SLF4J bridge");
                ReflectionUtils.invokeMethod(install, null);
            }
        } catch (ClassNotFoundException ignored) {
            //Indicates the java.util.logging bridge is not in the classpath. This is not an indication of a problem.
            servletContext.log("JUL to SLF4J bridge is not available on the classpath");
        }
    }

 

 

关于WebUtils.setWebAppRootSystemProperty(ServletContext)

 

我们先来看看 

 

 

// Expose the web app root system property.
        if (exposeWebAppRoot(servletContext)) {
            WebUtils.setWebAppRootSystemProperty(servletContext);
        }

 

意思是 如果web.xml配置了  

 

<context-param>
		<param-name>logbackExposeWebAppRoot</param-name>
		<param-value>true</param-value>
	</context-param>

 或者没有配置 logbackExposeWebAppRoot

 

会调用  org.springframework.web.util.WebUtils.setWebAppRootSystemProperty(ServletContext)

 

	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 + "]");
	}

 

对于 这个方法 ,如果使用过  org.springframework.web.util.WebAppRootListener 的同学,应该会熟悉

 

可以看到 

org.springframework.web.util.WebAppRootListener

ch.qos.logback.ext.spring.web.WebLogbackConfigurer

org.springframework.web.util.Log4jWebConfigurer

会调用这个方法



 

那么这个方法到底是做什么的呢?

 

step1.从 servletContext init param中查找 webAppRootKey参数值

 

step2:如果没有配置 webAppRootKey 那么使用 "webapp.root",从 java.lang.System.getProperty(String) 查到值

 

step3:如果从 System Property 得到的值 不是null,并且 值不等于 servletContext.getRealPath("/")

那么就抛

 

 

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!")

 

step4:设置属性到JVM Property

 

System.setProperty(key, root);

 

 

 好,分析完了  WebUtils.setWebAppRootSystemProperty(ServletContext)

 

 

继续看 ch.qos.logback.ext.spring.web.WebLogbackConfigurer.initLogging(ServletContext)

 

 

从 servletContext 获得 logbackConfigLocation参数

 

如果不是null, 那么 调用 org.springframework.util.SystemPropertyUtils.resolvePlaceholders(String)

来解析这个配置参数值 

 

也就是说, logbackConfigLocation配置的路径支持 ${}这样的写法 

 

注意 :  org.springframework.util.SystemPropertyUtils.resolvePlaceholders(String) 调用的是  org.springframework.util.SystemPropertyUtils.SystemPropertyPlaceholderResolver

 

而这个 Resolver只会使用 JVM properties 以及操作系统的env 环境变量

 



 

 

我了个去咧, 要是使用的是 ServletContextPropertyUtils  org.springframework.web.util.ServletContextPropertyUtils.ServletContextPlaceholderResolver就好了 

 

大家来看看 

 

org.springframework.web.util.Log4jWebConfigurer.initLogging(ServletContext) 就是使用的 ServletContextPropertyUtils

 

 

 



 

 

话又说回来了 ,org.springframework.web.util.ServletContextPropertyUtils  @since 3.2.2

期待 LogbackConfigListener 升级吧

 

 

 

  • 大小: 26.2 KB
  • 大小: 27.7 KB
  • 大小: 19.7 KB
  • 大小: 19.4 KB
  • 大小: 25.9 KB
0
1
分享到:
评论
1 楼 飞天奔月 2016-04-07  
从0.1.4 version 开始,使用Using spring servlet context property utils insted.


https://github.com/qos-ch/logback-extensions/pull/34/commits/520ab5ae2fb4b4ac4081cb2b37d08d30cc1f3a47

相关推荐

Global site tag (gtag.js) - Google Analytics