示例工程参照 aliyun-mq/rocketmq-logging。
在开发 RocketMQ 5.0 SDK 的过程中,遭遇到一个问题,由于历史上的原因,RocketMQ 的日志默认情况下是会输出到用户的账户主目录下的,但是在之前的版本中,RocketMQ 自己维护了一个日志模块,也是我们所熟知的 logging 模块(下图所示)。之所以没有使用开源的 slf4j 和 logback/log4j2 的方案主要还是想要避免配置文件和环境参数的冲突。
RocketMQ 作为经常被业务代码所依赖的中间件产品,如果自己也直接使用开源的 logback/log4j2 解决方案,那么和用户自己的配置发生冲突几乎是板上钉钉的事情。但是 RocketMQ 本身这个日志模块带来一定的维护成本不说,功能也是无法做到和开源的 logback/log4j2 的解决方案对齐的。
在 Java 中,对依赖进行 shade 和 relocation 是解决依赖冲突的标准姿势,不过一般只会 relocate 一下 java 的包名,那么除此之外,是否还可以替换被 shade 的 logback 的配置文件和各个参数呢?答案是可以的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>${maven-shade-plugin.version}</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <minimizeJar>false</minimizeJar> <createSourcesJar>true</createSourcesJar> <shadeSourcesContent>true</shadeSourcesContent> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> </transformers> <relocations> <relocation> <pattern>org.slf4j</pattern> <shadedPattern>${shade-prefix}.shaded.org.slf4j</shadedPattern> </relocation> <relocation> <pattern>ch.qos.logback</pattern> <shadedPattern>${shade-prefix}.ch.qos.logback</shadedPattern> </relocation> <relocation> <!-- logback internal constant, see ch.qos.logback.classic.util.ContextInitializer --> <rawString>true</rawString> <pattern>^logback.configurationFile$</pattern> <shadedPattern>${config-file-prefix}.logback.configurationFile</shadedPattern> </relocation> <relocation> <!-- shades logback internal constant, see ch.qos.logback.classic.util.ContextInitializer --> <rawString>true</rawString> <pattern>^logback.groovy$</pattern> <shadedPattern>${config-file-prefix}.logback.groovy</shadedPattern> </relocation> <relocation> <!-- shades logback internal constant and rename user's logback file, see ch.qos.logback.classic.util.ContextInitializer --> <rawString>true</rawString> <pattern>^logback(-test)?.xml$</pattern> <shadedPattern>${config-file-prefix}.logback$1.xml</shadedPattern> </relocation> <relocation> <!-- logback internal constant, see ch.qos.logback.classic.ClassicConstants --> <rawString>true</rawString> <pattern>^logback.ContextSelector$</pattern> <shadedPattern>${config-file-prefix}.logback.ContextSelector</shadedPattern> </relocation> <relocation> <!-- logback internal constant, see ch.qos.logback.classic.ClassicConstants --> <rawString>true</rawString> <pattern>^java:comp/env/logback/configuration-resource$</pattern> <shadedPattern>java:comp/env/${config-file-prefix}.logback/configuration-resource </shadedPattern> </relocation> <relocation> <!-- logback internal constant, see ch.qos.logback.classic.ClassicConstants --> <rawString>true</rawString> <pattern>^java:comp/env/logback/context-name$</pattern> <shadedPattern>java:comp/env/${config-file-prefix}.logback/context-name </shadedPattern> </relocation> </relocations> </configuration> </execution> </executions> </plugin>
|
其中 $shade-prefix
表示 shade 之后 package 名的前缀,$config-file-prefix
表示 shade 之后配置文件基于 logback.xml
的前缀,比如对于 rocketmq 而言,此时配置文件的前缀也就变成了 rocketmq.logback.groovy/rocketmq.logback-test.xml/rocketmq.logback.xml
了。可以达到与用户的配置文件 logback.groovy/logback-test.xml/logback.xml
不冲突的目的。相应的,原生的 logback 中一些环境参数也会被替换成 rocketmq 的前缀。
不过我们在 shade logback 的同时也要记得 shade slf4j,因为如果只 shade 了 logback 中 ch.qos.logback 开头的类的话,那么 shade 之后的 logback 还是基于没有 shade 的 slf4j 进行实现的。那么此时 logback-shaded 和 logback 同时被引入的话,就会作为 slf4j 的两个实现,从而冲突了的,因此我们在 shade logback 的时候也要 shade 一份 slf4j。这样 logback-shaded 是 slf4j-shaded 的实现,标准 logback 是标准 slf4j 的实现,两者互相不冲突。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>${maven-shade-plugin.version}</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <minimizeJar>false</minimizeJar> <createSourcesJar>true</createSourcesJar> <shadeSourcesContent>true</shadeSourcesContent> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" /> </transformers> <relocations> <relocation> <pattern>org.slf4j</pattern> <shadedPattern>${shade-prefix}.shaded.org.slf4j</shadedPattern> </relocation> </relocations> </configuration> </execution> </executions> </plugin>
|
值得提醒的是,使用 shade 之后 logback 的话,配置文件中使用的 appender 类也要都替换成 shade 之后的类名。
这里 shade 的方法主要是参考了 glowroot, 👏👏👏 感谢提供灵感。