从log4j切换到logback后项目无法启动
1、背景
有个旧项目之前使用的是log4j2来打印日志的,因为某些原因,同事想换成logback。
换成logback改动也很简单,大致就一下2步:
删除log4j2.xml配置,新增logback.xml配置。剔除掉log4j相关的jar
引入slf4j (其实之前使用log4j2的时候就已经引入了,只是有些地方写法不规范),
代码【import org.apache.log4j.Logger】改成【import org.slf4j.Logger】(以及其他类似修改)
2、现象
全部改了之后,按道理说,应该就可以正常打印了。
但是启动发现,日志报错:
ERROR {org.springframework.web.context.ContextLoader:356} - Context initialization failed
java.lang.NoClassDefFoundError: org/apache/log4j/Logger
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
at java.lang.Class.getDeclaredMethods(Class.java:1975)
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:612)
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:524)
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:510)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:241)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1069)
★小技巧:
报错的日志比较多,甚至有些报错看起来有点莫名其妙。
不要慌,先找到最早的案发现场日志,或者搜一下关键字。
因为我们改的是日志,所以可以在报错信息中搜一下log/log4j 等关键字
3、问题排查
看到这里第一反应应该是有代码没改全了。全局搜一下log4j关键字,果然发现还有一处:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
...
<property name="filters" value="config,stat,log4j,wall" /> <!-- 这里的log4j 需要改成 slf4j -->
...
</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上,进而集中输出
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.1</version>
</dependency>
引入log4j-over-slf4j之后,再启动,就ok了~
5、问题延伸
再回过头看一下,log4j-over-slf4j到底给我们做了什么?
- 定义了【org.apache.log4j.Logger】对象,确保使用了log4j的老项目代码不至于编译不通过
package org.apache.log4j;
import org.slf4j.Marker;
public class Logger extends Category {
...
}
- 将【org.apache.log4j.Logger】的打印动作偷偷转移到slf4j上。
Logger继承自Category,并且实现了info、warn、error等打印日志的方法
package org.apache.log4j;
import java.util.Enumeration;
import org.apache.log4j.helpers.NullEnumeration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import org.slf4j.spi.LocationAwareLogger;
public class Category {
private static final String CATEGORY_FQCN = Category.class.getName();
private String name;
protected Logger slf4jLogger;
private LocationAwareLogger locationAwareLogger;
private static Marker FATAL_MARKER = MarkerFactory.getMarker("FATAL");
Category(String name) {
this.name = name;
this.slf4jLogger = LoggerFactory.getLogger(name);
if (this.slf4jLogger instanceof LocationAwareLogger) {
this.locationAwareLogger = (LocationAwareLogger)this.slf4jLogger;
}
}
public Level getEffectiveLevel() {
if (this.slf4jLogger.isTraceEnabled()) {
return Level.TRACE;
} else if (this.slf4jLogger.isDebugEnabled()) {
return Level.DEBUG;
} else if (this.slf4jLogger.isInfoEnabled()) {
return Level.INFO;
} else {
return this.slf4jLogger.isWarnEnabled() ? Level.WARN : Level.ERROR;
}
}
public void info(Object message) {
this.differentiatedLog((Marker)null, CATEGORY_FQCN, 20, message, (Throwable)null);
}
void differentiatedLog(Marker marker, String fqcn, int level, Object message, Throwable t) {
String m = this.convertToString(message);
if (this.locationAwareLogger != null) {
this.locationAwareLogger.log(marker, fqcn, level, m, (Object[])null, t);
} else {
switch(level) {
case 0:
this.slf4jLogger.trace(marker, m);
break;
case 10:
this.slf4jLogger.debug(marker, m);
break;
case 20:
this.slf4jLogger.info(marker, m);
break;
case 30:
this.slf4jLogger.warn(marker, m);
break;
case 40:
this.slf4jLogger.error(marker, m);
}
}
}
}
其实,类似的还有【jcl-over-slf4j】也是起到相同的作用。
例如:把Commons logging,log4j和java.util.logging桥接到SLF4J,底层使用logback的case。其他示例

