经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
从log4j切换到logback后项目无法启动
来源:cnblogs  作者:yejg1212  时间:2023/1/18 8:43:12  对本文有异议

1、背景

有个旧项目之前使用的是log4j2来打印日志的,因为某些原因,同事想换成logback。

换成logback改动也很简单,大致就一下2步:

  1. 删除log4j2.xml配置,新增logback.xml配置。剔除掉log4j相关的jar

  2. 引入slf4j (其实之前使用log4j2的时候就已经引入了,只是有些地方写法不规范),

    代码【import org.apache.log4j.Logger】改成【import org.slf4j.Logger】(以及其他类似修改)

2、现象

全部改了之后,按道理说,应该就可以正常打印了。

但是启动发现,日志报错:

  1. ERROR {org.springframework.web.context.ContextLoader:356} - Context initialization failed
  2. java.lang.NoClassDefFoundError: org/apache/log4j/Logger
  3. at java.lang.Class.getDeclaredMethods0(Native Method)
  4. at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
  5. at java.lang.Class.getDeclaredMethods(Class.java:1975)
  6. at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:612)
  7. at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:524)
  8. at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:510)
  9. at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:241)
  10. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1069)
  11. ★小技巧:
  12. 报错的日志比较多,甚至有些报错看起来有点莫名其妙。
  13. 不要慌,先找到最早的案发现场日志,或者搜一下关键字。
  14. 因为我们改的是日志,所以可以在报错信息中搜一下log/log4j 等关键字

3、问题排查

看到这里第一反应应该是有代码没改全了。全局搜一下log4j关键字,果然发现还有一处:

  1. <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
  2. ...
  3. <property name="filters" value="config,stat,log4j,wall" /> <!-- 这里的log4j 需要改成 slf4j -->
  4. ...
  5. </bean>

DruidDatasource中定义了com.alibaba.druid.filter.logging.LogFilter,他有3个子类,分别对应不同的日志打印实现方式

  • com.alibaba.druid.filter.logging.Slf4jLogFilter
  • com.alibaba.druid.filter.logging.Log4jFilter
  • com.alibaba.druid.filter.logging.CommonsLogFilter

那我们怎么知道是配置成slf4j、Slf4j、还是Slf4jLogger呢?可以看这里druid源码文件【META-INF/druid-filter.properties】

druid.filters.default=com.alibaba.druid.filter.stat.StatFilter
druid.filters.stat=com.alibaba.druid.filter.stat.StatFilter
druid.filters.mergeStat=com.alibaba.druid.filter.stat.MergeStatFilter
druid.filters.counter=com.alibaba.druid.filter.stat.StatFilter
druid.filters.encoding=com.alibaba.druid.filter.encoding.EncodingConvertFilter
druid.filters.log4j=com.alibaba.druid.filter.logging.Log4jFilter
druid.filters.slf4j=com.alibaba.druid.filter.logging.Slf4jLogFilter
druid.filters.commonlogging=com.alibaba.druid.filter.logging.CommonsLogFilter
druid.filters.commonLogging=com.alibaba.druid.filter.logging.CommonsLogFilter
druid.filters.wall=com.alibaba.druid.wall.WallFilter
druid.filters.config=com.alibaba.druid.filter.config.ConfigFilter

无意中发现commonlogging这一行写重复了,哈哈,这个不是我拷贝重的,源码druid-1.0.18就是搞重复了!

改完之后,再启动项目,发现问题依旧啊!

4、问题分析

估计还是有别的地方写明了需要使用log4j,为了验证猜想,我设置了NoClassDefFoundError异常断点,再次debug启动。

进入断点的时候,就发现还有个 HttpSessionManager 代码中写死了【import org.apache.log4j.Logger;】

这个是个第三方的jar,代码是改不了的。就只能另寻他法了。

其实像这种情况,代码写死了使用log4j,想统一成slf4j,slf4j已经提供了解决方法。那就是引入log4j-over-slf4j。

使用log4j-over-slf4j取代log4j,这样log4j接口输出的日志就会通过log4j-over-slf4j路由到SLF4J上,这样即使系统(包含使用的第三方jar库,比如dubbo)都可以将日志最终路由到SLF4J上,进而集中输出

  1. <dependency>
  2. <groupId>org.slf4j</groupId>
  3. <artifactId>log4j-over-slf4j</artifactId>
  4. <version>1.7.1</version>
  5. </dependency>

引入log4j-over-slf4j之后,再启动,就ok了~

5、问题延伸

再回过头看一下,log4j-over-slf4j到底给我们做了什么?

  1. 定义了【org.apache.log4j.Logger】对象,确保使用了log4j的老项目代码不至于编译不通过
  1. package org.apache.log4j;
  2. import org.slf4j.Marker;
  3. public class Logger extends Category {
  4. ...
  5. }
  1. 将【org.apache.log4j.Logger】的打印动作偷偷转移到slf4j上。

Logger继承自Category,并且实现了info、warn、error等打印日志的方法

  1. package org.apache.log4j;
  2. import java.util.Enumeration;
  3. import org.apache.log4j.helpers.NullEnumeration;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.slf4j.Marker;
  7. import org.slf4j.MarkerFactory;
  8. import org.slf4j.spi.LocationAwareLogger;
  9. public class Category {
  10. private static final String CATEGORY_FQCN = Category.class.getName();
  11. private String name;
  12. protected Logger slf4jLogger;
  13. private LocationAwareLogger locationAwareLogger;
  14. private static Marker FATAL_MARKER = MarkerFactory.getMarker("FATAL");
  15. Category(String name) {
  16. this.name = name;
  17. this.slf4jLogger = LoggerFactory.getLogger(name);
  18. if (this.slf4jLogger instanceof LocationAwareLogger) {
  19. this.locationAwareLogger = (LocationAwareLogger)this.slf4jLogger;
  20. }
  21. }
  22. public Level getEffectiveLevel() {
  23. if (this.slf4jLogger.isTraceEnabled()) {
  24. return Level.TRACE;
  25. } else if (this.slf4jLogger.isDebugEnabled()) {
  26. return Level.DEBUG;
  27. } else if (this.slf4jLogger.isInfoEnabled()) {
  28. return Level.INFO;
  29. } else {
  30. return this.slf4jLogger.isWarnEnabled() ? Level.WARN : Level.ERROR;
  31. }
  32. }
  33. public void info(Object message) {
  34. this.differentiatedLog((Marker)null, CATEGORY_FQCN, 20, message, (Throwable)null);
  35. }
  36. void differentiatedLog(Marker marker, String fqcn, int level, Object message, Throwable t) {
  37. String m = this.convertToString(message);
  38. if (this.locationAwareLogger != null) {
  39. this.locationAwareLogger.log(marker, fqcn, level, m, (Object[])null, t);
  40. } else {
  41. switch(level) {
  42. case 0:
  43. this.slf4jLogger.trace(marker, m);
  44. break;
  45. case 10:
  46. this.slf4jLogger.debug(marker, m);
  47. break;
  48. case 20:
  49. this.slf4jLogger.info(marker, m);
  50. break;
  51. case 30:
  52. this.slf4jLogger.warn(marker, m);
  53. break;
  54. case 40:
  55. this.slf4jLogger.error(marker, m);
  56. }
  57. }
  58. }
  59. }

其实,类似的还有【jcl-over-slf4j】也是起到相同的作用。

例如:把Commons logging,log4j和java.util.logging桥接到SLF4J,底层使用logback的case。其他示例

原文链接:https://www.cnblogs.com/yejg1212/p/17057422.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号