https://github.com/tanpenggood/learning-java-log
Fully master Java Log System.
https://github.com/tanpenggood/learning-java-log
jcl jul log4j log4j2 logback slf4j
Last synced: about 1 month ago
JSON representation
Fully master Java Log System.
- Host: GitHub
- URL: https://github.com/tanpenggood/learning-java-log
- Owner: tanpenggood
- Created: 2021-12-16T09:14:50.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2021-12-19T15:09:28.000Z (over 4 years ago)
- Last Synced: 2025-04-01T22:51:03.344Z (over 1 year ago)
- Topics: jcl, jul, log4j, log4j2, logback, slf4j
- Language: Java
- Homepage:
- Size: 14.6 KB
- Stars: 2
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README-cn.md
Awesome Lists containing this project
README
# 学习Java日志体系
## 项目结构
```
├─java-log-impl # Java各个日志实现(jul、log4j、log4j2、logback)
├─java-log-api-jcl # commons logging
├─java-log-api-slf4j # slf4j
└─log4j2-vulnerabilities # log4j2安全漏洞复现
```
## Log4j2 CVE-2021-44228 复现步骤
1. cd `log4j2-vulnerabilities`
2. 启动`com.itplh.log4j2.RmiServer`
3. 运行`com.itplh.log4j2.TestLog4j2UseJndi`
## Java日志体系实现方案
- 接口
- JCL(Apache Commons Logging)
- Slf4j
- 实现
- JUL(java.util.logging)
- log4j
- log4j2
- logback
## JCL(Apache Commons Logging,又名Jakarta Commons Logging)
1. cd `java-log-api-jcl`
2. 运行 `com.itplh.jcl.TestCommonsLogging`,观察日志输出
3. 调整 `commons-logging.properties` 中 Log 的具体实现,再重新运行程序,观察日志输出
### 原理
查找`org.apache.commons.logging.Log`的实现类,进行日志框架的绑定,调用栈如下:
```
Log log = LogFactory.getLog("jcl");
|- getLog:669, LogFactory (org.apache.commons.logging)
|- getInstance:292, LogFactoryImpl (org.apache.commons.logging.impl)
|- newInstance:541, LogFactoryImpl (org.apache.commons.logging.impl)
|- discoverLogImplementation:790, LogFactoryImpl (org.apache.commons.logging.impl)
|- createLogFromClass:960, LogFactoryImpl (org.apache.commons.logging.impl)
```
commons-logging获取日志对象的全过程,具体文字总结如下:
```
获取当前线程的classLoader,根据classLoader从缓存中获取LogFactroy,使用的缓存是WeakHashTable对象;如果缓存中存在,则返回,没有则进入下面流程;
读取classpath下的commons-logging.properties文件,判断其中是否设置了use_tccl属性,如果不为空则判断,该属性的值是否为false,若为false,则将baseClassLoader替换为当前类的classLoader;
接着,继续获取LogFactory对象,此步骤分为4种方式:
(1)在系统属性中查找“org.apache.commons.logging.LogFactory”属性的值,根据值生成LogFactory对象;
(2)通过资源“META-INF/services/org.apache.commons.logging.LogFactory”文件,获取的值生成LogFactory对象;
(3)通过配置文件commons-logging.properties,获取以“org.apache.commons.logging.LogFactory”为key的值,根据值生成logFactory;
(4)如果以上均不成功,则创建系统默认的日志工厂:org.apache.commons.logging.impl.LogFactoryImpl
成功获取日志工厂后,根据类名获取日志对象;
主要逻辑在discoverLogImplementation方法中:
(1)检查commons-logging.properties文件中是否存在“org.apache.commons.logging.Log”属性,若存在则创建具体的日志对象;若不存在,进行下面逻辑;
(2)遍历classesToDiscover数组,该数组存有日志具体实现类的全限定类名:org.apache.commons.logging.impl.Log4JLogger、org.apache.commons.logging.impl.Jdk14Logger、org.apache.commons.logging.impl.Jdk13LumberjackLogger、org.apache.commons.logging.impl.SimpleLog;
(3)根据数组中存着的全限定类名,按照顺序依次加载Class文件,进行实例化操作,最后返回Log实例,默认为Jdk14Logger;
```
## Slf4j适配解决方案
1. cd `java-log-api-slf4j`
2. 运行 `com.itplh.slf4j.TestSlf4j`,观察日志输出
3. 调整 `pom.xml` 中 slf4j适配到各个日志的依赖关系,再重新运行程序,观察日志输出
### 原理
查找适配器包中的`org.slf4j.impl.StaticLoggerBinder`类,进行日志框架的绑定,调用栈如下:
```
Logger logger = LoggerFactory.getLogger("slf4j");
|- getLogger:362, LoggerFactory (org.slf4j)
|- getILoggerFactory:417, LoggerFactory (org.slf4j)
|- performInitialization:124, LoggerFactory (org.slf4j)
|- bind:146, LoggerFactory (org.slf4j)
|- findPossibleStaticLoggerBinderPathSet:301, LoggerFactory (org.slf4j)
```
> 当classpath中有多个`org.slf4j.impl.StaticLoggerBinder`时,最终由JVM类加载机制决定加载其中的一个。
## Slf4j桥接方案
## Spring日志方案实战
## 参考
[Apache Log4j](https://logging.apache.org/log4j/1.2/index.html)
[Apache Log4j 2](https://logging.apache.org/log4j/2.x/index.html)
[logback](http://logback.qos.ch/)
[Apache Commons Logging](https://commons.apache.org/proper/commons-logging/)
[Simple Logging Facade for Java (SLF4J)](http://www.slf4j.org/)