Strimzi Kafka Operator 日志动态变更系统测试:LoggingChangeST 如何验证 Inline / External 日志配置 📅 发布时间:2026/9/17 3:32:01 👁 浏览次数: Strimzi Kafka Operator 日志动态变更系统测试LoggingChangeST 如何验证 Inline / External 日志配置【免费下载链接】strimzi-kafka-operatorApache Kafka® running on Kubernetes项目地址: https://gitcode.com/GitHub_Trending/st/strimzi-kafka-operator本文以 Strimzi Kafka Operator 仓库中的系统测试套件LoggingChangeST为主体完整解析该套件覆盖的 15 个测试用例如何在不重启 Pod 的前提下动态修改 Kafka Broker、Entity Operator、Kafka Connect、Kafka Bridge、MirrorMaker2 与 Cluster Operator 的日志级别与格式以及每种变更是否触发滚动更新的判定逻辑。读完本文你可以掌握 Strimzi 日志配置模型inline/external两种Logging类型的实际行为边界、Log4j2monitorInterval热重载机制以及每个组件日志变更的验证手段与源码级证据。1. 测试套件定位与前置条件该套件对应的系统文档位于 io.strimzi.systemtest.log.LoggingChangeST.md测试实现位于 LoggingChangeST.java。文档对套件的描述是This suite verifies logging behavior under various configurations and scenarios.验证各种配置与场景下的日志行为。套件级信息与源码注解一一对应| 项目 | 内容 | 源码依据 | | - | - | - | | 套件描述 | 验证各种配置和场景下的日志行为 |SuiteDocLoggingChangeST.java | | 执行前置步骤 | 部署 Cluster OperatorBeforeAll中以默认配置安装 CO | LoggingChangeST.java | | 测试标签 |kafka、logging见 kafka.md、logging.md类级Tag(REGRESSION)| LoggingChangeST.java |套件共包含 15 个测试方法按验证目标可归纳为以下几组| 测试方法 | 组件 | 验证重点 | 源码位置 | | - | - | - | - | |testChangingInternalToExternalLoggingDoesNotTriggerRollingUpdate| Kafka | 日志配置从默认internal切换到 external 不触发滚动更新 | L2146 | |testDynamicallySetKafkaLoggingLevels| Kafka | inline 与 external 方式动态调整级别 | L1178 | |testDynamicallySetKafkaExternalLogging| Kafka | 直接修改 external ConfigMap 的日志级别 | L1454 | |testDynamicallySetUnknownKafkaLogger| Kafka | 在资源中动态设置未知 logger | L1374 | |testDynamicallySetUnknownKafkaLoggerValue| Kafka | 未知日志级别取值不触发滚动更新 | L1416 | |testNotExistingCMSetsDefaultLogging| Kafka | 引用的 ConfigMap 不存在时的容错行为 | L1938 | |testDynamicallySetEOloggingLevels| Entity Operator | TO / UO 日志级别动态更新 | L571 | |testDynamicallyAndNonDynamicSetConnectLoggingLevels| Kafka Connect | Connect 日志级别动态/非动态变更 | L1035 | |testLoggingHierarchy| Kafka Connect Connector | Connect 与 Connector 的日志层级隔离 | L2059 | |testDynamicallySetBridgeLoggingLevels| Kafka Bridge | Bridge 日志级别动态更新 | L784 | |testDynamicallySetMM2LoggingLevels| MirrorMaker2 | MM2 日志级别动态更新 | L1657 | |testMM2LoggingLevelsHierarchy| MirrorMaker2 | MM2 logger 层级继承关系 | L1791 | |testDynamicallySetClusterOperatorLoggingLevels| Cluster Operator | CO 自身日志级别动态更新 | L928 | |testJSONFormatLogging| 全部组件 |JsonLayoutJSON 格式日志 | L370 | |testJsonTemplateLayoutFormatLogging| 全部组件 |JsonTemplateLayoutJSON 格式日志需 Kafka ≥ 4.0.0 | L125 |2. 被测对象Strimzi 的日志配置模型理解这 15 个用例之前先明确测试作用的目标模型。Strimzi 所有支持日志配置的组件共用抽象类 Logging通过type字段区分两个子类型inlineInlineLogging日志级别直接写在自定义资源的spec.*.logging.loggers映射表中externalExternalLogging通过valueFrom.configMapKeyRef引用用户提供的 ConfigMap 中的 Log4j properties 文件。2.1 Operator 侧的配置生成逻辑LoggingUtils.loggingConfiguration() 是两种类型的统一处理入口inline先读取组件内置的默认日志配置defaultLogConfig()再把loggers映射中的键值对叠加上去最终生成 properties 字符串external校验valueFrom.configMapKeyRef必须同时给出 ConfigMap 名称与 key然后从用户 ConfigMap 中取出对应数据若 ConfigMap 或其 key 不存在抛出InvalidResourceException错误信息格式为ConfigMap %s with external logging configuration does not exist ...这正是第 4.1.6 节用例断言的状态信息未设置 logging直接使用默认配置。两个关键细节直接解释了多个测试用例的断言点monitorInterval30自动注入maybeAddMonitorIntervalToExternalLogging() 会在生成的 Log4j2 配置中追加monitorInterval30常量定义于 L34让 Log4j2 每 30 秒检查一次配置文件并热重载——这就是改日志不滚动 Pod的底层机制。测试代码中大量...out().contains(monitorInterval30)的等待断言如 L695-L696、L834验证的正是这一点默认配置的来源defaultLogConfig() 从 classpath 的/default-logging/{BaseName}.properties读取。仓库中 cluster-operator/src/main/resources/default-logging/ 目录为每个组件各提供一份默认文件KafkaCluster.properties、KafkaConnectCluster.properties、KafkaMirrorMaker2Cluster.properties、KafkaBridgeCluster.properties、EntityTopicOperator.properties、EntityUserOperator.properties、CruiseControl.properties。其中 Kafka 的默认配置为rootLogger.level INFO并为kafka、org.apache.kafka、kafka.request.logger、kafka.network.RequestChannel$等常见 logger 单独设定级别见 KafkaCluster.properties。此外LoggingUtils 还提供expandVars()用于展开配置值中的${NAME}变量如 CO 默认配置中的${env:STRIMZI_LOG_LEVEL:-INFO}对应 install/cluster-operator/050-ConfigMap-strimzi-cluster-operator.yaml 中 CO 的log4j2.properties写法。该类的单元测试位于 LoggingUtilsTest.java。3. 测试的四种验证手段整套用例的断言基于四种互补的验证手段理解它们是读懂每个用例的前提直接读 Pod 内的配置文件通过exec在 Pod 容器内执行cat。各组件的日志配置挂载路径分别为 Entity Operator 的/opt/topic-operator/custom-config/log4j2.properties、/opt/user-operator/custom-config/log4j2.propertiesL612-L613Bridge 与 CO 的/opt/strimzi/custom-config/log4j2.propertiesL833、L931Kafka AdminClient 动态 logger 查询Kafka 用例通过KafkaCmdClient.describeKafkaBrokerLoggersUsingPodCli(...)在Scraper Pod中运行kafka-configsCLI 查询 broker 的log4j.logger.*动态配置检查输出是否包含rootDEBUG、paprikaINFO等L1236。Scraper Pod 是一个辅助 Pod配合 NetworkPolicy 打通对 Kafka / Connect 服务的访问Connect REST APIConnect 与 MM2 用例在 Pod 内curl http://localhost:8083/admin/loggers/{name}查询运行时的 logger 级别L1702、L2100日志抓取 快照对比StUtils.getLogFromPodByTime(ns, pod, container, 30s)抓取最近 30 秒日志再用默认日志格式正则DEFAULT_LOG4J_PATTERNL105判断日志行是否出现/消失同时用PodUtils.podSnapshot()/DeploymentUtils.depSnapshot()记录 Pod 状态快照配合RollingUpdateUtils.waitForNoRollingUpdate()与componentHasRolled()断言变更前后 Pod 是否滚动。4. 分组件测试用例详解4.1 Kafka动态级别、未知 logger 与容错4.1.1 testDynamicallySetKafkaLoggingLevelsinline 与 external 双路径文档步骤LoggingChangeST.md以 OFF 级别部署 Kafka → 验证日志为空 → inline 改为rootLogger.levelDEBUG→ 验证 DEBUG 日志出现 → 切换为 INFO 级别的 external 日志 → 验证生效 → 断言全程无滚动更新。源码实现L1178-L1357中初始 inline 配置将九个常见 logger 全部置 OFF# Kafka.spec.kafka.logginginline 方式的 loggers 映射 loggers: rootLogger.level: OFF logger.kafka.level: OFF logger.orgapachekafka.level: OFF logger.requestlogger.level: OFF logger.requestchannel.level: OFF logger.controller.level: OFF logger.logcleaner.level: OFF logger.statechange.level: OFF logger.authorizer.level: OFF type: inline随后 inline 仅改rootLogger.levelDEBUG并等待describeKafkaBrokerLoggersUsingPodCli的输出包含rootDEBUG再创建名为external-configmap的 ConfigMapkey 为log4j.properties内容为rootLogger.level INFO及全套logger.*配置见 L1252-L1320把 Kafka 的 logging 切到 external等待 CLI 输出包含rootINFO。最后以RollingUpdateUtils.componentHasRolled(...) is false断言 broker Pod 未被替换L1356。4.1.2 testDynamicallySetKafkaExternalLogging只改 ConfigMap 不动资源该用例L1454-L1635先创建external-cmkeylog4j.propertiesrootLogger.level INFO部署引用它的 Kafka 集群随后只更新 ConfigMap将rootLogger、logger.kafka、logger.orgapachekafka、logger.controller、logger.logcleaner、logger.statechange、logger.authorizer等从INFO改为ERRORlogger.requestlogger/logger.requestchannel保持WARN。验证方式为waitForNoRollingUpdatecomponentHasRolled is false确认无滚动再用 CLI 确认kafka.authorizer.loggerERROR已在 broker 生效L1631-L1634。这说明仅修改 external ConfigMap 中的级别类配置是动态生效的。4.1.3 testDynamicallySetUnknownKafkaLogger新增未知 logger文档步骤获取 broker / Scraper Pod 名 → 快照 broker Pod 状态 → 用 InlineLogging 设置log4j.logger.paprikaINFO→ 等待变更完成 → 验证paprikaINFO。源码L1374-L1400中对应的 inline 配置为logger.paprika.namepaprika、logger.paprika.levelINFO验证时断言 CLI 输出包含paprikaINFO证明用户可以为任意包名哪怕是 Kafka 中不存在的paprika动态添加 logger 条目。4.1.4 testDynamicallySetUnknownKafkaLoggerValue非法级别取值将rootLogger.level设置为不存在的级别PAPRIKAL1427随后waitForNoRollingUpdate并断言componentHasRolled is falseL1433-L1434。用例结论即便资源中写出了无效的级别取值Operator 也不会因此强制滚动 broker Pod。4.1.5 testChangingInternalToExternalLoggingDoesNotTriggerRollingUpdateinternal → external 切换文档步骤与源码 L2146-L2246 对应部署一个未配置任何 logging的 Kafka 集群此时 broker 动态 logger 查询显示rootDEBUG将 Kafka 的 logging 改为 external引用 ConfigMaploggers-config-map中的 keylog4j2-custom.properties将 ConfigMap 中property.kafka.root.logger.level从INFO改为WARN。external 配置本身L2165-L2200使用了 Log4j2 动态属性值得注意monitorInterval 10与rootLogger.level ${kafka.root.logger.level}的写法status WARN # Periodic interval for Log4j2 to check the properties and change it accordingly monitorInterval 10 # Dynamic property property.kafka.root.logger.level INFO appender.console.type Console appender.console.name CONSOLE appender.console.target SYSTEM_OUT appender.console.layout.type PatternLayout appender.console.layout.pattern %d{ISO8601} %p %m (%c) [%t] rootLogger.level ${kafka.root.logger.level} rootLogger.appenderRef.console.ref CONSOLE logger.kafka.name kafka logger.kafka.level INFO # ... logger.orgapachekafka / logger.kafkarequest / logger.kafkanetproc / # logger.kafkaserverapis / logger.kafkarequestchannel / logger.kafkacontroller / # logger.kafkalogcleaner / logger.statechange(TRACE) / logger.kafkabrokerauth验证点两次变更切换 external、更新 ConfigMap后都等待 CLI 输出分别变为rootINFO、rootWARN且对controller 与 broker 两组 selector均断言无滚动更新L2226-L2245。4.1.6 testNotExistingCMSetsDefaultLoggingConfigMap 缺失时的行为文档步骤创建并引用有效的 external ConfigMap → 修改 Kafka 指向一个不存在的ConfigMapnon-existing-cm-name→ 验证不滚动且状态中出现错误信息。源码L1938-L2040还额外断言了两点实现细节变更失败后broker 内custom-config/log4j2.properties仍保留之前生效的自定义配置断言contains(loggingConfiguration)且not contains(defaultProps)L2032-L2034测试用 KafkaCluster.properties 的内容作为defaultProps对照Kafka CR 的status.conditions中出现NotReady条件且消息匹配ConfigMap non-existing-cm-name with external logging configuration does not exist .*L2037-L2039——该消息格式与 LoggingUtils 抛出的InvalidResourceException完全对应。4.2 Entity OperatorTO / UO 日志的动态更新testDynamicallySetEOloggingLevelsL571-L762的文档步骤为四步inline OFF 部署 → inline 改 DEBUG → 切 external OFF → 更新 external 为 DEBUG。实现细节对spec.entityOperator.topicOperator.logging与userOperator.logging分别设置两者共用同一个 EO Pod但各自独立挂载配置文件/opt/topic-operator/custom-config/与/opt/user-operator/custom-config/每次变更后先等待两个容器的log4j2.properties都出现目标级别如rootLogger.levelDEBUG且同时包含monitorInterval30L692-L697再等待 30 秒窗口内日志出现/消失external 配置采用nameTOConfig/nameUOConfig的完整 Log4j2 配置PatternLayout%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n结尾断言 EO Deployment 快照未变L761即日志级别变更全程不滚动 EO Pod。4.3 Kafka Connect级别变更与 Connector 日志层级4.3.1 testDynamicallyAndNonDynamicSetConnectLoggingLevels文档步骤9 步概括为以rootLogger.levelOFF部署 3 副本 Connect Scraper Pod NetworkPolicy → 验证无日志 → inline 改 DEBUG 验证 DEBUG 行 → inline 改 INFO 验证无 DEBUG 行 → 切 external ConfigMapOFF验证静默 → 断言 Pod 无滚动。源码L1035-L1157的补充细节Connect CR 带有注解strimzi.io/use-connector-resources: trueL1048文档步骤 7 即围绕该注解对应的 connector 资源行为展开日志级别判定使用两个专用正则log4jPatternDebugLevel/log4jPatternInfoLevelL1037-L1038并通过KafkaConnectUtils.waitForConnectLogLevelChangePropagation轮询传播结果external 配置external-cmkeylog4j.properties除rootLogger.level OFF外还显式压制org.reflections到 ERRORL1103-L1119每个阶段之后都有assertThat(Connect Pod should not roll, ..., equalTo(connectPods))L1084、L1100、L1156。4.3.2 testLoggingHierarchyConnector 日志不继承 Connect 根级别文档 7 步对应源码L2059-L2130部署 3 broker / 3 controller 的 Kafka 带 File Plugin 的 Connect配置key.converter/value.converter为StringConverter 一个KafkaConnectorFileStreamSourceConnector Scraper Pod NetworkPolicy通过 inline 配置logger.connector.name org.apache.kafka.connect.file.FileStreamSourceConnector、logger.connector.level ERROR验证curl http://connect-svc:8083/admin/loggers/class返回包含ERROR通过 Connect REST APIPOST /connectors/{name}/restart重启 connector等待其恢复RUNNING后再次查询——connector 级别仍保持 ERROR重启不丢失将 Connect 的rootLogger.level提到WARN同时保留 connector 条目为 ERROR验证 root 变为 WARN 后 connector 的 logger不继承Connect 的根级别KafkaConnectorUtils.loggerStabilityWait持续确认仍为 ERRORL2129。4.4 Kafka BridgetestDynamicallySetBridgeLoggingLevelsL784-L907按文档 8 步执行inline OFF 部署 Bridge → 验证日志为空 → inline 改rootLogger.levelDEBUG保留logger.bridge/healthy/readyOFF→ 等待/opt/strimzi/custom-config/log4j2.properties出现rootLogger.levelDEBUG与monitorInterval30→ 验证 DEBUG 日志出现 → 切 externalConfigMapexternal-configmap-bridge→ 验证日志再次为空 → 断言 Bridge Pod 快照未变。external 配置中值得注意的注释L867-L872说明了为何要把http.openapi.operation.healthy/http.openapi.operation.ready两个 logger 设为 OFFKubernetes 健康检查会高频调用这两个端点其日志默认非常冗长这也是 Bridge 默认日志配置单独列出这两个 logger 的原因。4.5 MirrorMaker24.5.1 testDynamicallySetMM2LoggingLevels文档 7 步对应源码L1657-L1770源/目标两个单节点 Kafka 集群 初始rootLogger.levelOFF并同步压制logger.reflections.levelOFF的 MM2 → 验证日志为空 → inline 改 DEBUG 并等待 Pod 内curl http://localhost:8083/admin/loggers/root返回DEBUG→ 验证 DEBUG 日志出现 → 创建 external ConfigMaprootLogger.level OFFkeylog4j.properties→ 切换后 REST 查询返回OFF→ 断言 MM2 Pod 快照未变L1769。MM2 复用 Connect 的 8083 REST 端口因此日志验证手段与 Connect 相同。4.5.2 testMM2LoggingLevelsHierarchy日志级别继承规则该用例L1791-L1920通过 external ConfigMap 验证 Log4j2 的层级继承初始配置中rootLoggerOFF并显式设置org.eclipse.jetty.util.threadFATAL、org.apache.kafka.connect.runtime.WorkerTaskOFF、org.eclipse.jetty.util.thread.strategy.EatWhatYouKillOFF更新后的配置把 root 提到INFO、org.eclipse.jetty.util.threadWARN并保留后两个子 logger 但删除其 levelL1875-L1898验证三条继承链L1911-L1917root 为INFOEatWhatYouKill继承父级org.eclipse.jetty.util.thread的WARNWorkerTask其父级未配置 level继承 root 的INFO。4.6 Cluster Operator修改 CO 自身的日志testDynamicallySetClusterOperatorLoggingLevelsL928-L1010标注为IsolatedTest注释说明原因是会配置并抓取共享 CO 的日志因为被测对象就是测试环境自身部署的那个 Cluster Operator先执行cat /opt/strimzi/custom-config/log4j2.properties确认当前配置与新配置不同更新 Operator 命名空间中名为strimzi-cluster-operator即TestConstants.STRIMZI_DEPLOYMENT_NAME、带app: strimzi标签的 ConfigMap该 ConfigMap 与 050-ConfigMap-strimzi-cluster-operator.yaml 部署的 CO 日志配置对应新配置将rootLogger.level与org.apache.kafka均置 OFF等待 CO Pod 内配置文件逐字等于新配置并用 Deployment 快照断言CO Pod 未被替换——变更由 Log4j2 热重载完成无需滚动再把所有OFF批量替换为INFO更新一次等待配置文件中出现rootLogger.level INFO快照再次断言未滚动最后确认日志行恢复出现。4.7 JSON 格式日志两个 JSON 用例都要求非 Helm、非 OLM 安装方式assumeTrue(!Environment.isHelmInstall() !Environment.isOlmInstall())因为需要直接改写 Operator 自身命名空间中的 CO ConfigMap。4.7.1 testJSONFormatLoggingJsonLayout为 KafkaConfigMapjson-layout-kafkakeylog4j.propertiesappender.console.layout.typeJsonLayout、EO 的 topic/user operatorjson-layout-operatorskeylog4j.properties/log4j2.properties以及 CO 各自创建 JSON 布局配置然后对 CO、broker、controller、topic-operator、user-operator 五个范围调用StUtils.checkLogForJSONFormat断言每行日志都是 JSONL544-L548最后把 CO 的log4j2.properties还原为测试前备份的原始值。4.7.2 testJsonTemplateLayoutFormatLoggingJsonTemplateLayout与 JsonLayout 用例相比有三个差别L125-L351前置条件assumeTrue(TestKafkaVersion.compareDottedVersions(Environment.ST_KAFKA_VERSION, 4.0.0) 0)即 Kafka 版本 ≥ 4.0.0 才支持JsonTemplateLayoutL127-L128布局改为可自定义字段的JsonTemplateLayouteventTemplate指定了带 UTC 时区时间戳的instant解析器、常量字段与 stringified 的messageL140-L141并通过StUtils.JSON_TEMPLATE_LAYOUT_PATTERN校验输出形状覆盖范围扩大到CruiseControl额外创建json-layout-ccConfigMap 并配置spec.cruiseControl.loggingL275-L331断言时新增 cruise-control 容器L346。5. 滚动更新行为总览将 15 个用例的断言汇总可以得出该套件验证的行为矩阵均为源码断言可直接证实的事实| 变更类型 | 是否滚动 Pod | 依据 | | - | - | - | | Kafka inline 级别变更DEBUG/INFO 等 | 否 | L1356 | | Kafka external ConfigMap 内级别变更 | 否 | L1631-L1632 | | Kafka 新增未知 loggerinline | 无滚动断言动态生效CLI 出现paprikaINFO | L1398-L1399 | | Kafka 非法级别取值PAPRIKA | 否 | L1433-L1434 | | Kafka internal → external 切换 | 否 | L2226-L2245 | | Kafka 引用的 ConfigMap 不存在 | 否CR 进入 NotReady 并报告错误 | L2024-L2039 | | EO / Connect / Bridge / MM2 / CO 日志变更 | 否各组件结尾快照断言 | L761、L1156、L906、L1769、L977 |支撑不滚动的机制在 Operator 源码中同样清晰LoggingUtils 为生成的 Log4j2 配置注入monitorInterval30由 Log4j2 自身的配置监听完成级别热更新Kafka Broker 侧则通过其 broker 动态配置log4j.logger.*与 AdminClient 通道生效。6. 延伸阅读路径测试套件文档本文章骨架来源development-docs/systemtests/io.strimzi.systemtest.log.LoggingChangeST.md标签说明development-docs/systemtests/labels/kafka.md、development-docs/systemtests/labels/logging.md测试实现systemtest/src/test/java/io/strimzi/systemtest/log/LoggingChangeST.javaAPI 模型Logging.java、InlineLogging.java、ExternalLogging.javaOperator 日志配置生成LoggingUtils.java 及其单测 LoggingUtilsTest.java各组件默认日志配置cluster-operator/src/main/resources/default-logging/CO 自身日志 ConfigMapinstall/cluster-operator/050-ConfigMap-strimzi-cluster-operator.yaml。【免费下载链接】strimzi-kafka-operatorApache Kafka® running on Kubernetes项目地址: https://gitcode.com/GitHub_Trending/st/strimzi-kafka-operator创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考