日志框架发展历史
2001 年(JDK1.4)之前 - 没有日志库
Java 是没有日志系统的,打印日志主要通过的方法是:System.out
、System.err
和 e.printStackTrace()
来实现。Debug日志被写到 STDOUT 流,错误日志被写到 STDERR 流。
缺点:
- 产生大量的 IO 操作,系统资源占用高。无法合理的控制内容的输出。
- 输出的内容只能打印在控制台,不能保存下来。
- 无法定制化,没有粒度控制。
public class ComparePrintLog {
static Logger logger = LogManager.getLogger();
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
System.out.println("输出" + i);
}
long end = System.currentTimeMillis() - start;
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
logger.log(Level.WARN,"输出" + i);
}
System.out.printf("printf耗时:%d\n", end);
System.out.printf("LOGGER 耗时:%d", System.currentTimeMillis() - start);
}
}
日志库 Log4j - Ceki Gülcü 2001年
2001 年,一位大佬 Ceki Gülcü 开发了一个日志库 Log4j,之后 Log4j 成为 Apache 的顶级项目。Log4j 解决了之前所面临的种种问题(据说 Apache 基金会还曾经建议 Sun 公司引入 Log4j 到 Java 的标准库中,但是被拒绝了)
JUL(Java Util Logging) - JDK 1.4
2002 年 2 月, JDK 1.4 发布。 Sun 公司推出了自己的日志标准库,但是性能没有当时的 Log4j 好。在 JDK1.5 版本之后性能和可用性才有所提升。由于 Log4j 已经非常成熟了,JUL 在使用上就少了很大的优势。
JCL(Jakarta Commons Logging)- 2002.8
Apache 推出了 JCL ,日志抽象层。支持运行时动态加载日志组件的实现,同时默认提供了一个实现 Simple Log。
在 ClassLoader 中进行查找,如果能找到 Log4j 则默认使用 Log4j 实现,如果没有则使用 JUL 实现,再没有就使用 JCL 内部提供的 Simple Log 实现(JDK 1.4 之前)。这样做的好处就是方便切换日志组件。
缺点:
- 效率比较低
- 容易引发混乱
- 使用了自定义 ClassLoader 的程序中,使用 JCL 会引发内存泄漏。
Slf4j - Ceki Gülcü 2006年
2006 年 Ceki(log4j 的作者)因为一些原因离开了 Apache 组织,之后 Ceki 觉得 JCL 不好用,自己写了一套新的日志标准接口 Slf4j(Simple Logging Facade For Java)对标 JCL,在之后的时间过程中也确实证明了 Slf4j 比 JCL 更优秀。
同时 Ceki 还提供了一系列的桥接包来帮助 Slf4j 与其他日志库建立关系,这种方式称为桥接设计模式。
代码使用 Slf4j 接口,就可以实现日志的统一标准化,后续如果想要更换日志实现只需要引入 Slf4j 与相关的桥接包,再引入日志相关的标准库就可以了
Logback 日志标准库 - Ceki Gülcü 2006 年
由于市场上的日志标准库都是间接实现的 Slf4j 接口,也就是说每次都需要配合桥接包。在同年,Ceki 基于 Slf4j 接口实现了 Logback 日志标准库,并且将其作为 Slf4j 接口的默认实现,Logback 在功能完整度和性能上超越了所有已有的日志标准库。(Log4j 成为性能瓶颈)
Log4j2 - Apache 2012年
Apache 直接退出新项目 Log4j2 (不兼容 Log4j)并且 Log4j2 全面借鉴 Slf4j + Logback 的组合。
Log4j2 不仅仅具有 Logback 的所有特性,还做了分离设计,分为 log4j-api 与 log4j-core。前者是日志接口,后者是日志标准库,同时 Apache 也为 Log4j2 提供了各种桥接包。
Log4j2 解决了 Logback 的一些问题,官方测试性能提升有 18 倍。
Log4j2 JNDI 漏洞 - CVE-2021-44228
Log4j2中存在JNDI注入漏洞,当程序将用户输入的数据进行日志记录时,即可触发此漏洞,成功利用此漏洞可以在目标服务器上执行任意代码。
部分时间线
- 11月24日:阿里云安全团队向Apache官方报告了Apache Log4j2远程代码执行漏洞CVE-2021-44228;
- 12月7日:Apache Log4j2官方发布log4j2-2.15.0-rc1并第一次修复CVE-2021-44228漏洞;
- 12月9日:启明星辰安全应急响应中心监测到Apache Log4j2 远程代码执行漏洞CVE-2021-44228(CVSS评分10.0),各产品线开展应急响应处置。
- 12月14日:启明星辰安全应急响应中心监测到Apache Log4j 1.2远程代码执行漏洞CVE-2021-4104(CVSS评分6.6)。
- 12月15日:启明星辰安全应急响应中心监测到Apache Log4j2拒绝服务漏洞CVE-2021-45046(CVSS评分3.7)。
JNDI
JNDI 是Java Naming and Directory Interface(JAVA命名和目录接口)的英文简写,它是为JAVA应用程序提供命名和目录访问服务的API(Application Programing Interface,应用程序编程接口)。
RMI
JAVA RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制。使用这种机制,某一台计算机(即JVM虚拟机)上的对象可以调用另外一台计算机上的对象来获取远程数据。
我们知道远程过程调用(Remote Procedure Call, RPC)可以用于一个进程调用另一个进程(很可能在另一个远程主机上)中的过程,从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步,即提供分布式对象间的通讯。
Code
demo
...
漏洞缓解措施:
- jvm参数 -Dlog4j2.formatMsgNoLookups=true
- log4j2.formatMsgNoLookups=True
升级 Log4j2 版本
Ref
【漏洞通告】Apache Log4j2拒绝服务漏洞(CVE-2021-45046)
Java 的 RMI 远程方法调用实现和应用
本文由 suxi 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: 2024/03/28 16:31