Skip to content
This repository has been archived by the owner on Feb 18, 2024. It is now read-only.

复写Log4jLogger,适配多模块下static final修饰的Logger情况使用 #478

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 76 additions & 2 deletions docs/content/zh-cn/docs/contribution-guidelines/runtime/logj42.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,91 @@ weight: 1

根据上面判断通过 bundle 的方式配置在多模块里不可行,因为 ResourceBundleLookup 可能只存在于基座中,导致始终只能拿到基座的 application.properties,导致模块的日志配置路径与基座相同,模块日志都打到基座中。所以需要改造成使用 ContextMapLookup。

## static final修饰的Logger导致三方组件下沉基座后日志打印不能正常隔离
如:
```java
private static final Logger LOG = LoggerFactory.getLogger(CacheManager.class);
```
1. static final修饰的变量只会在类加载的时候初始化话一次
2. 组件依赖下沉基座后,类加载器使用的为基座的类加载器,初始化log实例时使用的是基座的log配置,所以会打印到基座文件中,不能正常隔离

具体获取log源码如下:
```java
//org.apache.logging.log4j.spi.AbstractLoggerAdapter
@Override
public L getLogger(final String name) {
//关键是LoggerContext获取是否正确,往下追
final LoggerContext context = getContext();
final ConcurrentMap<String, L> loggers = getLoggersInContext(context);
final L logger = loggers.get(name);
if (logger != null) {
return logger;
}
loggers.putIfAbsent(name, newLogger(name, context));
return loggers.get(name);
}

//获取LoggerContext
protected LoggerContext getContext() {
Class<?> anchor = LogManager.getFactory().isClassLoaderDependent() ? StackLocatorUtil.getCallerClass(Log4jLoggerFactory.class, CALLER_PREDICATE) : null;
LOGGER.trace("Log4jLoggerFactory.getContext() found anchor {}", anchor);
return anchor == null ? LogManager.getContext(false) : this.getContext(anchor);
}

//获取LoggerContext,关键在这里
protected LoggerContext getContext(final Class<?> callerClass) {
ClassLoader cl = null;
if (callerClass != null) {
//会优先使用当前类相关的类加载器,这里肯定是基座的类加载,所以返回的是基座的LoggerContext
cl = callerClass.getClassLoader();
}
if (cl == null) {
cl = LoaderUtil.getThreadContextClassLoader();
}
return LogManager.getContext(cl, false);
}

```

## 预期多模块合并下的日志
基座与模块都能使用独立的日志配置、配置值,完全独立。但由于上述分析中,存在两处可能导致模块无法正常初始化的逻辑,故这里需要多 log4j2 进行适配。

static修饰的log在三方组件下沉基座后也会导致相关日志不能正常隔离打印,所以这里也需要做 log4j2 进行适配。
### 多模块适配点
1. getLoggerContext() 能拿到模块自身的 LoggerContext
![](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/149473/1696938182575-51ce1066-21f0-47bb-8bdb-c3c7d0814ca3.png)
2. 需要调整成使用 ContextMapLookup,从而模块日志能获取到模块应用名,日志能打印到模块目录里

a. 模块启动时将 application.properties 的值设置到 ThreadContext 中
b. 日志配置时,只能使用 ctx:xxx:xxx 的配置方式