从log4j切换到logback后项目无法启动的更多相关文章
- mac下 安装tomcat 后项目无法启动以及 错误 找不到或无法加载主类
按照网上的步骤,在mac上安装tomcat后,写个简单的测试类报错:错误 找不到或无法加载主类 Class JavaLaunchHelper is implemented in both /Libra ...
- 老板下了死命令,要把日志系统切换到Logback
Log4j 介绍过了,SLF4J 也介绍过了,那接下来,你懂的,Logback 就要隆重地登场了,毕竟它哥仨有一个爹,那就是巨佬 Ceki Gulcu. 就在昨天,老板听我说完 Logback 有多牛 ...
- Log4j、Log4j 2、Logback、SFL4J、JUL、JCL的比较
Log4j.Log4j 2.Logback.SFL4J.JUL.JCL的比较 之前就知道有好几种日志框架,但是一直都是听别人讲,在什么时候该用何种logger,哪种logger比较好……一直对Log4 ...
- 从Log4j迁移到LogBack的理由
英文原文:Reasons to prefer logback over log4j 无论从设计上还是实现上,Logback相对log4j而言有了相对多的改进.不过尽管难以一一细数,这里还是列举部分理由 ...
- log4j升级到logback
虽然现在log4j已经基本上不更新很久了,但实际上升级log4j到logback最大的难度并不在于本身的替换,而是现有大量的三方jar依然使用log4j,以至于无法100%的exclude掉,所以很有 ...
- unity 代码有调整,重新导出 iOS 最烦的就是 覆盖导出后项目不能打开
unity 代码有调整,重新导出 iOS 最烦的就是 覆盖导出后项目不能打开,原因是 editor 里面的脚本,破坏了 Unity-iPhone.xcodeproj 里面的结构,具体是什么原因,也不 ...
- 微擎修改 icon.jpg 后项目主页未变
微擎修改 icon.jpg 后项目主页Logo未变 产生原因: 设置了自定义图标,但系统未找到该图标,就选择使用默认的起始图标 解决办法: 在项目根目录位置上传一个图标名为 icon-custom.j ...
- Spring Boot项目指定启动后执行的操作
Spring Boot项目指定启动后执行的操作: (1)实现CommandLineRunner 接口 (2)重写run方法 (3)声明执行顺序@Order(1),数值越小,优先级越高 (4)如果需要注 ...
- 【项目启动】 tomcat启动,项目无法启动,无法正常访问/项目可以启动,报错:java.lang.ClassNotFoundException: ContextLoaderListener
使用maven搭建项目(这个错误和是不是使用maven搭建项目没有关系),然后部署到tomcat中运行. 出现问题1: tomcat跑起来了,但是启动时间很短,没有报错,项目不能正常访问 项目启动时间 ...
- zw字王《中华大字库》2018版升级项目正式启动
zw字王<中华大字库>2018版升级项目正式启动 https://www.cnblogs.com/ziwang/p/9500537.html 这次升级是和字库协会一起合作,首批推出的字体, ...
随机推荐
- 一天十道Java面试题----第二天(HashMap和hashTable的区别--------》sleep、wait、join)
这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 11.HashMap和HashTable的区别及底层实现 12.ConcurrentHashMap原理简述,jdk7和jd ...
- 测试架构师CAP原理(最简单)
测试架构师CAP原理(最简单) 很多人都不是很了解CAP理论,其实CAP很简单,不要想复杂了! C:一致性,就是数据一致性,就是数据不出错! A:可用性,就是说速度快,不延迟,无论请求成功失败都很快返 ...
- ARM TrustZone白皮书部分阅读
嵌入式系统安全的一些解决方法及缺陷 外部硬件安全模块:在主SoC之外包含一个专用的硬件安全模块或可信元件,e.g. 手机的SIM卡.隔离仅限于可以从非易失性存储器运行的相对静态程序 内部硬件安全模块: ...
- Codeforces Round #828 (Div. 3) E2. Divisible Numbers (分解质因子,dfs判断x,y)
题目链接 题目大意 给定a,b,c,d四个数,其中a<c,b<c,现在让你寻找一对数(x,y),满足一下条件: 1. a<x<c,b<y<d 2. (x*y)%(a ...
- easyclick 学习
什么是EC EasyClick 写自动化脚本,使用的编辑器 Idea 运行模式有两种:无障碍模式.代理模式 注释 三种类型: 1./** 对程序作介绍,解释说明用 / 2./* 多行注释 第二行 / ...
- Linux学习环境搭建流程
Linux学习环境搭建 Vmware安装 VMware下载:https://www.vmware.com/go/getworkstation-win 运行安装程序,该重启安装驱动就重启,不需要就下一步 ...
- java学习之注解
0x00前言 1.注解是什么: (1)可以叫做注释类型,注解是一种引用数据类型,编译后也是生成class文件 (2)提供信息给编译器: 编译器可以利用注解来探测错误和警告信息 比如 @Override ...
- go mod常用命令 已经 常见问题
最近接触到go mod,网上查了查资料,这里记录一下. 1 介绍 1.1.go mod是什么 go mod 是Golang 1.11 版本引入的官方包(package)依赖管理工具,用于解决之前没有地 ...
- 优雅的重启uwsgi 告别uwsgi reload过程中造成的无法请求、请求延迟等问题
[uwsgi]#使用优雅重启 lazy-apps = true #监听monitor文件 当monitor文件发生改变是重启uwsgi touch-chain-reload = /home/monit ...
- Centos 7.6 安装部署 openGauss 3.1.0 企业版一主两备集群
一.安装环境设置 1.1 硬件环境 名称 最低配置 建议配置 测试配置 服务器数量 3 略 略 硬盘 * 至少1GB用于安装openGauss的应用程序.* 每个主机需大约300MB用于元数据存储.* ...