Skip to content

Commit a227995

Browse files
fix: Allow overriding the logging configuration in Airflow 3.1 (#757)
* fix: Allow overriding the logging configuration in Airflow 3.1 * chore: Update changelog * chore: Run pre-commit * chore: Fix changelog * test(logging): Fix custom logging config * test(smoke): Remove the generation assertions * test(resources): Fix OOMKilled events * test: Do not use the sandbox image anymore
1 parent 4cfc08d commit a227995

9 files changed

Lines changed: 134 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
### Added
66

7-
- Add support for airflow 3.1.6 ([#742]).
7+
- Add support for airflow 3.1.6 ([#742], [#757]).
88
- Add operator versioning ([#725]).
99
- GitSync considered for v1alpha1 and v1alpha2
1010
- Support objectOverrides using `.spec.objectOverrides`.
@@ -32,6 +32,7 @@
3232
[#742]: https://github.com/stackabletech/airflow-operator/pull/742
3333
[#752]: https://github.com/stackabletech/airflow-operator/pull/752
3434
[#756]: https://github.com/stackabletech/airflow-operator/pull/756
35+
[#757]: https://github.com/stackabletech/airflow-operator/pull/757
3536
[#759]: https://github.com/stackabletech/airflow-operator/pull/759
3637

3738
## [25.11.0] - 2025-11-07

rust/operator-binary/src/product_logging.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ fn create_airflow_config(
8484
log_config: &AutomaticContainerLogConfig,
8585
log_dir: &str,
8686
resolved_product_image: &ResolvedProductImage,
87+
) -> String {
88+
if resolved_product_image.product_version.starts_with("2.")
89+
|| resolved_product_image.product_version.starts_with("3.0.")
90+
{
91+
create_airflow_stdlib_config(log_config, log_dir, resolved_product_image)
92+
} else {
93+
create_airflow_structlog_config(log_config, log_dir)
94+
}
95+
}
96+
97+
fn create_airflow_stdlib_config(
98+
log_config: &AutomaticContainerLogConfig,
99+
log_dir: &str,
100+
resolved_product_image: &ResolvedProductImage,
87101
) -> String {
88102
let loggers_config = log_config
89103
.loggers
@@ -176,3 +190,99 @@ LOGGING_CONFIG['root'] = {{
176190
.to_python_expression(),
177191
)
178192
}
193+
194+
fn create_airflow_structlog_config(
195+
log_config: &AutomaticContainerLogConfig,
196+
log_dir: &str,
197+
) -> String {
198+
let loggers_config = log_config
199+
.loggers
200+
.iter()
201+
.filter(|(name, _)| name.as_str() != AutomaticContainerLogConfig::ROOT_LOGGER)
202+
.fold(String::new(), |mut output, (name, config)| {
203+
let _ = writeln!(
204+
output,
205+
"
206+
LOGGING_CONFIG['loggers'].setdefault('{name}', {{ 'propagate': True }})
207+
LOGGING_CONFIG['loggers']['{name}']['level'] = {level}
208+
",
209+
level = config.level.to_python_expression()
210+
);
211+
output
212+
});
213+
214+
format!(
215+
"\
216+
import logging
217+
import os
218+
from airflow.config_templates import airflow_local_settings
219+
220+
os.makedirs('{log_dir}', exist_ok=True)
221+
222+
LOGGING_CONFIG = {{
223+
'filters': {{
224+
'mask_secrets_core': {{
225+
'()': 'airflow._shared.secrets_masker._secrets_masker',
226+
}}
227+
}},
228+
'formatters': {{
229+
'airflow': {{
230+
'format': '%(asctime)s logLevel=%(levelname)s logger=%(name)s - %(message)s',
231+
'class': 'airflow.utils.log.timezone_aware.TimezoneAware',
232+
}},
233+
'json': {{
234+
'()': 'airflow.utils.log.json_formatter.JSONFormatter',
235+
'json_fields': ['asctime', 'levelname', 'message', 'name']
236+
}}
237+
}},
238+
'handlers': {{
239+
'default': {{
240+
'level': {console_log_level}
241+
}},
242+
'file': {{
243+
'class': 'logging.handlers.RotatingFileHandler',
244+
'level': {file_log_level},
245+
'formatter': 'json',
246+
'filename': '{log_dir}/{LOG_FILE}',
247+
'maxBytes': 1048576,
248+
'backupCount': 1
249+
}},
250+
'task': {{
251+
'class': 'airflow.utils.log.file_task_handler.FileTaskHandler',
252+
'formatter': 'airflow',
253+
'base_log_folder': '{log_dir}',
254+
'filters': ['mask_secrets_core']
255+
}}
256+
}},
257+
'loggers': {{
258+
'airflow.task': {{
259+
'handlers': ['task'],
260+
'level': logging.INFO,
261+
'propagate': True,
262+
'filters': ['mask_secrets_core']
263+
}}
264+
}},
265+
'root': {{
266+
'handlers': ['default', 'file'],
267+
'level': {root_log_level},
268+
'propagate': True
269+
}}
270+
}}
271+
{loggers_config}
272+
REMOTE_TASK_LOG = airflow_local_settings.REMOTE_TASK_LOG
273+
",
274+
console_log_level = log_config
275+
.console
276+
.as_ref()
277+
.and_then(|console| console.level)
278+
.unwrap_or_default()
279+
.to_python_expression(),
280+
file_log_level = log_config
281+
.file
282+
.as_ref()
283+
.and_then(|file| file.level)
284+
.unwrap_or_default()
285+
.to_python_expression(),
286+
root_log_level = log_config.root_log_level().to_python_expression(),
287+
)
288+
}

tests/templates/kuttl/commons/metrics.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ def metrics_v3(role_group: str) -> None:
3838
# allow a few moments for the DAGs to be registered to all roles
3939
time.sleep(10)
4040

41-
rest_url = "http://airflow-webserver:8080/api/v2"
42-
token_url = "http://airflow-webserver:8080/auth/token"
41+
rest_url = f"http://airflow-webserver-{role_group}-headless:8080/api/v2"
42+
token_url = f"http://airflow-webserver-{role_group}-headless:8080/auth/token"
4343

4444
data = {"username": "airflow", "password": "airflow"}
4545

@@ -110,7 +110,7 @@ def metrics_v2(role_group: str) -> None:
110110
dag_id = "example_trigger_target_dag"
111111
dag_conf = {"message": "Hello World"}
112112

113-
rest_url = "http://airflow-webserver:8080/api/v1"
113+
rest_url = f"http://airflow-webserver-{role_group}-headless:8080/api/v1"
114114
auth = ("airflow", "airflow")
115115

116116
# allow a few moments for the DAGs to be registered to all roles

tests/templates/kuttl/logging/41-install-airflow-cluster.yaml.j2

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ data:
4949
'filename': '/stackable/log/airflow/airflow.py.json',
5050
}
5151

52+
LOGGING_CONFIG['loggers']['airflow'] = {
53+
'level': logging.DEBUG,
54+
}
55+
LOGGING_CONFIG['loggers']['sqlalchemy.engine'] = {
56+
'level': logging.DEBUG,
57+
}
58+
5259
LOGGING_CONFIG['root'] = {
5360
'level': logging.DEBUG,
5461
'handlers': ['file'],
@@ -159,6 +166,10 @@ spec:
159166
loggers:
160167
ROOT:
161168
level: DEBUG
169+
airflow:
170+
level: DEBUG
171+
sqlalchemy.engine:
172+
level: DEBUG
162173
git-sync:
163174
console:
164175
level: INFO

tests/templates/kuttl/logging/52-assert.yaml.j2

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,3 @@ commands:
1414
kubectl exec -n $NAMESPACE test-airflow-python-0 -- python /tmp/metrics.py --role-group automatic-log-config --airflow-version "{{ test_scenario['values']['airflow'] }}"
1515
kubectl exec -n $NAMESPACE test-airflow-python-0 -- python /tmp/metrics.py --role-group custom-log-config --airflow-version "{{ test_scenario['values']['airflow'] }}"
1616
{% endif %}
17-

tests/templates/kuttl/mount-dags-configmap/50-assert.yaml.j2

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,3 @@ commands:
1010
{% else %}
1111
- script: kubectl exec -n $NAMESPACE test-airflow-python-0 -- python /tmp/health.py --airflow-version "{{ test_scenario['values']['airflow-latest'] }}"
1212
{% endif %}
13-

tests/templates/kuttl/resources/30-assert.yaml.j2

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ spec:
2525
resources:
2626
requests:
2727
cpu: 100m
28-
memory: 2Gi
28+
memory: 3001Mi
2929
limits:
3030
cpu: "1"
31-
memory: 2Gi
31+
memory: 3001Mi
3232
- name: metrics
3333
{% if lookup('env', 'VECTOR_AGGREGATOR') %}
3434
- name: vector
@@ -49,10 +49,10 @@ spec:
4949
resources:
5050
requests:
5151
cpu: 200m
52-
memory: 3Gi
52+
memory: 3002Mi
5353
limits:
5454
cpu: "2"
55-
memory: 3Gi
55+
memory: 3002Mi
5656
- name: metrics
5757
{% if lookup('env', 'VECTOR_AGGREGATOR') %}
5858
- name: vector
@@ -73,10 +73,10 @@ spec:
7373
resources:
7474
requests:
7575
cpu: 300m
76-
memory: 2Gi
76+
memory: 3001Mi
7777
limits:
7878
cpu: 900m
79-
memory: 2Gi
79+
memory: 3001Mi
8080
- name: metrics
8181
{% if lookup('env', 'VECTOR_AGGREGATOR') %}
8282
- name: vector

tests/templates/kuttl/resources/30-install-airflow-cluster.yaml.j2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ spec:
5555
min: 100m
5656
max: "1"
5757
memory:
58-
limit: 2Gi
58+
limit: 3001Mi
5959
roleGroups:
6060
resources-from-role:
6161
replicas: 1
@@ -66,7 +66,7 @@ spec:
6666
min: 200m
6767
max: "2"
6868
memory:
69-
limit: 3Gi
69+
limit: 3002Mi
7070
replicas: 1
7171
resources-from-pod-overrides:
7272
podOverrides:

tests/templates/kuttl/smoke/40-assert.yaml.j2

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ apiVersion: apps/v1
1717
kind: StatefulSet
1818
metadata:
1919
name: airflow-webserver-default
20-
generation: 1 # There should be no unneeded Pod restarts
2120
labels:
2221
restarter.stackable.tech/enabled: "true"
2322
spec:
@@ -33,7 +32,6 @@ apiVersion: apps/v1
3332
kind: StatefulSet
3433
metadata:
3534
name: airflow-worker-default
36-
generation: 1 # There should be no unneeded Pod restarts
3735
labels:
3836
restarter.stackable.tech/enabled: "true"
3937
spec:
@@ -49,7 +47,6 @@ apiVersion: apps/v1
4947
kind: StatefulSet
5048
metadata:
5149
name: airflow-scheduler-default
52-
generation: 1 # There should be no unneeded Pod restarts
5350
labels:
5451
restarter.stackable.tech/enabled: "true"
5552
spec:
@@ -64,7 +61,6 @@ apiVersion: apps/v1
6461
kind: StatefulSet
6562
metadata:
6663
name: airflow-dagprocessor-default
67-
generation: 1 # There should be no unneeded Pod restarts
6864
labels:
6965
restarter.stackable.tech/enabled: "true"
7066
spec:
@@ -79,7 +75,6 @@ apiVersion: apps/v1
7975
kind: StatefulSet
8076
metadata:
8177
name: airflow-triggerer-default
82-
generation: 1 # There should be no unneeded Pod restarts
8378
labels:
8479
restarter.stackable.tech/enabled: "true"
8580
spec:

0 commit comments

Comments
 (0)