SkyWalking OAP 动态日志(Dynamical Logging)配置指南:core.default.log4j-xml 机制与源码解析
【免费下载链接】skywalkingAPM, Application Performance Monitoring System项目地址: https://gitcode.com/gh_mirrors/sky/skywalking
本文围绕 SkyWalking OAP 服务端的"动态日志"(Dynamical Logging)能力展开:它如何基于动态配置模块,在不停机、不手工改文件的前提下热更新 OAP 的 log4j 配置。读完本文,你将掌握配置键core.default.log4j-xml的使用方法、变更生效的周期控制、回滚与失效边界,以及从 LoggingConfigWatcher 到各配置中心实现(以 Kubernetes ConfigMap 为例)的完整调用链。
一、为什么需要动态日志
OAP 服务端使用log4j2管理日志系统。log4j2本身支持在运行时更换配置,但传统做法是人工修改 XML 配置文件并触发重载——这个过程既耗时,又容易因为手工编辑引入格式错误。
SkyWalking 的 Dynamical Logging 特性解决了这个问题:它依赖动态配置模块(Dynamic Configuration),让你通过配置中心的一次写入,即可整体更新 OAP 的log4j配置。配置项的键名为core.default.log4j-xml,你可以选择任意一个已实现的配置中心后端(如 k8s-configmap、zookeeper、etcd、nacos、consul、apollo、grpc 等)来存放log4j.xml的内容。
从源码看,这一能力的生命周期绑定在核心模块上:
- CoreModuleProvider 在
prepare()阶段创建 watcher:loggingConfigWatcher = new LoggingConfigWatcher(this); - 随后在
start()阶段,通过DynamicConfigurationService.registerConfigChangeWatcher(loggingConfigWatcher)将其注册到动态配置服务。
也就是说,一旦 core 模块启动,core.default.log4j-xml就会进入 OAP 的 log4j context,与文档描述一致。
二、配置键解析:core.default.log4j-xml
core.default.log4j-xml属于单值型(Single)动态配置,其键名由三部分组成:
| 片段 | 含义 | 取值 |
|---|---|---|
core | 模块名,对应 CoreModule.NAME | core |
default | 模块 Provider 名 | default |
log4j-xml | 配置项名(itemName) | log4j-xml |
在 LoggingConfigWatcher 的构造函数中可以看到键的拼装:
public LoggingConfigWatcher(final ModuleProvider provider) { super(CoreModule.NAME, provider, "log4j-xml"); this.ctx = (LoggerContext) LogManager.getContext(false); this.originConfiguration = (XmlConfiguration) ctx.getConfiguration(); }该 watcher 会同时持有:
- 当前
LoggerContext(ctx)——用于最终热替换日志配置; - 启动时加载的原始 XML 配置(
originConfiguration)——用于后续回滚恢复。
在 动态配置文档 的支持项表格中,该项的定义为:
| Config Key | 说明 | 格式 |
|---|---|---|
core.default.log4j-xml | log4j 的 XML 配置,覆盖本地log4j2.xml | 与 log4j2.xml 相同 |
需要理解"覆盖"的对象:OAP 随发行包提供的默认日志配置是 dist-material/log4j2.xml(打包后位于 OAP 的config目录),其中定义了RollingFile(按 102400KB 滚动、保留 30 个文件)与Console两个 Appender、Root 日志级别为info。当配置中心下发了core.default.log4j-xml后,这份运行时配置整体替换掉该默认文件生效的日志体系。
注意:动态配置功能默认是关闭的。在 application.yml 中,
configuration段默认selector: ${SW_CONFIGURATION:none},必须显式选择某个实现(如k8s-configmap)才能启用,这是使用动态日志的前置条件。
三、变更生效机制:同步周期与 period 参数
OAP 启动后,配置中心里的变更不会立即生效——OAP 采用"周期性拉取 + 变更检测"模型。文档给出的默认值是60 秒,可通过application.yml中configuration.<configuration implement>.period调整,例如 ConfigMap 实现即configuration.k8s-configmap.period。
源码印证了这两点:
- 默认 60 秒:基类 FetchingConfigWatcherRegister 的无参构造即为
this(60):
public FetchingConfigWatcherRegister() { this(60); }- 周期性调度:在
start()中(约 L83-L96),用一个单线程定时任务执行configSync():
Executors.newSingleThreadScheduledExecutor() .scheduleAtFixedRate( new RunnableWithExceptionProtection( this::configSync, t -> log.error("Sync config center error.", t) ), 0, syncPeriod, TimeUnit.SECONDS);注意初始延迟为0:启动时会先做一次"bootstrap sync"并打印 "Current configurations after the bootstrap sync.",之后每隔syncPeriod秒再拉取一次。每轮同步会读取所有已注册 SINGLE watcher 的键,与上一轮值比较,发现 ADD/MODIFY/DELETE 才回调对应 watcher 的notify()。
- 各实现注入自己的 period:以 ConfigMap 为例,ConfigmapConfigurationWatcherRegister 构造时传入
settings.getPeriod();而period字段定义在 ConfigmapConfigurationSettings(注释为 "the period for skywalking configuration reloading"),同文件还有namespace(部署命名空间)与labelSelector(用于定位目标 ConfigMap)两个必填项。
实践提示:
period控制的是整个动态配置模块的同步节奏,不只是日志。调小(如 10 秒)会让日志变更更快生效,但会增加配置中心的读取频率;调大则所有动态配置项的变更都会变慢。
四、回滚与关闭:何时恢复默认 log4j2.xml
文档明确:如果从配置中心删除core.default.log4j-xml,或禁用整个配置模块,则 OAP 会回落到config目录下的默认log4j2.xml。
删除场景在源码中有清晰的对应实现。LoggingConfigWatcher.notify() 收到 DELETE 事件时:
if (EventType.DELETE.equals(value.getEventType())) { this.content = null; newValue = null; }随后进入reconfigure(null):
private boolean reconfigure(final String newValue) { if (Strings.isNullOrEmpty(newValue)) { if (ctx.getConfiguration().equals(originConfiguration)) { return false; // 当前就是原始配置,无需切换 } ctx.onChange(originConfiguration); // 恢复启动时加载的原始 XML 配置 return true; } OapConfiguration oc; try { oc = new OapConfiguration(ctx, new ConfigurationSource(new ByteArrayInputStream(newValue.getBytes()))); } catch (IOException e) { throw new RuntimeException(String.format("failed to parse %s from configuration center", newValue), e); } oc.initialize(); ctx.onChange(oc); return true; }三个值得注意的细节:
- 回滚是精确恢复到"启动时的配置",而不是文件重新加载:构造函数里保存的
originConfiguration就是 OAP 启动时从log4j2.xml解析出的XmlConfiguration实例,DELETE 时直接ctx.onChange(originConfiguration)换回去。 - 幂等保护:若当前配置已经是
originConfiguration(例如从未下发过),DELETE 事件直接返回,避免无意义的 context 切换。 - 禁用配置模块的场景更直接:模块未启用时 watcher 根本不参与同步,log4j context 自始至终使用
config目录中的log4j2.xml,自然不受影响。
事件类型(ADD/MODIFY/DELETE)与观察模式(SINGLE/GROUP)定义在抽象基类 ConfigChangeWatcher 中,log4j-xml走的是SINGLE模式。
五、格式限制:OAP 只支持 XML
原文档有一条重要告诫:OAP 仅支持 XML 配置格式(不支持 properties、YAML 等 log4j2 其他格式)。这一点由 OapConfiguration 的类定义直接决定——它硬编码继承自XmlConfiguration:
public class OapConfiguration extends XmlConfiguration { public OapConfiguration(final LoggerContext loggerContext, final ConfigurationSource configSource) { super(loggerContext, configSource); } }因此下发到配置中心的内容必须是完整的<Configuration>XML 文档。另外,reconfigure()外层捕获了Throwable,若新配置解析或应用失败,只会记录failed to apply configuration to log4j错误日志并保留旧配置继续运行,不会导致 OAP 崩溃——这是一个对生产环境友好的失败语义:写错了 XML,日志系统会"保持现状"而不是挂掉,你需要通过错误日志排查配置内容。
六、实战:通过 Kubernetes ConfigMap 配置动态日志
以下示例来自 官方文档:在 Kubernetes 集群中,将 log4j XML 直接写入 OAP 使用的 ConfigMap,即可动态调整各包的日志级别。其他配置中心(Consul、etcd、Nacos 等)可参照同样思路。
apiVersion: v1 data: core.default.log4j-xml: |- <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout charset="UTF-8" pattern="%d - %c - %L [%t] %-5p %x - %m%n"/> </Console> </Appenders> <Loggers> <logger name="io.grpc.netty" level="INFO"/> <logger name="org.apache.skywalking.oap.server.configuration.api" level="TRACE"/> <logger name="org.apache.skywalking.oap.server.configuration.configmap" level="DEBUG"/> <Root level="WARN"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration> kind: ConfigMap metadata: labels: app: collector release: skywalking name: skywalking-oap namespace: default这个示例展示了两个典型用法:把org.apache.skywalking.oap.server.configuration.api调到TRACE、...configuration.configmap调到DEBUG——这正是排查"动态配置本身为何没生效"时的关键日志源;同时把 Root 提到WARN降低生产环境的日志噪音,并只保留ConsoleAppender 输出到标准输出,方便容器场景下采集。
启用配置模块的前置步骤
ConfigMap 方案要跑通,application.yml中的configuration段需同时满足:
configuration: selector: k8s-configmap # 启用 ConfigMap 实现(默认 none,即关闭) k8s-configmap: namespace: default # ConfigMap 所在命名空间 labelSelector: app=collector # 定位 ConfigMap 的标签选择器 period: 60 # 同步周期(秒),即变更生效的最大延迟两个约束来自 ConfigmapConfigurationProvider 的initConfigReader():若labelSelector或namespace为空,直接抛出ModuleStartException("the settings of configmap configuration is illegal.")。ConfigMap 实例内部通过ConfigurationConfigmapInformer读取data,ConfigmapConfigurationWatcherRegister.readConfig() 再按 watcher 注册的键名逐项取值。
七、端到端流程:一次日志变更的完整链路
把源码串起来,ConfigMap 中修改core.default.log4j-xml之后,OAP 内部的完整链路是:
- 定时同步:
FetchingConfigWatcherRegister的单线程调度器每隔period秒调用configSync(); - 读取:经 ConfigMap 实现的
readConfig()从 informer 缓存取出键core.default.log4j-xml的新值; - 变更检测:与上一轮快照比较,判定为 MODIFY,构造
ConfigChangeEvent(newXml, EventType.MODIFY); - 通知:
LoggingConfigWatcher.notify()被回调; - 热替换:以新 XML 字节流构造
ConfigurationSource→ 创建OapConfiguration→initialize()完成解析与校验 →ctx.onChange(oc)让 log4j 的LoggerContext原子切换到新配置,新的日志级别、Appender、Pattern 立即对新日志记录生效; - 失败兜底:解析/应用异常被捕获,记录错误日志,旧配置保持不变。
从源码结构看还有一个边界:registerConfigChangeWatcher()在isStarted为 true 后会抛出IllegalStateException,即 watcher 必须在配置模块start()之前注册——CoreModuleProvider正是先在prepare()创建 watcher、再于start()内注册的顺序,满足了这一约束。
八、注意事项与排障建议
- XML 完整性:下发内容必须是完整、良构的
<Configuration>文档,OAP 不支持 log4j2 的其他配置格式; - 生效延迟:变更后需等待一个同步周期(默认 60 秒)才会应用;若长期未生效,先把
org.apache.skywalking.oap.server.configuration.api调到TRACE、org.apache.skywalking.oap.server.configuration.configmap调到DEBUG(如第六节示例),观察同步与 watcher 回调日志; - 确认 selector 已启用:
configuration.selector为none(默认)时,动态配置整体关闭,core.default.log4j-xml不会被读取; - ConfigMap 定位参数:
namespace与labelSelector缺一不可,否则配置模块启动即失败; - 回滚手段:删除配置中心的
core.default.log4j-xml键,等待一个周期后,OAP 自动恢复启动时的默认log4j2.xml配置(参见第四节源码逻辑); - 历史修复参考:早期版本曾出现 LoggingConfigWatcher 的 value() 与真实配置内容不一致的问题,已在 8.8.0 修复,当前实现中
value()始终返回最后一次成功应用的content字段。
九、参考文件
| 资源 | 路径 |
|---|---|
| 动态日志文档 | docs/en/setup/backend/dynamical-logging.md |
动态配置总览(含core.default.log4j-xml配置表) | docs/en/setup/backend/dynamic-config.md |
| 默认日志配置(发行包模板) | dist-material/log4j2.xml |
| 日志配置监听器 | oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/logging/LoggingConfigWatcher.java |
| XML 配置封装 | oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/logging/log4j/OapConfiguration.java |
| Watcher 抽象与事件类型 | oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/ConfigChangeWatcher.java |
| 周期同步调度基类(默认 60 秒) | oap-server/server-configuration/configuration-api/src/main/java/org/apache/skywalking/oap/server/configuration/api/FetchingConfigWatcherRegister.java |
| ConfigMap 实现(Provider/Settings/WatcherRegister) | oap-server/server-configuration/configuration-k8s-configmap/src/main/java/org/apache/skywalking/oap/server/configuration/configmap/ |
| OAP 启动配置 | oap-server/server-starter/src/main/resources/application.yml |
【免费下载链接】skywalkingAPM, Application Performance Monitoring System项目地址: https://gitcode.com/gh_mirrors/sky/skywalking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考