This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new fd7d535c3ff Prevent scheduler crash when process/thread are missing 
from log format (#69402)
fd7d535c3ff is described below

commit fd7d535c3ff5d3618c2a4a83a634fdec09c911e9
Author: Anas Khan <[email protected]>
AuthorDate: Mon Jul 13 02:56:03 2026 +0530

    Prevent scheduler crash when process/thread are missing from log format 
(#69402)
    
    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.
---
 .../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"

Reply via email to