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

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


The following commit(s) were added to refs/heads/main by this push:
     new 455a73882c [MINOR] PythonAPI: Fix for correct logging setup for debug
455a73882c is described below

commit 455a73882c42246deeb4c2659952bd4729756e18
Author: e-strauss <lathan...@gmx.de>
AuthorDate: Wed Jul 23 15:32:47 2025 +0200

    [MINOR] PythonAPI: Fix for correct logging setup for debug
    
    Closes #2295
---
 .../python/systemds/context/systemds_context.py    | 57 +++++++++++++++-----
 .../python/tests/basics/test_context_creation.py   | 61 ++++++++++++++++++++++
 2 files changed, 106 insertions(+), 12 deletions(-)

diff --git a/src/main/python/systemds/context/systemds_context.py 
b/src/main/python/systemds/context/systemds_context.py
index 25824bc663..a559850c33 100644
--- a/src/main/python/systemds/context/systemds_context.py
+++ b/src/main/python/systemds/context/systemds_context.py
@@ -66,6 +66,7 @@ class SystemDSContext(object):
     _log: logging.Logger
     __stdout: Queue = None
     __stderr: Queue = None
+    _logging_initialized = False
 
     def __init__(
         self,
@@ -779,21 +780,53 @@ class SystemDSContext(object):
         :param level: The SystemDS logging part logging level.
         :param py4j_level: The Py4J logging level.
         """
-        logging.basicConfig()
+        # Set py4j level every time
         py4j = logging.getLogger("py4j.java_gateway")
         py4j.setLevel(py4j_level)
         py4j.propagate = False
 
+        if not SystemDSContext._logging_initialized:
+            # Add handler only once
+            logging.basicConfig()
+
+            root_logger = logging.getLogger(self.__class__.__name__)
+            root_logger.handlers.clear()
+
+            f_handler = DynamicStderrHandler()
+            f_handler.setLevel(logging.NOTSET)
+            f_handler.setFormatter(
+                logging.Formatter(
+                    "%(asctime)s %(levelname)s %(name)s: %(message)s",
+                    "%y/%m/%d %H:%M:%S",
+                )
+            )
+
+            root_logger.addHandler(f_handler)
+            root_logger.propagate = False
+
+            SystemDSContext._logging_initialized = True
+
+        # Per-instance logger setup
         self._log = logging.getLogger(self.__class__.__name__)
-        f_handler = logging.StreamHandler()
-        f_handler.setLevel(level)
-        f_format = logging.Formatter(
-            "%(asctime)s - SystemDS- %(levelname)s - %(message)s"
+        self._log.setLevel(level)
+        self._log.debug(
+            "Logging setup done (SystemDS level: %s, Py4J level: %s)",
+            logging.getLevelName(level),
+            logging.getLevelName(py4j_level),
         )
-        f_handler.setFormatter(f_format)
-        self._log.addHandler
-        # avoid the logger to call loggers above.
-        self._log.propagate = False
-        # Reset all handlers to only this new handler.
-        self._log.handlers = [f_handler]
-        self._log.debug("Logging setup done")
+
+
+class DynamicStderrHandler(logging.StreamHandler):
+    def __init__(self, level=logging.NOTSET, formatter=None):
+        # Avoid setting stream directly to sys.stderr, we will do that 
dynamically
+        # For more info see test case: 
tests/basics/test_context_creation/test_random_port_debug3
+        # where we redirect the err channel in between different contexts
+        super().__init__(stream=None)
+        self.setLevel(level)
+        if formatter:
+            self.setFormatter(formatter)
+
+    def emit(self, record):
+        # Always use the current sys.stderr when emitting
+        self.stream = sys.stderr
+        super().emit(record)
diff --git a/src/main/python/tests/basics/test_context_creation.py 
b/src/main/python/tests/basics/test_context_creation.py
index 920dace8be..3d80420d4e 100644
--- a/src/main/python/tests/basics/test_context_creation.py
+++ b/src/main/python/tests/basics/test_context_creation.py
@@ -21,12 +21,73 @@
 
 import unittest
 import logging
+import io
+import sys
+from contextlib import redirect_stdout, redirect_stderr
 
 from systemds.context import SystemDSContext
 
 
 class TestContextCreation(unittest.TestCase):
 
+    def test_random_port_debug(self):
+        SystemDSContext._logging_initialized = False
+
+        stderr_buffer = io.StringIO()
+
+        with redirect_stderr(stderr_buffer):
+            sds1 = SystemDSContext(logging_level=10)
+            sds1.close()
+
+        err = stderr_buffer.getvalue()
+        print("Captured STDERR:\n", err)
+        print("END OF STDERR\n")
+
+        self.assertIn("DEBUG SystemDSContext: Logging setup done", err)
+
+    def test_random_port_debug2(self):
+        SystemDSContext._logging_initialized = False
+
+        stderr_buffer = io.StringIO()
+
+        with redirect_stderr(stderr_buffer):
+            sds1 = SystemDSContext()
+            sds1.close()
+
+            err = stderr_buffer.getvalue()
+            print("\nCaptured STDERR (ctx1):\n", err)
+            print("END OF STDERR\n")
+
+            # clear the buffer
+            stderr_buffer.seek(0)
+            stderr_buffer.truncate(0)
+
+            sds2 = SystemDSContext(logging_level=10)
+            sds2.close()
+
+        err = stderr_buffer.getvalue()
+        print("\nCaptured STDERR (ctx2):\n", err)
+        print("END OF STDERR\n")
+
+        self.assertIn("DEBUG SystemDSContext: Logging setup done", err)
+
+    def test_random_port_debug3(self):
+        SystemDSContext._logging_initialized = False
+
+        sds1 = SystemDSContext()
+        sds1.close()
+        stderr_buffer = io.StringIO()
+
+        with redirect_stderr(stderr_buffer):
+            sds2 = SystemDSContext(logging_level=10)
+            sds2.close()
+
+        err = stderr_buffer.getvalue()
+        print("\nCaptured STDERR (ctx2):\n", err)
+        print("END OF STDERR\n")
+
+        self.assertIn("DEBUG SystemDSContext: Logging setup done", err)
+
     def test_random_port(self):
         sds1 = SystemDSContext()
         sds1.close()

Reply via email to