本文旨在解决tomcat过滤器在从log4j 1.x迁移到Log4j 2.x后,无法正确获取web.xml中配置的初始化参数问题。核心在于Log4j 2.x配置文件的语法错误导致应用启动异常,进而影响了servlet过滤器参数的加载。通过提供正确的Log4j 2.x XML配置示例,并详细解释其结构和最佳实践,帮助开发者顺利完成日志框架升级,确保应用功能正常运行。
问题描述
在将java web应用程序的日志框架从log4j 1.x升级到log4j 2.x(例如,从log4j-1.2.15.jar迁移到log4j v2.17.1)后,部署在tomcat 9上的应用程序遇到了一个异常行为。尽管web.xml中为自定义servlet过滤器(例如com.demo.custom.Filter.newsession)配置了初始化参数(init-param),但在过滤器init()方法中通过filterconfig.getinitparameter()获取这些参数时,却返回了NULL值。
例如,以下web.xml配置:
<filter> <filter-name>newsession</filter-name> <display-name>newsession</display-name> <description>newsession</description> <filter-class>com.demo.custom.filter.NewSession</filter-class> <init-param> <param-name>DomainName</param-name> <param-value>.testlab.com</param-value> </init-param> <init-param> <param-name>DomConfigFile</param-name> <param-value>/opt/tomcat/webapp/demoapp/WEB-INF/classes/NAM_log4j.xml</param-value> </init-param> <init-param> <param-name>customPropFile</param-name> <param-value>/opt/tomcat/webapp/demoapp/WEB-INF/classes/custom_resources.properties</param-value> </init-param> </filter> <filter-mapping> <filter-name>newsession</filter-name> <url-pattern>/idff/sso</url-pattern> </filter-mapping>
在过滤器NewSession的init方法中,尝试获取参数:
public void init(FilterConfig fConfig) throws ServletException { try{ domainName = fConfig.getInitParameter("DomainName"); domConfigFile=fConfig.getInitParameter("DomConfigFile"); custom_resources=fConfig.getInitParameter("customPropFile"); }catch(Exception e){ myLogger.Error(e.getMessage()); } // ... 后续对null值的处理 if(domConfigFile==null){ myLogger.error("domConfigFile got null from web.xml, setting hardcoded value"); // ... } if(custom_resources==null){ myLogger.error("custom_resources got null from web.xml, setting hardcoded value"); // ... } }
日志中会打印出类似以下错误信息:
ERROR com.demo.custom.filter.NewSession - domConfigFile got null from web.xml, setting hardcoded value ERROR com.demo.custom.filter.NewSession - custom_resources got null from web.xml, setting hardcoded value main ERROR Error processing element category ([Configuration: null]): CLASS_NOT_FOUND main ERROR Unknown Object "root" of type org.apache.Logging.log4j.core.config.LoggerConfig is ignored: try nesting it inside one of: ["Appenders", "Loggers", "Properties", "Scripts", "CustomLevels"]
这些错误表明,虽然过滤器本身被加载,但其初始化参数未能正确传递。更重要的是,日志中还出现了Log4j 2.x配置相关的错误,暗示问题的根源可能在于Log4j 2.x的配置不当。
根本原因分析
表面上看,问题是过滤器参数获取失败,但结合Log4j 2.x的错误日志,可以推断出更深层次的原因:Log4j 2.x的配置文件存在语法错误或使用了Log4j 1.x的配置元素,导致Log4j 2.x初始化失败,进而影响了整个应用程序的启动流程,包括Servlet容器对web.xml中过滤器参数的正确解析和传递。
具体的错误提示Error processing element category ([Configuration: null]): CLASS_NOT_FOUND和Unknown object “root” of type org.apache.logging.log4j.core.config.LoggerConfig is ignored: try nesting it inside one of: [“Appenders”, “Loggers”, “Properties”, “Scripts”, “CustomLevels”]清晰地指出了以下两点:
- category元素: 这是Log4j 1.x配置中用于定义Logger的元素。Log4j 2.x不再使用category,而是使用Logger元素。当Log4j 2.x尝试解析包含category的配置文件时,会因为不识别该元素而报错。
- root元素放置错误: 在Log4j 2.x的XML配置中,Root Logger必须嵌套在<Loggers>元素内部,而不是直接作为<Configuration>的子元素。错误信息明确指出”root” … is ignored: try nesting it inside one of: [“Appenders”, “Loggers”, …]。
这些Log4j配置错误可能导致日志系统无法正常初始化,甚至可能中断Tomcat的Web应用部署过程,从而影响到其他组件(如Servlet过滤器)的正常初始化。当Log4j 1.x的JAR包被移除后,Log4j 2.x成为唯一的日志实现,其配置的正确性变得至关重要。
解决方案
解决此问题的关键在于提供一个符合Log4j 2.x规范的、结构正确的XML配置文件。通过修正Log4j 2.x的配置文件,确保其能够正确解析和初始化,从而使应用程序能够顺利启动,并让Tomcat正确加载并传递过滤器初始化参数。
以下是一个修正后的Log4j 2.x配置示例(假设文件名为log4j2.xml或TEST_log4j.xml,并放置在classpath中):
<Configuration status="WARN"> <!-- status属性用于设置Log4j自身的日志级别 --> <Appenders> <console name="STDOUT" target="SYSTEM_OUT"> <PatternLayout pattern="%d{MM/dd HH:mm:ss} %-5p %30.30c %x - %m%n"/> </Console> <RollingFile name="RollingFile" fileName="/opt/tomcat/logs/MyCustomClassLogs.log" filePattern="/opt/tomcat/logs/MyCustomClassLogs.log-%i.log"> <PatternLayout> <pattern>%d{MM/dd HH:mm:ss} %-5p %30.30c %x - %m%n</pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="5 MB" /> </Policies> <DefaultRolloverStrategy max="5"/> </RollingFile> <RollingFile name="ResetClass" fileName="/opt/tomcat/logs/resetTrace.log" filePattern="/opt/tomcat/logs/resetTrace.log-%i.log"> <PatternLayout> <pattern>%d{MM/dd HH:mm:ss} %-5p %30.30c %x - %m%n</pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="5 MB" /> </Policies> <DefaultRolloverStrategy max="5"/> </RollingFile> </Appenders> <Loggers> <Logger name="com.demo.custom.filter.ResetClass" level="TRACE" additivity="false"> <AppenderRef ref="ResetClass"/> </Logger> <Logger name="com.demo.custom.test.MyCustomClass" level="TRACE" additivity="false"> <AppenderRef ref="RollingFile"/> </Logger> <Root level="error"> <AppenderRef ref="STDOUT"/> </Root> </Loggers> </Configuration>
关键修正点说明
- <Configuration>根元素: 这是Log4j 2.x配置文件的根元素。status=”WARN”用于控制Log4j内部日志的输出级别。
- <Appenders>元素: 定义了所有可用的Appender,如Console(输出到控制台)和RollingFile(滚动文件Appender)。
- PatternLayout中的pattern定义了日志输出格式。注意,Log4j 2.x中换行符通常使用%n而不是n,虽然两者在多数情况下都有效。
- RollingFile的filePattern应包含一个索引占位符(如%i)和文件扩展名,以确保滚动日志文件的命名规范。
- Policies定义了日志文件滚动的策略,例如SizeBasedTriggeringPolicy基于文件大小触发滚动。
- DefaultRolloverStrategy定义了滚动文件的最大数量。
- <Loggers>元素: 包含所有自定义Logger和Root Logger的定义。
- <Logger>元素: 用于定义特定包或类的日志级别和Appender引用。
- name属性指定Logger的名称(通常是包名或类名)。
- level属性设置该Logger的日志级别(如TRACE, DEBUG, INFO, WARN, ERROR, FATAL)。
- additivity=”false”是一个重要属性,表示该Logger的日志不会传递给父级Logger(包括Root Logger),从而避免重复输出。如果希望日志同时被父级Logger处理,则设置为true或省略(默认为true)。
- <AppenderRef ref=”AppenderName”/>用于将Logger与之前定义的Appender关联起来。
- <Root>元素: 定义了应用程序的根Logger。
- level属性设置了所有未明确配置的Logger的默认日志级别。
- <AppenderRef ref=”STDOUT”/>将根Logger的输出指向名为STDOUT的Console Appender。
- <Logger>元素: 用于定义特定包或类的日志级别和Appender引用。
通过以上修正,Log4j 2.x配置将符合其规范,能够正确初始化日志系统。一旦日志系统正常工作,Tomcat在部署Web应用时将不再因Log4j配置错误而中断,从而确保过滤器能够正确获取其初始化参数。
最佳实践与注意事项
- 移除所有Log4j 1.x JAR包: 在迁移到Log4j 2.x后,务必从应用程序的WEB-INF/lib目录和Tomcat的lib目录中彻底移除所有Log4j 1.x相关的JAR包(如log4j-1.2.x.jar、log4j-extras.jar等),以避免类路径冲突。
- 引入正确的Log4j 2.x依赖: 确保项目中引入了Log4j 2.x的核心依赖,至少包括log4j-api-2.x.x.jar和log4j-core-2.x.x.jar。如果使用了SLF4J等门面,还需要引入相应的桥接器(如log4j-slf4j-impl-2.x.x.jar)。
- 配置文件命名与位置: Log4j 2.x默认会按特定顺序查找配置文件,例如log4j2.xml、log4j2.json、log4j2.properties。通常将log4j2.xml放置在WEB-INF/classes目录下,使其位于应用程序的classpath中。
- 理解Log4j 1.x与2.x配置差异: Log4j 2.x引入了许多新的概念和配置语法(如插件架构、Async Loggers、Configuration、Appenders、Loggers等),与Log4j 1.x有显著不同。在迁移时,不能简单地复制Log4j 1.x的配置文件,而需要根据Log4j 2.x的文档重新编写。
- 调试Log4j配置: 可以在<Configuration>标签中添加status=”DEBUG”或status=”TRACE”属性,让Log4j在启动时输出自身的配置解析信息,这对于调试配置错误非常有帮助。
总结
当Tomcat过滤器在Log4j 2.x迁移后无法获取web.xml中的初始化参数时,应首先检查Log4j 2.x的配置文件。这类问题往往不是过滤器本身的配置错误,而是日志框架初始化失败导致的连锁反应。通过仔细检查并修正Log4j 2.x XML配置中的语法错误和Log4j 1.x遗留元素,确保日志系统能够正常启动,即可解决此类问题,保证Web应用程序的稳定运行。