如何配置Quarkus使所有日志(含启动/运行时)输出至文件及控制台?
解决Quarkus日志双输出(控制台+文件)缺失问题
问题描述
我们有多台配置了控制台及文件日志的Quarkus应用,存在以下日志异常:
- 部分Quarkus产生的错误既未输出至控制台也未写入文件
- 部分错误仅输出至控制台但未写入文件
由于应用通常以Linux服务方式启动,控制台日志难以查看,因此需要配置Quarkus,使**所有日志(包括启动时和运行时的错误)**无论以何种方式启动(控制台/服务),都能同时输出至文件和控制台。
当前日志配置
log: level: INFO handler: console: "CONSOLE": async: false enable: true format: '%K{level}%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p (%t) [%c.%M()] %s%e%n' file: "FILE": async: false enable: true format: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p (%t) [%c.%M()] %s%e%n' #level: INFO path: '/opt/remote-server/logs/remote-server.log' rotation: file-suffix: .yyyy-MM-dd max-backup-index: 999 max-file-size: 40000000 rotate-on-boot: true "AUDIT": async: false enable: true format: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p (%t) [%c.%M()] %s%e%n' #level: INFO path: '/opt/remote-server/logs/remote-server-audit.log' rotation: file-suffix: .yyyy-MM-dd max-backup-index: 999 max-file-size: 40000000 rotate-on-boot: true "DATAAUDIT": async: false enable: true format: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p (%t) [%c.%M()] %s%e%n' #level: INFO path: '/opt/remote-server/logs/remote-server-dataaudit.log' rotation: file-suffix: .yyyy-MM-dd max-backup-index: 999 max-file-size: 40000000 rotate-on-boot: true category: "com.bs.util.accumulator.AccumulatorLogger": level: INFO use-parent-handlers: false handlers: DATAAUDIT "com.bs.audit.Auditor": level: INFO handlers: AUDIT "org.hibernate.sql": level: ERROR handlers: FILE "org.hibernate.type.descriptor.sql": level: ERROR handlers: FILE "io.quarkus.http.access-log": level: ERROR handlers: FILE "com.bs.sdl.async": level: ERROR handlers: FILE "com.bs.sdl.service": level: ERROR handlers: FILE "com.bs": level: INFO handlers: FILE
未写入文件的错误示例
2023-07-14 20:34:52,659 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-2) HTTP Request to /datasync/orchestration/remote failed, error id: bbe963ab-803c-4af1-a1b8-52beccf3287e-1: org.jboss.resteasy.spi.UnhandledException: com.bs.aws.exception.client.KmsClientCreateException: Unable to create an KmsClient after employing the following strategies, [EC2InstanceProfileCredentialsProvider] at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:368) at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:218) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:519) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364) at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247) at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:82) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:147) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:93) at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:829) Caused by: com.bs.aws.exception.client.KmsClientCreateException: Unable to create an KmsClient after employing the following strategies, [EC2InstanceProfileCredentialsProvider] at com.bs.aws.factory.EC2InstanceProfileCredentialsProviderStrategyAwsClientFactory.getAwsKmsClient(EC2InstanceProfileCredentialsProviderStrategyAwsClientFactory.java:234) at com.bs.aws.api.impl.AwsKmsApi.<init>(AwsKmsApi.java:92) at com.bs.aws.api.impl.AwsKmsApi.<init>(AwsKmsApi.java:83) at com.bs.aws.api.impl.AwsKmsApi_Bean.create(Unknown Source) at com.bs.aws.api.impl.AwsKmsApi_Bean.create(Unknown Source) at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:113) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:37) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:34) at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26) at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69) at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:34) at com.bs.aws.api.impl.AwsKmsApi_Bean.get(Unknown Source) at com.bs.aws.api.impl.AwsKmsApi_Bean.get(Unknown Source) at com.bs.remote.server.rest.EnterpriseDataSyncOrchestrationREST_Bean.create(Unknown Source) at com.bs.remote.server.rest.EnterpriseDataSyncOrchestrationREST_Bean.create(Unknown Source) at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:113) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:37) at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:34) at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26) at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69) at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:34) at io.quarkus.arc.impl.ClientProxies.getApplicationScopedDelegate(ClientProxies.java:21) at com.bs.remote.server.rest.EnterpriseDataSyncOrchestrationREST_ClientProxy.arc$delegate(Unknown Source) at com.bs.remote.server.rest.EnterpriseDataSyncOrchestrationREST_ClientProxy.arc_contextualInstance(Unknown Source) at io.quarkus.arc.ClientProxy.unwrap(ClientProxy.java:52) at io.quarkus.arc.runtime.ClientProxyUnwrapper.apply(ClientProxyUnwrapper.java:15) at io.quarkus.resteasy.common.runtime.QuarkusInjectorFactory$UnwrappingPropertyInjector.inject(QuarkusInjectorFactory.java:74) at org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory.createResource(POJOResourceFactory.java:80) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:401) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:69) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492) ... 15 more
配置调整方案
问题根源分析
- 根日志未明确指定同时输出到
CONSOLE和FILE处理器,导致未配置category的日志可能只走默认输出 - 部分日志类别(如
io.quarkus开头的组件)未配置处理器,仅依赖父处理器,但父处理器可能未关联双输出 - 启动阶段的日志未配置文件输出,导致早期启动错误无法写入文件
- 部分category配置了
use-parent-handlers: false且仅指定了专属处理器,阻断了双输出
调整后的完整配置
log: level: INFO # 根日志同时关联控制台和文件处理器,确保默认所有日志双输出 handlers: CONSOLE, FILE handler: console: "CONSOLE": async: false enable: true format: '%K{level}%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p (%t) [%c.%M()] %s%e%n' file: "FILE": async: false enable: true # 开启启动阶段文件日志输出 enable-at-boot: true format: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p (%t) [%c.%M()] %s%e%n' path: '/opt/remote-server/logs/remote-server.log' rotation: file-suffix: .yyyy-MM-dd max-backup-index: 999 max-file-size: 40000000 rotate-on-boot: true "AUDIT": async: false enable: true format: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p (%t) [%c.%M()] %s%e%n' path: '/opt/remote-server/logs/remote-server-audit.log' rotation: file-suffix: .yyyy-MM-dd max-backup-index: 999 max-file-size: 40000000 rotate-on-boot: true "DATAAUDIT": async: false enable: true format: '%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p (%t) [%c.%M()] %s%e%n' path: '/opt/remote-server/logs/remote-server-dataaudit.log' rotation: file-suffix: .yyyy-MM-dd max-backup-index: 999 max-file-size: 40000000 rotate-on-boot: true category: "com.bs.util.accumulator.AccumulatorLogger": level: INFO use-parent-handlers: true handlers: DATAAUDIT, CONSOLE, FILE "com.bs.audit.Auditor": level: INFO handlers: AUDIT, CONSOLE, FILE "org.hibernate.sql": level: ERROR handlers: FILE, CONSOLE "org.hibernate.type.descriptor.sql": level: ERROR handlers: FILE, CONSOLE "io.quarkus.http.access-log": level: ERROR handlers: FILE, CONSOLE "com.bs.sdl.async": level: ERROR handlers: FILE, CONSOLE "com.bs.sdl.service": level: ERROR handlers: FILE, CONSOLE "com.bs": level: INFO handlers: FILE, CONSOLE # 新增Quarkus核心组件日志配置,确保错误双输出 "io.quarkus": level: INFO handlers: FILE, CONSOLE "io.qua.ver.htt.run.QuarkusErrorHandler": level: ERROR handlers: FILE, CONSOLE
关键调整说明
- 根日志添加handlers配置:指定
CONSOLE, FILE,确保所有未单独配置的日志默认同时输出到控制台和文件 - 开启启动阶段文件日志:为
FILE处理器添加enable-at-boot: true,解决启动时错误无法写入文件的问题 - 全类别补充双输出处理器:所有自定义category的
handlers都添加CONSOLE和FILE,确保专属日志也能双输出 - 修复use-parent-handlers设置:将
com.bs.util.accumulator.AccumulatorLogger的use-parent-handlers改为true,避免阻断父处理器的双输出逻辑 - 新增Quarkus核心组件配置:为
io.quarkus及具体错误处理类添加双输出配置,解决示例中QuarkusErrorHandler日志仅输出控制台的问题
内容的提问来源于stack exchange,提问作者user1340123
相关产品推荐
相关产品推荐

