This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/v3-3-test by this push:
new 4a0484265f3 [v3-3-test] Prevent scheduler crash when process/thread
are missing from log format (#69402) (#69787)
4a0484265f3 is described below
commit 4a0484265f34e4ca41a5a04da340c5ca32e412c4
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Mon Jul 13 02:28:01 2026 +0200
[v3-3-test] Prevent scheduler crash when process/thread are missing from
log format (#69402) (#69787)
When a record reaches the percent formatter without callsite information,
for example a stdlib warning routed through the logging bridge, the process
and thread fields are absent. The formatter fell back to the "(unknown)"
string for them, so a format string using the numeric "%(process)d" or
"%(thread)d" specifiers raised "TypeError: %d format: a real number is
required" and could take down the scheduler at startup.
Give those two numeric callsite parameters a numeric fallback of 0, the same
way lineno is already handled, so the format never receives a string where a
number is expected.
(cherry picked from commit fd7d535c3ff5d3618c2a4a83a634fdec09c911e9)
Co-authored-by: Anas Khan <[email protected]>
---
.../src/airflow_shared/logging/percent_formatter.py | 6 ++++++
shared/logging/tests/logging/test_percent_formatter.py | 18 ++++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/shared/logging/src/airflow_shared/logging/percent_formatter.py
b/shared/logging/src/airflow_shared/logging/percent_formatter.py
index ee9195e59b5..0efbb78d992 100644
--- a/shared/logging/src/airflow_shared/logging/percent_formatter.py
+++ b/shared/logging/src/airflow_shared/logging/percent_formatter.py
@@ -55,6 +55,12 @@ class _LazyLogRecordDict(collections.abc.Mapping):
#
https://github.com/python/cpython/blob/d3c888b4ec15dbd7d6b6ef4f15b558af77c228af/Lib/logging/__init__.py#L1652C34-L1652C48
if key == "lineno":
return self.event.get("lineno") or 0
+ # process and thread are numeric callsite params formatted with %d, so
fall back to a
+ # number (like lineno above) rather than the "(unknown)" string used
for text params;
+ # otherwise "%(process)d"/"%(thread)d" raises TypeError when the
callsite info is absent
+ # (e.g. warnings routed through the logging bridge).
+ if key == "process" or key == "thread":
+ return self.event.get(key) or 0
if key == "filename":
return self.event.get("filename", "(unknown file)")
if key == "funcName":
diff --git a/shared/logging/tests/logging/test_percent_formatter.py
b/shared/logging/tests/logging/test_percent_formatter.py
index 3a3ae84f562..217c23708e5 100644
--- a/shared/logging/tests/logging/test_percent_formatter.py
+++ b/shared/logging/tests/logging/test_percent_formatter.py
@@ -19,6 +19,8 @@ from __future__ import annotations
from unittest import mock
+import pytest
+
from airflow_shared.logging.percent_formatter import PercentFormatRender
@@ -40,3 +42,19 @@ class TestPercentFormatRender:
)
assert formatted == "test.py:0 our msg"
+
+ @pytest.mark.parametrize(
+ "event",
+ [
+ pytest.param({"event": "our msg"}, id="missing"),
+ pytest.param({"event": "our msg", "process": None, "thread":
None}, id="none"),
+ ],
+ )
+ def test_numeric_callsite_without_process_or_thread(self, event):
+ # Regression for a scheduler crash: a %d specifier for process/thread
with no callsite
+ # info (e.g. a warning routed through the logging bridge) must not
raise TypeError.
+ fmter = PercentFormatRender("%(process)d:%(thread)d %(message)s")
+
+ formatted = fmter(mock.Mock(name="Logger"), "info", event)
+
+ assert formatted == "0:0 our msg"