3. LoggerFactory.getLogger()获取的是org.apache.logging.slf4j.Log4jLogger实例,他是一个包装类,所以有一定操作空间,
针对Log4jLogger进行复写改造,根据当前线程上下文类加载器动态获取底层ExtendedLogger对象
```java
public class Log4JLogger implements LocationAwareLogger, Serializable {
private transient final Map<ClassLoader, ExtendedLogger> loggerMap = new ConcurrentHashMap<>();
private static final Map<ClassLoader, LoggerContext> LOGGER_CONTEXT_MAP = new ConcurrentHashMap<>();

pubblic void info(final String format, final Object o) {
//每次调用都获取对应的ExtendedLogger
getLogger().logIfEnabled(FQCN, Level.INFO, null, format, o);
}

//根据当前线程类加载器动态获取ExtendedLogger
private ExtendedLogger getLogger() {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
ExtendedLogger extendedLogger = loggerMap.get(classLoader);
if (extendedLogger == null) {
LoggerContext loggerContext = LOGGER_CONTEXT_MAP.get(classLoader);
if (loggerContext == null) {
loggerContext = LogManager.getContext(classLoader, false);
LOGGER_CONTEXT_MAP.put(classLoader, loggerContext);
}
extendedLogger = loggerContext.getLogger(this.name);
loggerMap.put(classLoader, extendedLogger);
}
return extendedLogger;
}
}
```
## 模块改造方式
[详细查看源码](https://github.com/sofastack/sofa-serverless/tree/master/sofa-serverless-runtime/sofa-serverless-adapter-ext/sofa-serverless-adapter-log4j2/src/main/java/com/alipay/sofa/serverless/log4j2)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 24 additions & 3 deletions samples/springboot-samples/logging/log4j2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ base 为普通 springboot 改造成的基座,改造内容为在 pom 里增加
</dependency>
<!-- end log4j2 依赖引入 -->

<!--用ehcache模拟三方组件下沉基座后日志能能够正常隔离打印-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!-- end ehcache 依赖引入-->

```

### biz
Expand All @@ -58,6 +65,14 @@ biz 包含两个模块,分别为 biz1 和 biz2, 都是普通 springboot,修
<scope>provided</scope>
</dependency>

<!--用ehcache模拟三方组件下沉基座后日志能能够正常隔离打印-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<scope>provided</scope>
</dependency>
<!-- end ehcache 依赖引入-->

<!-- 修改打包插件为 sofa-ark biz 打包插件,打包成 ark biz jar -->
<plugin>
<groupId>com.alipay.sofa</groupId>
Expand Down Expand Up @@ -142,11 +157,17 @@ curl http://localhost:8080/biz2
2. 检查内容2, `./samples/logging/log4j2/logs/` 目录里的日志分布在符合如下情况
![img_2.png](../imgs/logs-structure.png)

- biz1 的应用日志在 `./samples/logging/log4j2/logs/biz1/` 目录下
- biz2 的应用日志在 `./samples/logging/log4j2/logs/biz2/` 目录下
- base 的应用日志在 `./samples/logging/log4j2/logs/base/` 目录下
3.三方组件(这里如ehcache)依赖下沉基座后日志正常隔离打印
![img_1.png](../imgs/biz1-3-log.png)
![img.png](../imgs/biz2-3-log.png)

- biz1 的应用及三方组件(ehcache)日志在 `./samples/logging/log4j2/logs/biz1/` 目录下
- biz2 的应用及三方组件(ehcache)日志在 `./samples/logging/log4j2/logs/biz2/` 目录下
- base 的应用及三方组件(ehcache)日志在 `./samples/logging/log4j2/logs/base/` 目录下
- biz1, biz2, base 的框架日志(如 spring sofaArk arklet等),统一合并在同一个目录文件里


## 注意事项
这里主要使用简单应用做验证,如果复杂应用,需要注意模块做好瘦身,基座有的依赖,模块尽可能设置成 provided,尽可能使用基座的依赖。


6 changes: 5 additions & 1 deletion samples/springboot-samples/logging/log4j2/base/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
<groupId>com.alipay.sofa</groupId>
<artifactId>web-ark-plugin</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
Expand Down Expand Up @@ -58,6 +57,11 @@
<artifactId>disruptor</artifactId>
<version>${disruptor.version}</version>
</dependency>
<!--用ehcache模拟三方组件下沉基座后日志能能够正常隔离打印-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.alipay.sofa.logging.base.rest;

import com.alipay.sofa.logging.base.facade.SampleService;
import net.sf.ehcache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -26,6 +27,9 @@ public String hello() {

sampleService.service();

CacheManager.create();
CacheManager.create();

return String.format("hello to %s deploy", appName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ logging.path=./logging/log4j2/logs/
logging.level.com.alipay.sofa=DEBUG
logging.level.root=INFO
logging.level.com.alipay.sofa.arklet=INFO
logging.level.net.sf.ehcache=DEBUG
logging.config=classpath:log4j2-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@
<AppenderRef ref="STDOUT-APPENDER"/>
</AsyncLogger>

<AsyncLogger name="net.sf.ehcache" additivity="false"
level="${ctx:logging.level.net.sf.ehcache}">
<AppenderRef ref="APP-DEFAULT-APPENDER"/>
<AppenderRef ref="WARN-APPENDER"/>
<AppenderRef ref="ERROR-APPENDER"/>
<AppenderRef ref="STDOUT-APPENDER"/>
</AsyncLogger>

<AsyncRoot level="${ctx:logging.level.com.alipay.sofa}">
<AppenderRef ref="APP-DEFAULT-APPENDER"/>
<AppenderRef ref="WARN-APPENDER"/>
Expand Down
6 changes: 6 additions & 0 deletions samples/springboot-samples/logging/log4j2/biz1/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
<version>${sofa.serverless.runtime.version}</version>
<scope>provided</scope>
</dependency>
<!--用ehcache模拟三方组件下沉基座后日志能能够正常隔离打印-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.alipay.sofa.logging.biz1.rest;

import net.sf.ehcache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -12,13 +13,18 @@
public class SampleController {
private static final Logger LOGGER = LoggerFactory.getLogger(SampleController.class);


@Autowired
private ApplicationContext applicationContext;

@RequestMapping(value = "/", method = RequestMethod.GET)
public String hello() {
String appName = applicationContext.getApplicationName();
LOGGER.info("{} web test: into sample controller", appName);

CacheManager.create();
CacheManager.create();

return String.format("hello to %s deploy", appName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ logging.path=./logging/log4j2/logs/
logging.level.com.alipay.sofa=DEBUG
logging.level.root=INFO
logging.level.com.alipay.sofa.arklet=INFO
logging.level.net.sf.ehcache=DEBUG
logging.config=classpath:log4j2-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@
<AppenderRef ref="STDOUT-APPENDER"/>
</AsyncLogger>

<AsyncLogger name="net.sf.ehcache" additivity="false"
level="${ctx:logging.level.net.sf.ehcache}">
<AppenderRef ref="APP-DEFAULT-APPENDER"/>
<AppenderRef ref="WARN-APPENDER"/>
<AppenderRef ref="ERROR-APPENDER"/>
<AppenderRef ref="STDOUT-APPENDER"/>
</AsyncLogger>

<AsyncRoot level="${ctx:logging.level.com.alipay.sofa}">
<AppenderRef ref="APP-DEFAULT-APPENDER"/>
<AppenderRef ref="WARN-APPENDER"/>
Expand Down
6 changes: 6 additions & 0 deletions samples/springboot-samples/logging/log4j2/biz2/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@
<version>${sofa.serverless.runtime.version}</version>
<scope>provided</scope>
</dependency>
<!--用ehcache模拟三方组件下沉基座后日志能能够正常隔离打印-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.alipay.sofa.logging.biz2.rest;

import net.sf.ehcache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -19,6 +20,9 @@ public class SampleController {
public String hello() {
String appName = applicationContext.getApplicationName();
LOGGER.info("{} web test: into sample controller", appName);

CacheManager.create();
CacheManager.create();
return String.format("hello to %s deploy", appName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ logging.path=./logging/log4j2/logs/
logging.level.com.alipay.sofa=DEBUG
logging.level.root=INFO
logging.level.com.alipay.sofa.arklet=INFO
logging.level.net.sf.ehcache=DEBUG
logging.config=classpath:log4j2-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@
<AppenderRef ref="STDOUT-APPENDER"/>
</AsyncLogger>

<AsyncLogger name="net.sf.ehcache" additivity="false"
level="${ctx:logging.level.net.sf.ehcache}">
<AppenderRef ref="APP-DEFAULT-APPENDER"/>
<AppenderRef ref="WARN-APPENDER"/>
<AppenderRef ref="ERROR-APPENDER"/>
<AppenderRef ref="STDOUT-APPENDER"/>
</AsyncLogger>

<AsyncRoot level="${ctx:logging.level.com.alipay.sofa}">
<AppenderRef ref="APP-DEFAULT-APPENDER"/>
<AppenderRef ref="WARN-APPENDER"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
<class>
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem
</class>
<class>
org.apache.logging.slf4j.Log4jLogger
</class>
</classes>
</exported>
<excludes>
Expand Down
Loading