This is an automated email from the ASF dual-hosted git repository.
gyfora pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-kubernetes-operator.git
The following commit(s) were added to refs/heads/main by this push:
new 9099abd2 [FLINK-39501] Logback Support in Flink Kubernetes Operator
9099abd2 is described below
commit 9099abd27f2a9de20e79fd2e194f2a593b26dee2
Author: Dennis-Mircea Ciupitu <[email protected]>
AuthorDate: Tue Apr 28 17:12:33 2026 +0300
[FLINK-39501] Logback Support in Flink Kubernetes Operator
---
.github/workflows/ci.yml | 21 ++++
.github/workflows/e2e.yaml | 3 +
Dockerfile | 5 +-
docker-entrypoint.sh | 14 ++-
docs/content.zh/docs/operations/helm.md | 3 +
docs/content.zh/docs/operations/metrics-logging.md | 101 ++++++++++++++++--
docs/content/docs/operations/helm.md | 3 +
docs/content/docs/operations/metrics-logging.md | 101 ++++++++++++++++--
e2e-tests/test_logback_logging.sh | 116 +++++++++++++++++++++
flink-kubernetes-operator/pom.xml | 34 +++++-
.../conf/logback-console.xml | 56 ++++++++++
.../conf/logback-operator.xml | 35 +++++++
.../templates/_helpers.tpl | 12 +++
.../templates/controller/configmap.yaml | 12 +++
.../templates/controller/deployment.yaml | 15 ++-
.../tests/controller/configmap_test.yaml | 32 ++++++
.../tests/controller/deployment_test.yaml | 80 ++++++++++++++
helm/flink-kubernetes-operator/values.yaml | 8 +-
pom.xml | 1 +
19 files changed, 631 insertions(+), 21 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e11507ef..1dd5a143 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -235,3 +235,24 @@ jobs:
flink-version: ${{ matrix.flink-version }}
test: ${{ matrix.test }}
mode: ${{ matrix.mode }}
+ e2e_logging:
+ name: Logging framework tests
+ needs: e2e_smoke_test
+ strategy:
+ matrix:
+ flink-version:
+ - "v2_2"
+ - "v2_1"
+ - "v2_0"
+ - "v1_20"
+ mode:
+ - "native"
+ - "standalone"
+ test:
+ - test_logback_logging.sh
+ uses: ./.github/workflows/e2e.yaml
+ with:
+ java-version: 17
+ flink-version: ${{ matrix.flink-version }}
+ test: ${{ matrix.test }}
+ mode: ${{ matrix.mode }}
diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml
index 5434da7a..02b7bd3d 100644
--- a/.github/workflows/e2e.yaml
+++ b/.github/workflows/e2e.yaml
@@ -78,6 +78,9 @@ jobs:
if [[ "${{ inputs.test }}" == "test_dynamic_config.sh" ]]; then
sed -i "s/flink-conf.yaml: |+/config.yaml: |+/"
helm/flink-kubernetes-operator/values.yaml
fi
+ if [[ "${{ inputs.test }}" == "test_logback_logging.sh" ]]; then
+ sed -i 's/framework: "log4j2"/framework: "logback"/'
helm/flink-kubernetes-operator/values.yaml
+ fi
helm --debug install flink-kubernetes-operator -n ${{
inputs.namespace }} helm/flink-kubernetes-operator --set
image.repository=flink-kubernetes-operator --set image.tag=ci-latest ${{
steps.namespace-creator.outputs.EXTRA_HELM_INSTALL_ARGS }}
kubectl wait --for=condition=Available --timeout=120s -n ${{
inputs.namespace }} deploy/flink-kubernetes-operator
kubectl get pods -n ${{ inputs.namespace }}
diff --git a/Dockerfile b/Dockerfile
index c80d0686..2f0bfc39 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -44,7 +44,8 @@ ENV
WEBHOOK_JAR=flink-kubernetes-webhook-$OPERATOR_VERSION-shaded.jar
ENV KUBERNETES_STANDALONE_JAR=flink-kubernetes-standalone-$OPERATOR_VERSION.jar
ENV OPERATOR_LIB=$FLINK_HOME/operator-lib
-RUN mkdir -p $OPERATOR_LIB
+ENV OPERATOR_LOG=$FLINK_HOME/log
+RUN mkdir -p $OPERATOR_LIB $OPERATOR_LOG/log4j $OPERATOR_LOG/logback
WORKDIR /flink-kubernetes-operator
RUN groupadd --system --gid=9999 flink && \
@@ -56,6 +57,8 @@ COPY --chown=flink:flink --from=build
/app/flink-kubernetes-operator/target/$OPE
COPY --chown=flink:flink --from=build
/app/flink-kubernetes-webhook/target/$WEBHOOK_JAR .
COPY --chown=flink:flink --from=build
/app/flink-kubernetes-standalone/target/$KUBERNETES_STANDALONE_JAR .
COPY --chown=flink:flink --from=build
/app/flink-kubernetes-operator/target/plugins $FLINK_HOME/plugins
+COPY --chown=flink:flink --from=build
/app/flink-kubernetes-operator/target/log4j/ $FLINK_HOME/log/log4j/
+COPY --chown=flink:flink --from=build
/app/flink-kubernetes-operator/target/logback/ $FLINK_HOME/log/logback/
COPY --chown=flink:flink --from=build
/app/tools/license/licenses-output/NOTICE .
COPY --chown=flink:flink --from=build
/app/tools/license/licenses-output/licenses ./licenses
COPY --chown=flink:flink --from=build /app/LICENSE ./LICENSE
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
index 7464645e..db1dde61 100755
--- a/docker-entrypoint.sh
+++ b/docker-entrypoint.sh
@@ -43,6 +43,16 @@ maybe_enable_jemalloc() {
maybe_enable_jemalloc
+# Select the logging framework JARs to put on the classpath.
+# Supported values: "log4j2" (default), "logback".
+OPERATOR_LOG=${FLINK_HOME:-/opt/flink}/log
+LOGGING_FRAMEWORK="${LOGGING_FRAMEWORK:-log4j2}"
+if [ "$LOGGING_FRAMEWORK" = "logback" ]; then
+ LOG_CLASSPATH="$OPERATOR_LOG/logback/*"
+else
+ LOG_CLASSPATH="$OPERATOR_LOG/log4j/*"
+fi
+
if [ "$1" = "help" ]; then
printf "Usage: $(basename "$0") (operator|webhook)\n"
printf " Or $(basename "$0") help\n\n"
@@ -50,12 +60,12 @@ if [ "$1" = "help" ]; then
elif [ "$1" = "operator" ]; then
echo "Starting Operator"
- exec java -cp
"./$KUBERNETES_STANDALONE_JAR:./$OPERATOR_JAR:$OPERATOR_LIB/*" $LOG_CONFIG
$JVM_ARGS org.apache.flink.kubernetes.operator.FlinkOperator
+ exec java -cp
"./$KUBERNETES_STANDALONE_JAR:./$OPERATOR_JAR:$LOG_CLASSPATH:$OPERATOR_LIB/*"
$LOG_CONFIG $JVM_ARGS org.apache.flink.kubernetes.operator.FlinkOperator
elif [ "$1" = "webhook" ]; then
echo "Starting Webhook"
# Adds the operator shaded jar on the classpath when the webhook starts
- exec java -cp
"./$KUBERNETES_STANDALONE_JAR:./$OPERATOR_JAR:./$WEBHOOK_JAR:$OPERATOR_LIB/*"
$LOG_CONFIG $JVM_ARGS
org.apache.flink.kubernetes.operator.admission.FlinkOperatorWebhook
+ exec java -cp
"./$KUBERNETES_STANDALONE_JAR:./$OPERATOR_JAR:./$WEBHOOK_JAR:$LOG_CLASSPATH:$OPERATOR_LIB/*"
$LOG_CONFIG $JVM_ARGS
org.apache.flink.kubernetes.operator.admission.FlinkOperatorWebhook
fi
args=("${args[@]}")
diff --git a/docs/content.zh/docs/operations/helm.md
b/docs/content.zh/docs/operations/helm.md
index b2a843a6..704a3f10 100644
--- a/docs/content.zh/docs/operations/helm.md
+++ b/docs/content.zh/docs/operations/helm.md
@@ -76,6 +76,8 @@ The configurable parameters of the Helm chart and which
default values as detail
| defaultConfiguration.flink-conf.yaml | The default configuration
of flink-conf.yaml.
|
kubernetes.operator.metrics.reporter.slf4j.factory.class:
org.apache.flink.metrics.slf4j.Slf4jReporterFactory<br/>kubernetes.operator.metrics.reporter.slf4j.interval:
5 MINUTE<br/>kubernetes.operator.reconcile.interval: 15
s<br/>kubernetes.operator.observer.progress-check.interva [...]
| defaultConfiguration.log4j-console.properties | The default configuration
of log4j-console.properties.
|
[...]
| defaultConfiguration.log4j-operator.properties | The default configuration
of log4j-operator.properties.
|
[...]
+| defaultConfiguration.logback-operator.xml | The default configuration
of logback-operator.xml. Used when `logging.framework` is set to `logback`.
|
[...]
+| defaultConfiguration.logback-console.xml | The default configuration
of logback-console.xml. Used when `logging.framework` is set to `logback`.
|
[...]
| fullnameOverride | Overrides the fullname with
the specified full name.
|
[...]
| image.digest | The image tag of
flink-kubernetes-operator. If set then it takes precedence and the image tag
will be ignored. |
[...]
| image.pullPolicy | The image pull policy of
flink-kubernetes-operator.
| IfNotPresent
[...]
@@ -87,6 +89,7 @@ The configurable parameters of the Helm chart and which
default values as detail
| jobServiceAccount.name | The name of job service
account.
| flink
[...]
| jvmArgs.operator | The JVM start up options
for operator.
|
[...]
| jvmArgs.webhook | The JVM start up options
for webhook.
|
[...]
+| logging.framework | The logging framework to
use for the operator. Supported values: `log4j2`, `logback`. Controls which
config files are mounted and which JVM flag is passed. | log4j2
[...]
| metrics.port | The metrics port on the
container for default configuration.
|
[...]
| nameOverride | Overrides the name with the
specified name.
|
[...]
| operatorHealth.livenessProbe | Liveness probe
configuration for the operator using the health endpoint. Only time settings
should be configured, endpoint is set automatically based on port. |
[...]
diff --git a/docs/content.zh/docs/operations/metrics-logging.md
b/docs/content.zh/docs/operations/metrics-logging.md
index d1a585dd..af7afb6d 100644
--- a/docs/content.zh/docs/operations/metrics-logging.md
+++ b/docs/content.zh/docs/operations/metrics-logging.md
@@ -616,7 +616,33 @@ Once the custom resource is created in the Kubernetes
environment the operator m
## Logging
The Operator controls the logging behaviour for Flink applications and the
Operator itself using configuration files mounted externally via ConfigMaps.
[Configuration
files](https://github.com/apache/flink-kubernetes-operator/tree/main/helm/flink-kubernetes-operator/conf)
with default values are shipped in the Helm chart. It is recommended to review
and adjust them if needed in the `values.yaml` file before deploying the
Operator in production environments.
-To append/override the default log configuration properties for the operator
and Flink deployments define the `log4j-operator.properties` and
`log4j-console.properties` keys respectively:
+The operator supports two logging frameworks: **Log4j2** (default) and
**Logback**. Both sets of configuration files are shipped in the Helm chart and
Docker image. The active framework is selected at install time via the
`logging.framework` Helm value, either in `values.yaml` or on the command line:
+
+```shell
+# Use the default Log4j2 framework (explicit, same as omitting the flag)
+helm install flink-operator helm/flink-kubernetes-operator --set
logging.framework=log4j2
+
+# Switch to Logback
+helm install flink-operator helm/flink-kubernetes-operator --set
logging.framework=logback
+```
+
+This controls three things:
+1. Which logging configuration files are mounted into `/opt/flink/conf/`
+2. Which JVM system property is passed to select the framework
(`log4j.configurationFile` or `logback.configurationFile`)
+3. Which SLF4J binding JAR is placed on the classpath at runtime
+
+{{< hint info >}}
+Logging in the operator is intentionally succinct and does not include
contextual information such as namespace or name of the FlinkDeployment objects.
+We rely on the MDC provided by the operator-sdk to access this information and
use it directly in the log layout.
+
+See the [Java Operator SDK
docs](https://javaoperatorsdk.io/docs/features#contextual-info-for-logging-with-mdc)
for more detail.
+{{< /hint >}}
+
+To learn more about accessing the job logs or changing the log level
dynamically check the corresponding
[section](https://nightlies.apache.org/flink/flink-docs-master/docs/deployment/resource-providers/native_kubernetes/#logging)
of the core documentation.
+
+### Log4j2
+
+Log4j2 is the default logging framework. To append/override the default log
configuration properties for the operator and Flink deployments define the
`log4j-operator.properties` and `log4j-console.properties` keys respectively:
```yaml
defaultConfiguration:
@@ -634,18 +660,60 @@ defaultConfiguration:
# monitorInterval = 30
```
-{{< hint info >}}
-Logging in the operator is intentionally succinct and does not include
contextual information such as namespace or name of the FlinkDeployment objects.
-We rely on the MDC provided by the operator-sdk to access this information and
use it directly in the log layout.
+### Logback
-See the [Java Operator SDK
docs](https://javaoperatorsdk.io/docs/features#contextual-info-for-logging-with-mdc)
for more detail.
+When using Logback, the equivalent configuration keys are
`logback-operator.xml` and `logback-console.xml`:
+
+```yaml
+logging:
+ framework: logback
+
+defaultConfiguration:
+ create: true
+ append: true
+ logback-operator.xml: |+
+ <!-- Flink Operator Logback Overrides -->
+ <!-- <configuration>
+ <appender name="ConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-60logger{26} %X
- %msg%n</pattern>
+ </encoder>
+ </appender>
+ <root level="DEBUG">
+ <appender-ref ref="ConsoleAppender" />
+ </root>
+ </configuration> -->
+ logback-console.xml: |+
+ <!-- Flink Deployment Logback Overrides -->
+ <!-- <configuration>
+ <appender name="ConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-60logger{26} %X
- %msg%n</pattern>
+ </encoder>
+ </appender>
+ <root level="DEBUG">
+ <appender-ref ref="ConsoleAppender" />
+ </root>
+ </configuration> -->
+```
+
+{{< hint warning >}}
+**Logback XML overrides replace the entire default configuration.** Unlike
Log4j2 `.properties` files where user entries are appended, XML files cannot be
concatenated (two `<configuration>` root elements produce invalid XML). Ensure
your custom `logback-operator.xml` or `logback-console.xml` is complete and
self-contained. Logback supports only XML-based configuration and does not
provide native support for `.properties` configuration files.
{{< /hint >}}
-To learn more about accessing the job logs or changing the log level
dynamically check the corresponding
[section](https://nightlies.apache.org/flink/flink-docs-master/docs/deployment/resource-providers/native_kubernetes/#logging)
of the core documentation.
+### Logging Library Version Overrides
+
+The operator ships with **Logback 1.2.x** and **SLF4J 1.7.x**. These versions
are bundled in the Docker image and the SLF4J 1.7.x API is shaded into the
operator JAR.
+
+{{< hint warning >}}
+**Upgrading to Logback 1.4+/1.5+ or SLF4J 2.x is not supported.** SLF4J 2.x
uses a `ServiceLoader`-based binding mechanism that is incompatible with the
SLF4J 1.7.x API shaded inside the operator. Replacing the JARs at runtime will
result in `ClassNotFoundException: org.slf4j.impl.StaticLoggerBinder`.
+{{< /hint >}}
### FlinkDeployment Logging Configuration
-Users have the freedom to override the default `log4j-console.properties`
settings on a per-deployment level by putting the entire log configuration into
`spec.logConfiguration`:
+Users have the freedom to override the default logging settings on a
per-deployment level by putting the entire log configuration into
`spec.logConfiguration`.
+
+For Log4j2:
```yaml
spec:
@@ -656,3 +724,22 @@ spec:
rootLogger.appenderRef.file.ref = LogFile
...
```
+
+For Logback:
+
+```yaml
+spec:
+ ...
+ logConfiguration:
+ logback-console.xml: |+
+ <configuration>
+ <appender name="ConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss,SSS} %-5level %-60.60logger{60} %X
- %msg%n</pattern>
+ </encoder>
+ </appender>
+ <root level="DEBUG">
+ <appender-ref ref="ConsoleAppender" />
+ </root>
+ </configuration>
+```
diff --git a/docs/content/docs/operations/helm.md
b/docs/content/docs/operations/helm.md
index 84e14828..64d98f2c 100644
--- a/docs/content/docs/operations/helm.md
+++ b/docs/content/docs/operations/helm.md
@@ -76,6 +76,8 @@ The configurable parameters of the Helm chart and which
default values as detail
| defaultConfiguration.flink-conf.yaml | The default configuration
of flink-conf.yaml.
|
kubernetes.operator.metrics.reporter.slf4j.factory.class:
org.apache.flink.metrics.slf4j.Slf4jReporterFactory<br/>kubernetes.operator.metrics.reporter.slf4j.interval:
5 MINUTE<br/>kubernetes.operator.reconcile.interval: 15
s<br/>kubernetes.operator.observer.progress-check.interva [...]
| defaultConfiguration.log4j-console.properties | The default configuration
of log4j-console.properties.
|
[...]
| defaultConfiguration.log4j-operator.properties | The default configuration
of log4j-operator.properties.
|
[...]
+| defaultConfiguration.logback-operator.xml | The default configuration
of logback-operator.xml. Used when `logging.framework` is set to `logback`.
|
[...]
+| defaultConfiguration.logback-console.xml | The default configuration
of logback-console.xml. Used when `logging.framework` is set to `logback`.
|
[...]
| fullnameOverride | Overrides the fullname with
the specified full name.
|
[...]
| image.digest | The image tag of
flink-kubernetes-operator. If set then it takes precedence and the image tag
will be ignored. |
[...]
| image.pullPolicy | The image pull policy of
flink-kubernetes-operator.
| IfNotPresent
[...]
@@ -87,6 +89,7 @@ The configurable parameters of the Helm chart and which
default values as detail
| jobServiceAccount.name | The name of job service
account.
| flink
[...]
| jvmArgs.operator | The JVM start up options
for operator.
|
[...]
| jvmArgs.webhook | The JVM start up options
for webhook.
|
[...]
+| logging.framework | The logging framework to
use for the operator. Supported values: `log4j2`, `logback`. Controls which
config files are mounted and which JVM flag is passed. | log4j2
[...]
| metrics.port | The metrics port on the
container for default configuration.
|
[...]
| nameOverride | Overrides the name with the
specified name.
|
[...]
| operatorHealth.livenessProbe | Liveness probe
configuration for the operator using the health endpoint. Only time settings
should be configured, endpoint is set automatically based on port. |
[...]
diff --git a/docs/content/docs/operations/metrics-logging.md
b/docs/content/docs/operations/metrics-logging.md
index dfe08ab6..f4451d72 100644
--- a/docs/content/docs/operations/metrics-logging.md
+++ b/docs/content/docs/operations/metrics-logging.md
@@ -616,7 +616,33 @@ Once the custom resource is created in the Kubernetes
environment the operator m
## Logging
The Operator controls the logging behaviour for Flink applications and the
Operator itself using configuration files mounted externally via ConfigMaps.
[Configuration
files](https://github.com/apache/flink-kubernetes-operator/tree/main/helm/flink-kubernetes-operator/conf)
with default values are shipped in the Helm chart. It is recommended to review
and adjust them if needed in the `values.yaml` file before deploying the
Operator in production environments.
-To append/override the default log configuration properties for the operator
and Flink deployments define the `log4j-operator.properties` and
`log4j-console.properties` keys respectively:
+The operator supports two logging frameworks: **Log4j2** (default) and
**Logback**. Both sets of configuration files are shipped in the Helm chart and
Docker image. The active framework is selected at install time via the
`logging.framework` Helm value, either in `values.yaml` or on the command line:
+
+```shell
+# Use the default Log4j2 framework (explicit, same as omitting the flag)
+helm install flink-operator helm/flink-kubernetes-operator --set
logging.framework=log4j2
+
+# Switch to Logback
+helm install flink-operator helm/flink-kubernetes-operator --set
logging.framework=logback
+```
+
+This controls three things:
+1. Which logging configuration files are mounted into `/opt/flink/conf/`
+2. Which JVM system property is passed to select the framework
(`log4j.configurationFile` or `logback.configurationFile`)
+3. Which SLF4J binding JAR is placed on the classpath at runtime
+
+{{< hint info >}}
+Logging in the operator is intentionally succinct and does not include
contextual information such as namespace or name of the FlinkDeployment objects.
+We rely on the MDC provided by the operator-sdk to access this information and
use it directly in the log layout.
+
+See the [Java Operator SDK
docs](https://javaoperatorsdk.io/docs/features#contextual-info-for-logging-with-mdc)
for more detail.
+{{< /hint >}}
+
+To learn more about accessing the job logs or changing the log level
dynamically check the corresponding
[section](https://nightlies.apache.org/flink/flink-docs-master/docs/deployment/resource-providers/native_kubernetes/#logging)
of the core documentation.
+
+### Log4j2
+
+Log4j2 is the default logging framework. To append/override the default log
configuration properties for the operator and Flink deployments define the
`log4j-operator.properties` and `log4j-console.properties` keys respectively:
```yaml
defaultConfiguration:
@@ -634,18 +660,60 @@ defaultConfiguration:
# monitorInterval = 30
```
-{{< hint info >}}
-Logging in the operator is intentionally succinct and does not include
contextual information such as namespace or name of the FlinkDeployment objects.
-We rely on the MDC provided by the operator-sdk to access this information and
use it directly in the log layout.
+### Logback
-See the [Java Operator SDK
docs](https://javaoperatorsdk.io/docs/features#contextual-info-for-logging-with-mdc)
for more detail.
+When using Logback, the equivalent configuration keys are
`logback-operator.xml` and `logback-console.xml`:
+
+```yaml
+logging:
+ framework: logback
+
+defaultConfiguration:
+ create: true
+ append: true
+ logback-operator.xml: |+
+ <!-- Flink Operator Logback Overrides -->
+ <!-- <configuration>
+ <appender name="ConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-60logger{26} %X
- %msg%n</pattern>
+ </encoder>
+ </appender>
+ <root level="DEBUG">
+ <appender-ref ref="ConsoleAppender" />
+ </root>
+ </configuration> -->
+ logback-console.xml: |+
+ <!-- Flink Deployment Logback Overrides -->
+ <!-- <configuration>
+ <appender name="ConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-60logger{26} %X
- %msg%n</pattern>
+ </encoder>
+ </appender>
+ <root level="DEBUG">
+ <appender-ref ref="ConsoleAppender" />
+ </root>
+ </configuration> -->
+```
+
+{{< hint warning >}}
+**Logback XML overrides replace the entire default configuration.** Unlike
Log4j2 `.properties` files where user entries are appended, XML files cannot be
concatenated (two `<configuration>` root elements produce invalid XML). Ensure
your custom `logback-operator.xml` or `logback-console.xml` is complete and
self-contained. Logback supports only XML-based configuration and does not
provide native support for `.properties` configuration files.
{{< /hint >}}
-To learn more about accessing the job logs or changing the log level
dynamically check the corresponding
[section](https://nightlies.apache.org/flink/flink-docs-master/docs/deployment/resource-providers/native_kubernetes/#logging)
of the core documentation.
+### Logging Library Version Overrides
+
+The operator ships with **Logback 1.2.x** and **SLF4J 1.7.x**. These versions
are bundled in the Docker image and the SLF4J 1.7.x API is shaded into the
operator JAR.
+
+{{< hint warning >}}
+**Upgrading to Logback 1.4+/1.5+ or SLF4J 2.x is not supported.** SLF4J 2.x
uses a `ServiceLoader`-based binding mechanism that is incompatible with the
SLF4J 1.7.x API shaded inside the operator. Replacing the JARs at runtime will
result in `ClassNotFoundException: org.slf4j.impl.StaticLoggerBinder`.
+{{< /hint >}}
### FlinkDeployment Logging Configuration
-Users have the freedom to override the default `log4j-console.properties`
settings on a per-deployment level by putting the entire log configuration into
`spec.logConfiguration`:
+Users have the freedom to override the default logging settings on a
per-deployment level by putting the entire log configuration into
`spec.logConfiguration`.
+
+For Log4j2:
```yaml
spec:
@@ -656,3 +724,22 @@ spec:
rootLogger.appenderRef.file.ref = LogFile
...
```
+
+For Logback:
+
+```yaml
+spec:
+ ...
+ logConfiguration:
+ logback-console.xml: |+
+ <configuration>
+ <appender name="ConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss,SSS} %-5level %-60.60logger{60} %X
- %msg%n</pattern>
+ </encoder>
+ </appender>
+ <root level="DEBUG">
+ <appender-ref ref="ConsoleAppender" />
+ </root>
+ </configuration>
+```
diff --git a/e2e-tests/test_logback_logging.sh
b/e2e-tests/test_logback_logging.sh
new file mode 100755
index 00000000..a557b26d
--- /dev/null
+++ b/e2e-tests/test_logback_logging.sh
@@ -0,0 +1,116 @@
+#!/usr/bin/env bash
+################################################################################
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+################################################################################
+
+# This script tests that the operator starts correctly with the logback logging
+# framework. The operator must be installed with logging.framework=logback
+# before running this test, e.g.:
+# helm install flink-operator helm/flink-kubernetes-operator --set
logging.framework=logback
+#
+# Verifications:
+# 1. Operator pod is running
+# 2. No SLF4J multiple-bindings warnings in logs
+# 3. Operator produces log output (proves logback is active)
+# 4. LOGGING_FRAMEWORK env var is set to "logback"
+# 5. LOG_CONFIG env var points to logback config
+# 6. logback-operator.xml is mounted in the container
+# 7. log4j-operator.properties is NOT mounted
+
+SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
+source "${SCRIPT_DIR}/utils.sh"
+
+TIMEOUT=120
+
+passed=true
+
+operator_namespace=$(get_operator_pod_namespace)
+operator_pod=$(get_operator_pod_name)
+echo "Current operator pod is ${operator_pod} in namespace
${operator_namespace}"
+
+# Check that there are no SLF4J multiple-bindings warnings
+echo "Checking for SLF4J multiple bindings warnings..."
+if kubectl logs "${operator_pod}" -c flink-kubernetes-operator -n
"${operator_namespace}" | grep -q "SLF4J: Class path contains multiple SLF4J
bindings"; then
+ echo "ERROR: Found SLF4J multiple bindings warning"
+ passed=false
+fi
+
+# Verify logback is actually producing output
+echo "Checking that operator produces log output..."
+log_lines=$(kubectl logs "${operator_pod}" -c flink-kubernetes-operator -n
"${operator_namespace}" | wc -l)
+if [ "$log_lines" -lt 1 ]; then
+ echo "ERROR: No log output from operator"
+ passed=false
+fi
+
+# Verify log format
+echo "Verifying log format..."
+sample_log=$(kubectl logs "${operator_pod}" -c flink-kubernetes-operator -n
"${operator_namespace}" | grep "Starting Flink Kubernetes Operator" | head -1 |
sed 's/\x1b\[[0-9;]*m//g')
+
+# Check timestamp includes milliseconds (dot followed by 3 digits for logback)
+if ! echo "$sample_log" | grep -qE '^[0-9]{4}-[0-9]{2}-[0-9]{2}
[0-9]{2}:[0-9]{2}:[0-9]{2}[.][0-9]{3} '; then
+ echo "ERROR: Timestamp missing milliseconds. Sample: ${sample_log}"
+ passed=false
+fi
+
+if ! echo "$sample_log" | grep -qE 'o\.a\.f\.k\.o\.[A-Za-z]+'; then
+ echo "ERROR: Logger name not abbreviated. Sample: ${sample_log}"
+ passed=false
+fi
+
+if ! echo "$sample_log" | grep -qE '\[INFO \]'; then
+ echo "ERROR: Level format does not match [INFO ]. Sample: ${sample_log}"
+ passed=false
+fi
+
+# Verify LOGGING_FRAMEWORK env var
+echo "Verifying LOGGING_FRAMEWORK env var..."
+framework=$(kubectl get pod "${operator_pod}" -n "${operator_namespace}" -o
jsonpath='{.spec.containers[0].env[?(@.name=="LOGGING_FRAMEWORK")].value}')
+if [ "$framework" != "logback" ]; then
+ echo "ERROR: LOGGING_FRAMEWORK is '${framework}', expected 'logback'"
+ passed=false
+fi
+
+# Verify LOG_CONFIG env var
+echo "Verifying LOG_CONFIG env var..."
+log_config=$(kubectl get pod "${operator_pod}" -n "${operator_namespace}" -o
jsonpath='{.spec.containers[0].env[?(@.name=="LOG_CONFIG")].value}')
+if [[ "$log_config" != *"logback"* ]]; then
+ echo "ERROR: LOG_CONFIG is '${log_config}', expected it to contain 'logback'"
+ passed=false
+fi
+
+# Verify logback config file is mounted
+echo "Verifying logback-operator.xml is mounted..."
+if ! kubectl exec "${operator_pod}" -c flink-kubernetes-operator -n
"${operator_namespace}" -- test -f /opt/flink/conf/logback-operator.xml; then
+ echo "ERROR: logback-operator.xml not found in container"
+ passed=false
+fi
+
+# Verify log4j config files are NOT mounted
+echo "Verifying log4j-operator.properties is NOT mounted..."
+if kubectl exec "${operator_pod}" -c flink-kubernetes-operator -n
"${operator_namespace}" -- test -f /opt/flink/conf/log4j-operator.properties
2>/dev/null; then
+ echo "ERROR: log4j-operator.properties should not be mounted when using
logback"
+ passed=false
+fi
+
+if [ "$passed" = true ]; then
+ echo "Successfully run the logback logging framework test"
+else
+ echo "Logback logging framework test failed"
+ exit 1
+fi
+
diff --git a/flink-kubernetes-operator/pom.xml
b/flink-kubernetes-operator/pom.xml
index f4881275..6669472f 100644
--- a/flink-kubernetes-operator/pom.xml
+++ b/flink-kubernetes-operator/pom.xml
@@ -122,7 +122,9 @@ under the License.
<scope>provided</scope>
</dependency>
- <!-- Logging -->
+ <!-- Logging: SLF4J API and Log4j2 implementation are shaded into the
operator JAR.
+ Only the SLF4J bindings (log4j-slf4j-impl / logback-classic) are
excluded
+ and shipped as separate JARs so the entrypoint can pick one. -->
<dependency>
<groupId>org.slf4j</groupId>
@@ -134,6 +136,7 @@ under the License.
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
+ <scope>provided</scope>
</dependency>
<dependency>
@@ -154,6 +157,14 @@ under the License.
<version>${log4j.version}</version>
</dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>${logback.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
+
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
@@ -311,6 +322,27 @@ under the License.
</executions>
<configuration>
<artifactItems>
+ <!-- Log4j2 SLF4J binding (default logging framework)
-->
+ <artifactItem>
+ <groupId>org.apache.logging.log4j</groupId>
+ <artifactId>log4j-slf4j-impl</artifactId>
+ <version>${log4j.version}</version>
+
<outputDirectory>${project.build.directory}/log4j</outputDirectory>
+ </artifactItem>
+ <!-- Logback SLF4J binding (alternative logging
framework) -->
+ <artifactItem>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>${logback.version}</version>
+
<outputDirectory>${project.build.directory}/logback</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>${logback.version}</version>
+
<outputDirectory>${project.build.directory}/logback</outputDirectory>
+ </artifactItem>
+ <!-- Metrics plugins -->
<artifactItem>
<groupId>org.apache.flink</groupId>
<artifactId>flink-metrics-datadog</artifactId>
diff --git a/helm/flink-kubernetes-operator/conf/logback-console.xml
b/helm/flink-kubernetes-operator/conf/logback-console.xml
new file mode 100644
index 00000000..3e29f8a8
--- /dev/null
+++ b/helm/flink-kubernetes-operator/conf/logback-console.xml
@@ -0,0 +1,56 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<configuration>
+ <appender name="ConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss,SSS} %-5level %-60.60logger{60} %X
- %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <appender name="RollingFileAppender"
class="ch.qos.logback.core.rolling.RollingFileAppender">
+ <file>${log.file}</file>
+ <append>false</append>
+ <encoder>
+ <pattern>%d{yyyy-MM-dd HH:mm:ss,SSS} %-5level %-60.60logger{60} %X
- %msg%n</pattern>
+ </encoder>
+ <rollingPolicy
class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
+ <fileNamePattern>${log.file}.%i</fileNamePattern>
+ <minIndex>1</minIndex>
+ <maxIndex>10</maxIndex>
+ </rollingPolicy>
+ <triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
+ <maxFileSize>100MB</maxFileSize>
+ </triggeringPolicy>
+ </appender>
+
+ <!-- The following loggers keep the log level of common
libraries/connectors on INFO -->
+ <logger name="akka" level="INFO" />
+ <logger name="org.apache.kafka" level="INFO" />
+ <logger name="org.apache.hadoop" level="INFO" />
+ <logger name="org.apache.zookeeper" level="INFO" />
+
+ <!-- Suppress the irrelevant (wrong) warnings from the Netty channel
handler -->
+ <logger
name="org.apache.flink.shaded.akka.org.jboss.netty.channel.DefaultChannelPipeline"
level="OFF" />
+
+ <root level="INFO">
+ <appender-ref ref="ConsoleAppender" />
+ <appender-ref ref="RollingFileAppender" />
+ </root>
+</configuration>
+
diff --git a/helm/flink-kubernetes-operator/conf/logback-operator.xml
b/helm/flink-kubernetes-operator/conf/logback-operator.xml
new file mode 100644
index 00000000..3e1e62b2
--- /dev/null
+++ b/helm/flink-kubernetes-operator/conf/logback-operator.xml
@@ -0,0 +1,35 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<configuration>
+ <appender name="ConsoleAppender"
class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>%yellow(%d{yyyy-MM-dd HH:mm:ss.SSS})
%cyan(%-30logger{26})
%highlight([%-5level][%X{resource.namespace}/%X{resource.name}])
%msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <!-- Do not log config loading -->
+ <logger name="org.apache.flink.configuration.GlobalConfiguration"
level="ERROR" />
+
+ <!-- Avoid logging fallback key INFO messages -->
+ <logger name="org.apache.flink.configuration.Configuration" level="ERROR"
/>
+
+ <root level="INFO">
+ <appender-ref ref="ConsoleAppender" />
+ </root>
+</configuration>
diff --git a/helm/flink-kubernetes-operator/templates/_helpers.tpl
b/helm/flink-kubernetes-operator/templates/_helpers.tpl
index f4673d1e..e28eae05 100644
--- a/helm/flink-kubernetes-operator/templates/_helpers.tpl
+++ b/helm/flink-kubernetes-operator/templates/_helpers.tpl
@@ -77,3 +77,15 @@ Create the path of the operator image to use
{{- .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag
}}
{{- end }}
{{- end }}
+
+{{/*
+Determine the JVM log configuration flag based on the selected logging
framework.
+Supported values for .Values.logging.framework: "log4j2" (default), "logback".
+*/}}
+{{- define "flink-operator.logConfig" -}}
+{{- if eq .Values.logging.framework "logback" -}}
+-Dlogback.configurationFile=/opt/flink/conf/logback-operator.xml
+{{- else -}}
+-Dlog4j.configurationFile=/opt/flink/conf/log4j-operator.properties
+{{- end -}}
+{{- end }}
diff --git a/helm/flink-kubernetes-operator/templates/controller/configmap.yaml
b/helm/flink-kubernetes-operator/templates/controller/configmap.yaml
index d8695481..5ab29cd8 100644
--- a/helm/flink-kubernetes-operator/templates/controller/configmap.yaml
+++ b/helm/flink-kubernetes-operator/templates/controller/configmap.yaml
@@ -64,5 +64,17 @@ data:
{{- end }}
{{- if index (.Values.defaultConfiguration) "log4j-console.properties" }}
{{- index (.Values.defaultConfiguration) "log4j-console.properties" |
nindent 4 -}}
+{{- end }}
+ logback-operator.xml: |+
+{{- if index (.Values.defaultConfiguration) "logback-operator.xml" }}
+ {{- index (.Values.defaultConfiguration) "logback-operator.xml" | nindent 4
-}}
+{{- else if .Values.defaultConfiguration.append }}
+ {{- $.Files.Get "conf/logback-operator.xml" | nindent 4 -}}
+{{- end }}
+ logback-console.xml: |+
+{{- if index (.Values.defaultConfiguration) "logback-console.xml" }}
+ {{- index (.Values.defaultConfiguration) "logback-console.xml" | nindent 4
-}}
+{{- else if .Values.defaultConfiguration.append }}
+ {{- $.Files.Get "conf/logback-console.xml" | nindent 4 -}}
{{- end }}
{{- end }}
diff --git
a/helm/flink-kubernetes-operator/templates/controller/deployment.yaml
b/helm/flink-kubernetes-operator/templates/controller/deployment.yaml
index 85e6a465..a31175c9 100644
--- a/helm/flink-kubernetes-operator/templates/controller/deployment.yaml
+++ b/helm/flink-kubernetes-operator/templates/controller/deployment.yaml
@@ -116,8 +116,10 @@ spec:
value: /opt/flink/conf
- name: FLINK_PLUGINS_DIR
value: /opt/flink/plugins
+ - name: LOGGING_FRAMEWORK
+ value: {{ .Values.logging.framework }}
- name: LOG_CONFIG
- value: {{ .Values.jvmArgs.logConfig }}
+ value: {{ include "flink-operator.logConfig" . }}
- name: JVM_ARGS
value: {{ .Values.jvmArgs.operator }}
{{- if .Values.tls.create }}
@@ -198,13 +200,15 @@ spec:
- name: WEBHOOK_SERVER_PORT
value: "9443"
- name: LOG_CONFIG
- value: {{ .Values.jvmArgs.logConfig }}
+ value: {{ include "flink-operator.logConfig" . }}
- name: JVM_ARGS
value: {{ .Values.jvmArgs.webhook }}
- name: FLINK_CONF_DIR
value: /opt/flink/conf
- name: FLINK_PLUGINS_DIR
value: /opt/flink/plugins
+ - name: LOGGING_FRAMEWORK
+ value: {{ .Values.logging.framework }}
- name: OPERATOR_NAMESPACE
valueFrom:
fieldRef:
@@ -247,10 +251,17 @@ spec:
- key: config.yaml
path: config.yaml
{{- end }}
+ {{- if eq .Values.logging.framework "logback" }}
+ - key: logback-operator.xml
+ path: logback-operator.xml
+ - key: logback-console.xml
+ path: logback-console.xml
+ {{- else }}
- key: log4j-operator.properties
path: log4j-operator.properties
- key: log4j-console.properties
path: log4j-console.properties
+ {{- end }}
{{- if .Values.operatorVolumes.create }}
{{- toYaml .Values.operatorVolumes.data | nindent 8 }}
{{- else }}
diff --git
a/helm/flink-kubernetes-operator/tests/controller/configmap_test.yaml
b/helm/flink-kubernetes-operator/tests/controller/configmap_test.yaml
index b9dc1277..90476695 100644
--- a/helm/flink-kubernetes-operator/tests/controller/configmap_test.yaml
+++ b/helm/flink-kubernetes-operator/tests/controller/configmap_test.yaml
@@ -36,3 +36,35 @@ tests:
kind: ConfigMap
name: flink-operator-config
namespace: flink-operator
+
+ - it: Should include logback-operator.xml key in configmap
+ set:
+ defaultConfiguration:
+ create: true
+ asserts:
+ - isNotNull:
+ path: data["logback-operator.xml"]
+
+ - it: Should include logback-console.xml key in configmap
+ set:
+ defaultConfiguration:
+ create: true
+ asserts:
+ - isNotNull:
+ path: data["logback-console.xml"]
+
+ - it: Should include log4j-operator.properties key in configmap
+ set:
+ defaultConfiguration:
+ create: true
+ asserts:
+ - isNotNull:
+ path: data["log4j-operator.properties"]
+
+ - it: Should include log4j-console.properties key in configmap
+ set:
+ defaultConfiguration:
+ create: true
+ asserts:
+ - isNotNull:
+ path: data["log4j-console.properties"]
diff --git
a/helm/flink-kubernetes-operator/tests/controller/deployment_test.yaml
b/helm/flink-kubernetes-operator/tests/controller/deployment_test.yaml
index 8769721e..e6333d76 100644
--- a/helm/flink-kubernetes-operator/tests/controller/deployment_test.yaml
+++ b/helm/flink-kubernetes-operator/tests/controller/deployment_test.yaml
@@ -303,3 +303,83 @@ tests:
- equal:
path: spec.template.spec.serviceAccountName
value: test-service-account
+
+# Logging framework tests
+
+- it: Should use log4j2 logging by default
+ asserts:
+ - contains:
+ path: spec.template.spec.containers[0].env
+ content:
+ name: LOGGING_FRAMEWORK
+ value: log4j2
+ - contains:
+ path: spec.template.spec.containers[0].env
+ content:
+ name: LOG_CONFIG
+ value:
-Dlog4j.configurationFile=/opt/flink/conf/log4j-operator.properties
+
+- it: Should mount log4j config files by default
+ asserts:
+ - contains:
+ path: spec.template.spec.volumes[0].configMap.items
+ content:
+ key: log4j-operator.properties
+ path: log4j-operator.properties
+ - contains:
+ path: spec.template.spec.volumes[0].configMap.items
+ content:
+ key: log4j-console.properties
+ path: log4j-console.properties
+
+- it: Should use logback logging when `logging.framework` is set to logback
+ set:
+ logging:
+ framework: logback
+ asserts:
+ - contains:
+ path: spec.template.spec.containers[0].env
+ content:
+ name: LOGGING_FRAMEWORK
+ value: logback
+ - contains:
+ path: spec.template.spec.containers[0].env
+ content:
+ name: LOG_CONFIG
+ value: -Dlogback.configurationFile=/opt/flink/conf/logback-operator.xml
+
+- it: Should mount logback config files when `logging.framework` is set to
logback
+ set:
+ logging:
+ framework: logback
+ asserts:
+ - contains:
+ path: spec.template.spec.volumes[0].configMap.items
+ content:
+ key: logback-operator.xml
+ path: logback-operator.xml
+ - contains:
+ path: spec.template.spec.volumes[0].configMap.items
+ content:
+ key: logback-console.xml
+ path: logback-console.xml
+
+- it: Should not mount logback config files when using log4j2
+ asserts:
+ - notContains:
+ path: spec.template.spec.volumes[0].configMap.items
+ content:
+ key: logback-operator.xml
+ path: logback-operator.xml
+
+- it: Should not mount log4j config files when using logback
+ set:
+ logging:
+ framework: logback
+ asserts:
+ - notContains:
+ path: spec.template.spec.volumes[0].configMap.items
+ content:
+ key: log4j-operator.properties
+ path: log4j-operator.properties
+
diff --git a/helm/flink-kubernetes-operator/values.yaml
b/helm/flink-kubernetes-operator/values.yaml
index 497bcc71..c9242f2b 100644
--- a/helm/flink-kubernetes-operator/values.yaml
+++ b/helm/flink-kubernetes-operator/values.yaml
@@ -194,6 +194,13 @@ defaultConfiguration:
log4j-console.properties: |+
# Flink Deployment Logging Overrides
# rootLogger.level = DEBUG
+ logback-operator.xml:
+ logback-console.xml:
+
+# Logging framework to use for the operator. Supported values: "log4j2",
"logback".
+# This controls which config file is passed to the JVM and which files are
mounted.
+logging:
+ framework: "log4j2"
# (Optional) Exposes metrics port on the container if defined
metrics:
@@ -206,7 +213,6 @@ fullnameOverride: ""
jvmArgs:
webhook: ""
operator: ""
- logConfig:
"-Dlog4j.configurationFile=/opt/flink/conf/log4j-operator.properties"
# Configure health probes for the operator
operatorHealth:
diff --git a/pom.xml b/pom.xml
index f3a0a8bc..693124a2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,6 +88,7 @@ under the License.
<slf4j.version>1.7.36</slf4j.version>
<log4j.version>2.23.1</log4j.version>
+ <logback.version>1.2.13</logback.version>
<spotless.version>2.40.0</spotless.version>
<it.skip>true</it.skip>