在Micronaut 项目中,使用了 Logback 输出日志。在添加了RollingFileAppender 后,编译 Native Image 就会报错了。
反复搜索后,发现问题原因是:编译 Native Image 也会使用 logback 进行日志输出,这个时候就会打开日志文件句柄,然后编译器发现有文件句柄被打开了,编译就被中止了。
按 GitHub 上大佬的建议,解决文案是定义一个延迟加载的 FileAppender。
具体文案如下:
自定义 FileAppender
示例代码如下:
package fun.mortnon.framework.log; import ch.qos.logback.core.rolling.RollingFileAppender; import java.util.concurrent.atomic.AtomicBoolean; /** * 自定义的日志文件 appender * 避免 GraalVM Native Image 编译时 logback 进程占用日志文件导致编译失败 * * @author dev2007 * @date 2024/3/13 */ public class LazyInitRollingFileAppender<E> extends RollingFileAppender<E> { private AtomicBoolean started = new AtomicBoolean(false); @Override public void start() { if (!inGraalImageBuildtimeCode()) { super.start(); this.started.set(true); } } /** * This method is synchronized to avoid double start from doAppender(). */ protected void maybeStart() { lock.lock(); try { if (!this.started.get()) { this.start(); } } finally { lock.unlock(); } } @Override public void doAppend(E eventObject) { if (!inGraalImageBuildtimeCode()) { if (!this.started.get()) { maybeStart(); } super.doAppend(eventObject); } } private static final String PROPERTY_IMAGE_CODE_VALUE_BUILDTIME = "buildtime"; private static final String PROPERTY_IMAGE_CODE_KEY = "org.graalvm.nativeimage.imagecode"; private static boolean inGraalImageBuildtimeCode() { return PROPERTY_IMAGE_CODE_VALUE_BUILDTIME.equals(System.getProperty(PROPERTY_IMAGE_CODE_KEY)); } }
说明:实现一个延迟加载,当 Graal 编译 Native Image 时不输出日志。
注意:
需要添加 logback 依赖和 Graal 依赖(
org.graalvm.sdk:graal-sdk
)lock
锁对象来源于父类OutputStreamAppender
,如果是较高版本的 logback,锁对象的名为streamWriteLock
,要注意检查
配置 Logback
示例配置如下:
<appender name="file" class="fun.mortnon.framework.log.LazyInitRollingFileAppender"> <file>logs/${service}.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> <fileNamePattern>logs/${service}-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern> <!--每天1个文件--> <maxHistory>30</maxHistory> <!--每个文件最大50M--> <maxFileSize>50MB</maxFileSize> <!--最大容量1GB--> <totalSizeCap>1GB</totalSizeCap> </rollingPolicy> <layout class="ch.qos.logback.classic.PatternLayout"> <pattern>${pattern}</pattern> </layout> </appender>
说明:配置应用自定义的 FileAppender
在 GraalVM Native Image 的
reflect-config.json
中添加自定义的 FileAppender
示例配置如下:
{ "name": "fun.mortnon.framework.log.LazyInitRollingFileAppender", "allDeclaredFields": true, "allPublicConstructors": true, "allDeclaredMethods": true }
以上就是解决方法,如果你有好的方法,欢迎一起探讨。