Log

日志概览

市面日志框架
JCL

JCL即Jakarta Commons Logging 后进入apache 又名 Apache Commons Logging

<dependency>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
</dependency>
JUL

JUL即 jdk自带的logging 以java.util.logging.Logger为接口的日志组件

log4j1

其仅仅是一个实现的日志框架,并非门面模式的接口

<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
</dependency>

即log4j1框架,使用配置文件为log4j.properties

log4j2

log4j团队做的门面模式框架,但是和log4j1并不兼容,有slf4j、commons-logging门面。
其使用配置文件为 log4j2.xml(xml、yml、json)

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
</dependency>
<dependency>
	<groupId>org.apache.logging.log4j</groupId>
	<artifactId>log4j-core</artifactId>
</dependency>
logback

其依赖jar,官方使用方式中即和slf4j集成

<dependency> 
	<groupId>ch.qos.logback</groupId> 
	<artifactId>logback-core</artifactId> 
</dependency> 
<dependency> 
    <groupId>ch.qos.logback</groupId> 
    <artifactId>logback-classic</artifactId> 
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
</dependency>

使用配置文件 logback.groovy>logback-test.xml>logback.xml
配置文件中只有logback.configurationFile的系统属性是可以更改的,

System.setProperty("logback.configurationFile", "/path/to/config.xml");
slf4j

会在jar包中找org/slf4j/impl/StaticLoggerBinder.class 类,如果找到多个,则随机选取一个。选取的会打印在日志中。然后会拥有ILoggerFactory的实现来走实际的日志实现框架。

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
</dependency>
jar总结

log4j1:
log4j:log4j1的全部内容

log4j2:
log4j-api:log4j2定义的API
log4j-core:log4j2上述API的实现

logback:
logback-core:logback的核心包
logback-classic:logback实现了slf4j的API

commons-logging:
commons-logging:commons-logging的原生全部内容
log4j-jcl:commons-logging到log4j2的桥梁
jcl-over-slf4j:commons-logging到slf4j的桥梁

slf4j转向某个实际的日志框架:
slf4j-jdk14:slf4j到jdk-logging的桥梁
slf4j-log4j12:slf4j到log4j1的桥梁
log4j-slf4j-impl:slf4j到log4j2的桥梁
logback-classic:slf4j到logback的桥梁
slf4j-jcl:slf4j到commons-logging的桥梁

某个实际的日志框架转向slf4j:
jul-to-slf4j:jdk-logging到slf4j的桥梁
log4j-over-slf4j:log4j1到slf4j的桥梁
jcl-over-slf4j:commons-logging到slf4j的桥梁

业务系统现状

在这里插入图片描述

综上可以看出,我们有
log4j1 的1.2.8版本jar 和log4j-over-slf4j:log4j1到slf4j的桥梁
log4j2 的api及core log4j-jcl jcl到log4j的桥梁 log4j-to-slf4j 即log4j到slf4j的桥梁
slf4j 的api 及 log4j-slf4j-impl:slf4j到log4j2的桥梁 slf4j-log4j12:slf4j到log4j1的桥梁
jcl jcl-over-slf4j:commons-logging到slf4j的桥梁
logback 的集成包和实现包
其中 log4j-slf4j-impl 和 log4j-to-slf4j 都是log4j2的包,是不可以互相使用的,会导致log4j和slf4j的互相桥接,引起内存泄露。但是目前在web模块是引用,但是非同版本。是非常危险的。参照 log4j™2官网:http://logging.apache.org/log4j/2.x/runtime-dependencies.html

从上面可以看出,在2.3版本中,我们目前实际使用的是 其他转slf4j然后由log4j-slf4j-impl 最终由log4j2进行实际的日志输出。

新进日志问题

在引入二方包的SDK中,
在这里插入图片描述在这里插入图片描述

二方库中进行了参数打印,并且打印了一个对象。。
升级完二方包后,出现了一个问题,即LOGGER.info 会报 noSuchMethodError异常。
排查方法:

第一

首先对日志包进行了排查和同名类的排查,将依赖中的2.2版本的log4j-api进行了排除,升级到2.3。
无果

第二

我们使用了idea的debug的
在这里插入图片描述

方式,进行了日志打印及反射获取到该方法里面的LOGGER并进行打印,都是没有问题的。无果

第三

考虑是否是依赖包发生了桥接问题,从而导致其找不到实际需要的具体实现,于是将项目中关于log4j及slf4j全部依赖进行排除,并引入最新的依赖关系即

        <!-- commons-logging到slf4j的桥梁 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>

        <!-- java.util.logging到slf4j的桥梁 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
        </dependency>
        
        <!-- log4j1 官方重写至log4j2  -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-1.2-api</artifactId>
        </dependency>
         <!-- log4j2对slf4j的桥接实现 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
        </dependency>
        <!-- slf4j 核心包  -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <!-- log4j2 核心包 start -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
        </dependency>
        <!-- log4j2 核心包 end -->

