我使用的是cdh数据平台,在yarn上跑flink集群,操作步骤如下:

一、本地尝试启动
flink用kafka做连接器,本地启动后,发现控制台报错。

SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

第一次尝试:
我尝试添加slf4j-log4j12桥接器依赖

<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>1.7.5</version>
</dependency>

log4j.properties

log4j.rootLogger=INFO, console

# Uncomment this if you want to _only_ change Flink's logging
log4j.logger.org.apache.flink=WARN

# The following lines keep the log level of common libraries/connectors on
# log level INFO. The root logger does not override this. You have to manually
# change the log levels here.
log4j.logger.akka=WARN
log4j.logger.org.apache.kafka=INFO
log4j.logger.org.apache.hadoop=WARN
log4j.logger.org.apache.zookeeper=WARN

# Log all infos to the console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p %-60c %x - %m%n

# Suppress the irrelevant (wrong) warnings from the Netty channel handler
log4j.logger.org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline=ERROR, console

二、环境上启动
将jar包放到yarn上执行,发现flink web ui上无法输出日志,打开log list中的taskmanager.err查看 slf4j 的桥接日志,发现报错了,下面三个桥接器冲突了。log4j-slf4j-impl-2.17.5 、log4j-slf4j-impl-2.17.1 、slf4j-log4j12-1.7.25。

报错信息

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0034/filecache/28/hycan-flink-hycanvalue-job-1.0-SNAPSHOT1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0034/filecache/11/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/cloudera/parcels/CDH-6.2.1-1.cdh6.2.1.p0.1425774/jars/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
log4j:WARN No appenders could be found for logger (org.apache.flink.yarn.YarnTaskExecutorRunner).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

观察一下flink官方的二进制运行包中就自带了slf4j和log4j2的jar包:flink-dist带有有关slf4j的jar包,其余是log4j2的。

log4j-1.2-api-2.17.1.jar
log4j-api-2.17.1.jar
log4j-core-2.17.1.jar
log4j-slf4j-impl-2.17.1.jar
flink-dist-1.15.0.jar

也就是说我引入的jar包,flink官方的桥接器、cdh的桥接器是冲突的。因为flink官方使用的是log4j2的日志组件,所以需要移除我自己引入的包中的日志组件和cdh中的日志组件,应该就ok了。因此首先我将jar包中的org.slf4j.*排除。

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <executions>
                    <!-- Run shade goal on package phase -->
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <excludes>
                                    <exclude>org.slf4j:*</exclude>
                                    <exclude>org.apache.logging.log4j:*</exclude>
                                    <exclude>ch.qos.logback:*</exclude>
                                </excludes>
                            </artifactSet>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

再次打包,生成*****-shaded.jar,执行后发现日志正常记录。再次打开taskmanager.err时,发现有如下报错

报错信息

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0035/filecache/12/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/cloudera/parcels/CDH-6.2.1-1.cdh6.2.1.p0.1425774/jars/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
log4j:WARN No appenders could be found for logger (org.apache.hadoop.metrics2.lib.MutableMetricsFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

对比上面的第一次报错日志,此次减少了我的jar包引入导致的桥接器冲突,并且最终slf4j绑定的变成为org.apache.logging.slf4j.Log4jLoggerFactory

对比分析:

  • 正常的slf4j binding的类:org.apache.logging.slf4j.Log4jLoggerFactory
    不正常的slf4j binding的类:org.slf4j.impl.Log4jLoggerFactory

这明显是slf4j binding包冲突造成的,经过调查发现,前者是log4j2.x用的工厂实现类,而后者是兼容log4j1.x用的工厂类,他们分别位于:
log4j-slf4j-impl-2.17.1.jar
slf4j-log4j12-1.7.5.jar(第一次我自己引入的jar包中的)

根据一些参考文章我有了想法,如果我自己的jar包引入log4j-slf4j-impl桥接器依赖会怎样呢?是否还是会依赖冲突,但是日志正常打印呢?

第二次尝试:
之后我又进行了尝试,本地使用slf4j+log4j2,引入依赖

            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-api</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-core</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.logging.log4j</groupId>
                <artifactId>log4j-slf4j-impl</artifactId>
                <version>${log4j.version}</version>
            </dependency>

log4j2.xml

<?xml version="1.0" encoding="UTF-8" ?>

<Configuration status="WARN" monitorInterval="600">
    <Appenders>
        <Console name="console_out_appender" target="SYSTEM_OUT">
            <!-- 输出日志的格式 -->
            <PatternLayout pattern="[%t] %d{MM/dd-HH:mm:ss,SSS} [%-5p] : %m (%F:%L)%n"/>
        </Console>
    </Appenders>
    <Loggers>
        <!-- 配置日志的根节点,打印trace等级及其以上等级的日志 -->
        <root level="info">
            <appender-ref ref="console_out_appender"/>
        </root>
    </Loggers>
</Configuration>

再次打包,生成*****-.jar,执行后发现日志正常记录。但是日志的报错变化了

报错信息

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0034/filecache/28/hycan-flink-hycanvalue-job-1.0-SNAPSHOT.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0035/filecache/12/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/cloudera/parcels/CDH-6.2.1-1.cdh6.2.1.p0.1425774/jars/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]
log4j:WARN No appenders could be found for logger (org.apache.hadoop.metrics2.lib.MutableMetricsFactory).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

