从log4j切换到logback后项目无法启动

科技资讯 投稿 5600 0 评论

从log4j切换到logback后项目无法启动

1、背景

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

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

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

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

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

4、问题分析

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

使用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、问题延伸

    定义了【org.apache.log4j.Logger】对象,确保使用了log4j的老项目代码不至于编译不通过
package org.apache.log4j;

import org.slf4j.Marker;

public class Logger extends Category {
    ...
}
  1. 将【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 = (LocationAwareLoggerthis.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((Markernull, CATEGORY_FQCN, 20, message, (Throwablenull;
    }
    
    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】也是起到相同的作用。

其他示例

编程笔记 » 从log4j切换到logback后项目无法启动

赞同 (34) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