但是版本还是为了和项目保持一致,对于log4j2的版本使用了2.3,对slf4j的版本使用了1.7.25.具体参考log4j2的官网 http://logging.apache.org/log4j/2.x/log4j-slf4j-impl/index.html
然后进行了打印当时的logger对象是谁

Class<? extends Logger> clazz = LOGGER.getClass();
            String name = clazz.getName();
            CodeSource cs = clazz.getProtectionDomain().getCodeSource();
            String location = cs.getLocation().getPath();
            StringBuilder allMethodName = new StringBuilder();

            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                String methodName = method.getName();
                allMethodName.append("方法名称:").append(methodName);
                Class<?>[] parameterTypes = method.getParameterTypes();
                for (Class<?> clas : parameterTypes) {
                    String parameterName = clas.getName();
                    allMethodName.append("参数名称:").append(parameterName);
                }
                allMethodName.append("*****************************");
            }
            System.out.println(String.format("当前使用类【%s】文件位置【%s】类加载器为【%s】,所有方法及参数类型为【%s】", name, location, clazz.getClassLoader(), allMethodName.toString()));

进行测试后无果。

第四

在开始的时候报错为 noSuchMethodError(java.lang.String,java.lang.Object)开始引起我们的注意,由于只有一个参数的在Log4j1中出现过,于是有了上面的桥接器。但是我们知道对于2.3版本的Logger接口
在这里插入图片描述

方法名称:info参数名称:java.lang.String参数名称:[Ljava.lang.Object;
其中的第二个参数产生了怀疑。
第二个参数为可变长对象
引申一下可变长对象的一些东西

1 在调用方法的时候,如果能够和固定参数的方法匹配,也能够与可变长参数的方法匹配,则选择固定参数的方法。看下面代码的输出:
2 如果要调用的方法可以和两个可变参数匹配,则出现错误
3 一个方法只能有一个可变长参数,并且这个可变长参数必须是该方法的最后一个参数
    public static void m1(String s, String... ss) {
        for (int i = 0; i < ss.length; i++) {
            System.out.println(ss[i]);
        }
    }

    public static void main(String[] args) {
        m1("");
        m1("aaa");
        m1("aaa", "bbb");
    }

此时开始锁定到二方包的class里面应该是被编译成了第二个参数为Object,也就是二方包里面的Logger是存在一个方法为
在这里插入图片描述

的方法,于是我们分析了他里面的包,将java文件拿出来做出来class进行了查看,利用jdk自带的工具
javap -verbose class文件
从而发现他们的class文件真的就是
在这里插入图片描述

于是锁定问题所在
依赖方打包时使用了高版本的编译成的class,在实际运行时,lib中只有我们系统低版本的2.3的接口,其中确实没有类似的接口。idea在进行调用时,由于是自己本地包,编译的本地class(可理解为不同于二方包的class植入,例如阿里测试工具那样)所以也没有问题。
为了验证,我们让依赖方打一个显示依赖日志jar的快照包

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-jcl</artifactId>
            <version>2.3</version>
        </dependency>

然后进行测试,发现是没有问题的。
同样我们将本项目包升级至

 <slf4j.new.version>1.7.5</slf4j.new.version>
        <log4j.version>2.11.2</log4j.version>
        
 <!-- commons-logging到slf4j的桥梁 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>
        <!-- java.util.logging到slf4j的桥梁 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-1.2-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>
        <!-- log4j2对slf4j的桥接实现 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
        </dependency>
        <!-- log4j1到slf4j的桥接器 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
        </dependency>

同样可以进行运行,其中slf4j的版本升级是由于1.7.25无法适配2.11.2,依赖方使用的slf4j为1.7.29,且slf4j官网
http://www.slf4j.org/manual.html 其中强烈建议使用者使用1.7.5或者以后版本。

待决策项

1.日志是否进行升级至2.11.2和1.7.5版本。具体对比详见上述情况
2.日志是否直接进行显示依赖,并排除其他二方三方间接依赖

思考

1.提供出去的jar,是否有必要打印日志。日志打印的一些注意事项
2.提供出去的jar,是否需要遵循最简单化原则。例如是否需要引入三方包,甚至二方包
3.接收到别人的jar,是否需要进行强制排除,从而确保不会受到相关影响例如

                <exclusions>
                    <exclusion>
                        <groupId>*</groupId>
                        <artifactId>*</artifactId>
                    </exclusion>
                </exclusions>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值