果然,产生了三个jar包的冲突,因为我自己的jar包引入的桥接器是log4j-slf4j-impl的版本与flink官方的不同。最终slf4j绑定的类仍然是org.apache.logging.slf4j.Log4jLoggerFactory,所以本次依然能正常打印日志。

虽然大概了解了原因,但是仍然有一些疑问,为什么出现了jar包的冲突仍然能够正常打印日志呢?

其实,slf4j在运行时会打印所有在classpath里面发现的所有日志实现类,然后会选择第一个被类加载器加载的实现类作为底层的真正的日志组件,之后其他的实现类会被忽略,因为Java类加载器在加载多个同包名同类名的class的时候,只有第一个会成功,后面的不会被加载,这也是双亲委派模型的经典之处。

这就引出了,jvm加载jar的顺序问题,文章上是这么说的:

Java类加载器加载同一个目录下的jar包的顺序是随机的,会受操作系统的文件系统影响。
在包冲突的情况下(包括同jar不同版本和不同jar但存在同包名同类名的class),如果类加载器按照正常的顺序加载,是没有问题的,但如果恰好冲突的jar包,加载的顺序发生了颠倒,那么就极有可能引发莫名其妙的问题

jdk官网:同一个目录下,jvm加载jar包顺序是无法保证的,每个系统的都不一样,甚至同一个系统不同的时刻加载都不一样。良好设计的系统不应该依赖任何特定的加载顺序。

第三次尝试:
最终决定本地引入slf4j+log4j2依赖(第二次尝试中的方式),并且将有关slf4j和log4j的依赖排除:

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <executions>
                    <!-- Run shade goal on package phase -->
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <artifactSet>
                                <excludes>
                                    <exclude>org.slf4j:*</exclude>
                                    <exclude>org.apache.logging.log4j:*</exclude>
                                    <exclude>ch.qos.logback:*</exclude>
                                </excludes>
                            </artifactSet>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

最终web ui上的展示taskmanager.err如下:cdh环境中的依赖冲突还存在,但是暂且不管(这是不对的,但是时间问题,就暂时没有处理了),日志已经可以正常跑起来了。
第四次报错

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/sdv1/yarn/nm/usercache/root/appcache/application_1652426634423_0042/filecache/11/log4j-slf4j-impl-2.17.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/cloudera/parcels/CDH-6.2.1-1.cdh6.2.1.p0.1425774/jars/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]

参考文章:
日志之间的依赖关系1:https://cloud.tencent.com/developer/article/1512698
有关日志之间的依赖关系2: https://cloud.tencent.com/developer/article/1513305?from=article.detail.1512698
flink的日志配置:https://blog.csdn.net/ifenggege/article/details/114434195
log4j、slf4j、log4j2依赖选择:https://blog.csdn.net/Andrew_Yuan/article/details/83010938
排除依赖方式:https://www.jianshu.com/p/c2287c8c87b6
不错的参考文章:https://blog.csdn.net/ifenggege/article/details/114434195

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