关于tomcat的日志框架
1.摘要
最近部门在做JVM监控工具的时候碰到的一个打印日志的问题。
2.背景
- 应用部署在tomcat中,使用log4j作为日志框架。
- 监控工具使用javaagent的方式随tomcat启动,同样使用log4j作为日志框架。
3.现象
- 在tomcat目录下放置log4j jar包和log4j.properties文件,启动服务后发现监控工具日志正常,原有应用的日志不见了。
- 只放置jar包,保留应用中的log4j.properties,所有日志都没有了。
- 只放jar包,agent启动时sleep100秒,两边日志都正常了。
4.原因
- javaagent的premain和java main在同一个jvm中执行,使用的是system classloader。
- system classloader的搜索路径是启动命令行里的classpath(-cp)。
-
tomcat webapps下的应用是由WebappClassLoader加载的,搜索路径是WEB-INF/classes,WEB-INF/lib,找不到则调用父加载器,继承关系如下:
1234567Bootstrap|System|Common/ \Webapp1 Webapp2 ...默认情况下(直接类引用的方式),父加载器是感知不到子加载器的,或者说,java agent加载不了webapps下的jar,webapps可以加载system classpath下的jar。
-
log4j加载log4j.properties时会先取thread context classloader的classpath。如果由java agent初始化,会找system classpath;如果由webapp初始化,则会找webapp classpath(WEB_INF/classes),所以sleep一段时间有奇效。
-
tomcat的日志使用了jdk提供的logging框架,具体实现是由$CATALINA_HOME/bin/tomcat-juli.jar提供,这个jar包在catalina.sh里被加入到system classpath:
1234rem Add tomcat-juli.jar to classpathrem tomcat-juli.jar can be over-ridden per instanceif not exist "%CATALINA_BASE%\bin\tomcat-juli.jar" goto juliClasspathHomeset "CLASSPATH=%CLASSPATH%;%CATALINA_BASE%\bin\tomcat-juli.jar"$CATALINA_HOME/conf/logging.properties文件配置了tomcat本身的日志系统,包含juli file和console两部分,console被输出为catalina.out,juli file包含localhost_xxx、host_manager等。
5.解决方案
-
在代码里使用java.util.logging,$CATALINA_HOME/conf/logging.properties里新增一个输出的handler,agent就可以直接使用这套日志系统了。
-
配置tomcat使用log4j,所有日志通过apache common-logging输出,具体参考这里
根据官方文档里的说明,这里对原有的tomcat-juli做了修改,主要是增加了apache common logging。也就是说juli由system classloader加载,log4j由common classloader加载,由acl完成logger实现类的转移。
6.参考资料
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可,转载请注明作者及原网址。
hi “javaagent的premain和java main在同一个jvm中执行,使用的是system classloader” 这句话的意思是说和tomcat共用一个jvm吗?怎么做到的?
这个是java的特性之一,类似于java提供了对main函数的拦截器。启动的时候通过-javaagent参数可以指定javaagent,详细的描述可以参考http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html