Flink原理:Flink中的⽇志框架配置
⽂章⽬录
1. 背景
很多同学在进⾏Flink开发时,⽆论是使⽤log4j或log4j2,常常出现各种问题,如下图所⽰:
今天我们就要拨开云雾见天⽇,聊聊⽇志相关的知识,搞清楚这些报错的原因。
众所周知,现代框架都是⽤门⾯模式进⾏⽇志输出,例如使⽤Slf4j中的接⼝输出⽇志,具体实现类需要由log4j,log4j2,logback等⽇志框架进⾏实现,如Flink的类中是这样输出⽇志的:
// org.apache.flink.api.java.ClosureCleaner
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Internal
public class ClosureCleaner {
private static final Logger LOG = Logger(ClosureCleaner.class);
...
}
这种设计使得⽤户可以⾃由地进⾏⽇志框架的切换。
2. ⽇志门⾯slf4j
slf4j全名Simple Logging Facade for Java,为java提供的简单⽇志Facade。Facade门⾯说⽩了就是接⼝。它允许⽤户以⾃⼰的喜好,在⼯程中通过slf4j接⼊不同的⽇志系统。slf4j⼊⼝就是众多接⼝的集合,它不负责具体的⽇志实现,只在编译时负责寻合适的⽇志系统进⾏绑定。具体有哪些接⼝,全部都定义在slf4j-api中。查看slf4j-api源码就可以发现,⾥⾯除了public final class LoggerFactory类之外,都是接⼝定义。因此slf4j-api本质就是⼀个接⼝定义。要想使⽤slf4j⽇志门⾯,需要引⼊以下依赖
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
这个包只有⽇志的接⼝,并没有实现,所以如果要使⽤就得再给它提供⼀个实现了些接⼝的⽇志框架包,⽐如:log4j,log4j2,logback 等⽇志框架包,但是这些⽇志实现⼜不能通过接⼝直接调⽤,实现上他们根本就和slf4j-api不⼀致,因此slf4j和⽇志框架之间⼜增加了⼀层桥接器来转换各⽇志实现包的使⽤,⽐如slf4j-log4j12,log4j-slf4j-impl等。
2.1 slf4j + log4j
Log4j + Slf4j的使⽤组合最为常见,依赖关系如下所⽰:
使⽤时要引⼊maven依赖:
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.2 slf4j + log4j2
但是Log4j⽬前已经停⽌更新了。Apache推出了新的Log4j2来代替Log4j,Log4j2是对Log4j的升级,与其前⾝Log4j相⽐有了显着的改进,并提供了许多Logback可⽤的改进,同时解决了Logback体系结构中的⼀些固有问题。因此,Log4j2 + Slf4j应该是未来的⼤势所趋。其依赖关系如下:
使⽤时要引⼊maven依赖:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
log4j2不打印日志<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.9.1</version>
</dependency>
⽆论是使⽤log4j还是log4j2,尤其要记得引⼊各⾃的桥接器,否在就会报⽂章开头不到配置的错误!
3. 避免冲突
由于log4j2的性能优异且是⼤势所趋,本⽂决定使⽤log4j2作为Flink开发的⽇志框架,⼀切按照上⽂配置就绪,却在运⾏时抛出如下错误:
如报错所⽰,在classpath下发现了两个桥接器,很明显,有某个依赖间接依赖了log4j的桥接器slf4j-log4j12,使得classpath下同时存在了slf4j-log4j12和log4j-slf4j-impl这两个桥接器,经过mvn dependency:tree -Dincludes=:slf4j-log4j12命令分析,我们出了这个依赖,在其中
将slf4j-log4j12进⾏了排除:
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-redis_2.11</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
<version>1.1.5</version>
</dependency>
错误顺利消失,正当喜悦之时,有报出如下错误:
乍⼀看有些懵,不过仔细想想,刚刚排除了桥接器的冲突,这个问题是否是因为项⽬间接依赖了log4j,⽽导致底层⽇志框架log4j和log4j2产⽣了冲突呢?⽇志门⾯slf4j虽然正确到了log4j2的桥接器log4j-slf4j-impl,但是接下来会不会⼜根据maven的短路优先原则,歪打正着地到了log4j框架(log4j-slf4j-impl桥接器本应去寻log4j2框架)?本⽂意图使⽤log4j2,因此配置了l,⾃然不会去配置
log4j.properties,更⽆法到log4j.properties中的appender等组件了。
果不其然,在到并排除了间接依赖的log4j之后,错误也随之消失:
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-redis_2.11</artifactId>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
<version>1.1.5</version>
</dependency>
4. 总结
1. 不管使⽤log4j还是log4j2,别忘了在l中配置桥接器!
2. 使⽤⼀个⽇志框架时,请排除另⼀个⽇志框架所使⽤的⼀切直接和间接依赖,避免⼀些令⼈迷惑的报错!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论