Spark升级中对log4j中的一些思考

背景

最近在做Spark版本的升级(由spark3.1升级到spark3.5),其实单纯从spark升级涉及到的log4j来说,并没有什么能够记录的,

但是由于公司内部做了Spark的serveless,把spring和spark混在了一起,所以导致了不可预见的问题

分析

我们Spring用的是5.2.6.RELEASE版本,由于spark用的是logback作为日志的具体实现,而Spark在3.1和spark 3.5是采用了不同的日志具体实现:

在 spark3.1中采用的是log4j1 (log4j + slf4j-log4j2),spark 3.5中采用的是log42(log4j-core + log4j-api + log4j-slf4j2-impl),

再加上由于其他log包引入,比如说:

复制代码
1. "org.apache.logging.log4j" % "log4j-1.2-api"

2. "org.apache.logging.log4j" % "log4j-to-slf4j"

3. "org.slf4j" % "slf4j-log4j12"

4. "org.slf4j" % "slf4j-api"

5. "org.slf4j" % "jcl-over-slf4j"

6. "org.slf4j" % "jul-to-slf4j"

7. "commons-logging" % "commons-logging"

8. "ch.qos.logback" % "logback-classic"

9. "ch.qos.logback" % "logback-core"

这真是太酸爽了,一时间几乎日志系统都齐全了,出现的错误也是千奇百怪:

复制代码
1. Getting Exception org.apache.logging.slf4j.SLF4JLoggerContext cannot be cast to org.apache.logging.log4j.core.LoggerContext

2. java.lang.StackOverflowError at org.apache.logging.log4j.util.StackLocator.getCallerClass

3. Slf4j: Found slf4j-api dependency but no providers were found

4. java.lang.AssertionError: unsafe symbol Level (child of package log4j) in runtime reflection universe
        at scala.reflect.internal.Symbols$Symbol.<init>(Symbols.scala:224)
        at scala.reflect.internal.Symbols$TypeSymbol.<init>(Symbols.scala:3070)
        at scala.reflect.internal.Symbols$ClassSymbol.<init>(Symbols.scala:3261)
        at scala.reflect.internal.Symbols$StubClassSymbol.<init>(Symbols.scala:3539)

这些问题一度让我认为是Spark scala版本升级导致的(当然也是我在其中排除了各种乱七八糟的log包导致的问题),以上问题在google中也能搜寻到,

但是最后反思了一下:还是对log sl4j logback理解的不够:

  1. slf4j 原理及使用原则,可以参考slf4j 原理及使用原则
    这里面讲述了slf4j的一些原理以及关系

  2. 常用日志框架(Log4j,Slf4j,Logback)之间到底有啥区别 可以参考 常用日志框架(Log4j,Slf4j,Logback)之间到底有啥区别
    这里面讲了logback与其中的关系

  3. Difference between slf4j-log4j12 and log4j-slf4j-impl,可以参考Difference between slf4j-log4j12 and log4j-slf4j-impl
    这里讲了slf4j1.x slf4j2.x与Log4j 2.x Log4j 1.2的jar包的关系

  4. 秒懂log4j1与log4j2的区别,参考秒懂log4j1与log4j2的区别
    这里最为关键:

    复制代码
     slf4j的成功在于他的高屋建瓴,俯视一切。slf4j是日志门面(像:java的接口,没有提供任何实现),通过提供各种桥接器,适配各种日志框架(log4j1,logback等)。log4j1并没有这样的高度,于是log4j2就借鉴了slf4j的设计,log4f2有两部分组成:log4j-api、log4j-core。log4j-api和slf4j是相同的,都是日志门面,log4j-core是对log4j-api的实现,和log4j1、logback是相同的, 并且通过桥接器log4j-api还可以适配其他的日志系统(logback等)。这样log4j2就成了一个文堪比slf4,武可斗logback的双优生

    这里说明可以用log4j-api 适配logback ,再加上极客时间的讨论

    复制代码
     Geek_58afe9 问:
      
     "其实,我们只是换成了 Log4j2 API,真正的日志记录还是走的 Logback 框架。没错,这就是 SLF4J 适配的一个好处。". Log4j2 和 LogBack 不是同质化产品吗, Log4j2 api 怎么会走到Logback?
     作者回复: log4j2 API,并不是log4j 你在ch.qos.logback.classic.Logger.buildLoggingEventAndAppend设一个断点看一下整个过程就知道了,或者直接访问 https://github.com/JosephZhu1983/java-common-mistakes/blob/master/src/main/java/org/geekbang/time/commonmistakes/logging/placeholder/log4j2api_to_slf4j_to_logack.jpg 查看```

从以上我们知道 log4j2 不仅仅可以和slf4j适配,也可以和logback适配,这提供了另一条思路:不要只考虑slf4j和log4的爱恨情仇,也得考虑log4j和logback的亲密关系

解决

最终我们只留下了log4j2 (log4j-core + log4j-api) + logback (logback-classic + logback-core) ,其他的都排除掉,web端打包加编译没有任何问题,一切还是那么的美好(毕竟花了一天时间)

其他

相关推荐
TDengine (老段)4 小时前
TDengine 线程模型 — 网络、调度、执行
大数据·数据库·物联网·制造·时序数据库·tdengine·涛思数据
智购科技自动贩卖机6 小时前
自动售货机嵌入式状态机设计实战:从45个事件源到层次型状态机的工程重构
大数据·人工智能·stm32·物联网·重构·硬件架构
乐迪信息6 小时前
智慧港口船舶AI算法实现在线状态监测
大数据·人工智能·深度学习·算法·计算机视觉
Raas1006 小时前
MAI Gateway(魔芋企业级AI网关)对比分析:AI网关和API网关区别?企业级能力差距一览
大数据·人工智能·数据挖掘·mai gateway·企业级产品
Neighbor_OldY7 小时前
云上安全配置审计与误配置修复实战:从安全组、OSS、RAM到数据库的全栈排查复盘
大数据·运维·安全·云计算
工业甲酰苯胺8 小时前
AI低代码破局:制造业数智转型落地实战
大数据·人工智能·低代码·制造
大大大大晴天9 小时前
每天认识一个组件:流式存储Apache Fluss
大数据
熊野君9 小时前
第 4 章 技术产品经理核心能力模型
大数据·人工智能·产品经理
财复视界9 小时前
光智科技从“光学元件”到“稀散金属材料平台”的进化逻辑
大数据·人工智能·科技
牛企老板俱乐部10 小时前
2026年珠海精锐增材智造金属小批量交付可提供SGS与RoHS材质证明
大数据