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

amoghdesai 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 8b4d2e60c88 Move LoggingMixin to task sdk internal module (#58551)
8b4d2e60c88 is described below

commit 8b4d2e60c886c4a7966c3fb91769dbd268139312
Author: Amogh Desai <[email protected]>
AuthorDate: Tue Nov 25 18:06:27 2025 +0530

    Move LoggingMixin to task sdk internal module (#58551)
---
 .../logging-monitoring/logging-tasks.rst           | 11 ++-
 task-sdk/src/airflow/sdk/bases/hook.py             |  2 +-
 task-sdk/src/airflow/sdk/bases/notifier.py         |  2 +-
 .../sdk/definitions/_internal/logging_mixin.py     | 89 ++++++++++++++++++++++
 4 files changed, 98 insertions(+), 6 deletions(-)

diff --git 
a/airflow-core/docs/administration-and-deployment/logging-monitoring/logging-tasks.rst
 
b/airflow-core/docs/administration-and-deployment/logging-monitoring/logging-tasks.rst
index 5ab451f29b6..2403a487f96 100644
--- 
a/airflow-core/docs/administration-and-deployment/logging-monitoring/logging-tasks.rst
+++ 
b/airflow-core/docs/administration-and-deployment/logging-monitoring/logging-tasks.rst
@@ -61,10 +61,13 @@ Airflow uses the standard Python `logging 
<https://docs.python.org/3/library/log
 write logs, and for the duration of a task, the root logger is configured to 
write to the task's log.
 
 Most operators will write logs to the task log automatically. This is because 
they
-have a ``log`` logger that you can use to write to the task log.
-This logger is created and configured by 
:class:`~airflow.utils.log.LoggingMixin` that all
-operators derive from. But also due to the root logger handling, any standard 
logger (using default settings) that
-propagates logging to the root will also write to the task log.
+have a ``log`` property (of type :class:`~airflow.sdk.types.Logger`) that you 
can use
+to write to the task log. This logger is automatically configured for all 
operators
+derived from :class:`~airflow.sdk.BaseOperator`.
+
+Additionally, due to the root logger configuration during task execution, any 
standard
+Python logger (using default settings) that propagates to the root logger will 
also write to
+the task log.
 
 So if you want to log to the task log from custom code of yours you can do any 
of the following:
 
diff --git a/task-sdk/src/airflow/sdk/bases/hook.py 
b/task-sdk/src/airflow/sdk/bases/hook.py
index 8aaba17ca78..9c70684fd80 100644
--- a/task-sdk/src/airflow/sdk/bases/hook.py
+++ b/task-sdk/src/airflow/sdk/bases/hook.py
@@ -19,7 +19,7 @@ from __future__ import annotations
 import logging
 from typing import TYPE_CHECKING, Any
 
-from airflow.utils.log.logging_mixin import LoggingMixin
+from airflow.sdk.definitions._internal.logging_mixin import LoggingMixin
 
 if TYPE_CHECKING:
     from airflow.sdk.definitions.connection import Connection
diff --git a/task-sdk/src/airflow/sdk/bases/notifier.py 
b/task-sdk/src/airflow/sdk/bases/notifier.py
index 7cfed6ae97e..73d6d5e41c1 100644
--- a/task-sdk/src/airflow/sdk/bases/notifier.py
+++ b/task-sdk/src/airflow/sdk/bases/notifier.py
@@ -20,9 +20,9 @@ from __future__ import annotations
 from collections.abc import Generator, Sequence
 from typing import TYPE_CHECKING
 
+from airflow.sdk.definitions._internal.logging_mixin import LoggingMixin
 from airflow.sdk.definitions._internal.templater import Templater
 from airflow.sdk.definitions.context import context_merge
-from airflow.utils.log.logging_mixin import LoggingMixin
 
 if TYPE_CHECKING:
     import jinja2
diff --git a/task-sdk/src/airflow/sdk/definitions/_internal/logging_mixin.py 
b/task-sdk/src/airflow/sdk/definitions/_internal/logging_mixin.py
new file mode 100644
index 00000000000..f482dfede56
--- /dev/null
+++ b/task-sdk/src/airflow/sdk/definitions/_internal/logging_mixin.py
@@ -0,0 +1,89 @@
+# 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.
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, TypeVar
+
+import structlog
+
+if TYPE_CHECKING:
+    from airflow.sdk.types import Logger
+
+_T = TypeVar("_T")
+
+
+class LoggingMixin:
+    """Convenience super-class to have a logger configured with the class 
name."""
+
+    _log: Logger | None = None
+
+    # Parent logger used by this class. It should match one of the loggers 
defined in the
+    # `logging_config_class`. By default, this attribute is used to create the 
final name of the logger, and
+    # will prefix the `_logger_name` with a separating dot.
+    _log_config_logger_name: str | None = None
+
+    _logger_name: str | None = None
+
+    def __init__(self, context=None):
+        self._set_context(context)
+        super().__init__()
+
+    @staticmethod
+    def _create_logger_name(
+        logged_class: type[_T],
+        log_config_logger_name: str | None = None,
+        class_logger_name: str | None = None,
+    ) -> str:
+        """
+        Generate a logger name for the given `logged_class`.
+
+        By default, this function returns the `class_logger_name` as logger 
name. If it is not provided,
+        the {class.__module__}.{class.__name__} is returned instead. When a 
`parent_logger_name` is provided,
+        it will prefix the logger name with a separating dot.
+        """
+        logger_name: str = (
+            class_logger_name
+            if class_logger_name is not None
+            else f"{logged_class.__module__}.{logged_class.__name__}"
+        )
+
+        if log_config_logger_name:
+            return f"{log_config_logger_name}.{logger_name}" if logger_name 
else log_config_logger_name
+        return logger_name
+
+    @classmethod
+    def _get_log(cls, obj: Any, clazz: type[_T]) -> Logger:
+        if obj._log is None:
+            logger_name: str = cls._create_logger_name(
+                logged_class=clazz,
+                log_config_logger_name=obj._log_config_logger_name,
+                class_logger_name=obj._logger_name,
+            )
+            obj._log = structlog.get_logger(logger_name)
+        return obj._log
+
+    @classmethod
+    def logger(cls) -> Logger:
+        """Return a logger."""
+        return LoggingMixin._get_log(cls, cls)
+
+    @property
+    def log(self) -> Logger:
+        """Return a logger."""
+        return LoggingMixin._get_log(self, self.__class__)
+
+    def _set_context(self, context): ...

Reply via email to