20613: Update unconfigured logger check to accommodate NullHandler 20613-googleapiclient-init-logs
authorBrett Smith <brett.smith@curii.com>
Wed, 7 Jun 2023 19:00:43 +0000 (15:00 -0400)
committerBrett Smith <brett.smith@curii.com>
Wed, 7 Jun 2023 19:00:43 +0000 (15:00 -0400)
googleapiclient sets up its own NullHandler, as recommended by the
logging documentation. That makes the hasHandlers() test not work as
intended, so instead we traverse the hierarchy ourselves looking for a
real handler.

Arvados-DCO-1.1-Signed-off-by: Brett Smith <brett.smith@curii.com>

sdk/python/arvados/api.py
sdk/python/tests/test_api.py

index a1493b904eb6176da16a7cbe7a412b245b28634f..a7f3837599c20d82df65a50e6e139d89629f56b5 100644 (file)
@@ -261,11 +261,14 @@ def api_client(
     # can cause clients to appear to hang early. This can be removed after
     # we have a more general story for handling googleapiclient logs (#20521).
     client_logger = logging.getLogger('googleapiclient.http')
-    client_logger_unconfigured = (
-        # "first time a client is instantiated" = thread that acquires this lock
-        # It is never released.
-        _googleapiclient_log_lock.acquire(blocking=False)
-        and not client_logger.hasHandlers()
+    # "first time a client is instantiated" = thread that acquires this lock
+    # It is never released.
+    # googleapiclient sets up its own NullHandler so we detect if logging is
+    # configured by looking for a real handler anywhere in the hierarchy.
+    client_logger_unconfigured = _googleapiclient_log_lock.acquire(blocking=False) and all(
+        isinstance(handler, logging.NullHandler)
+        for logger_name in ['', 'googleapiclient', 'googleapiclient.http']
+        for handler in logging.getLogger(logger_name).handlers
     )
     if client_logger_unconfigured:
         client_level = client_logger.level
index 8380185bbb34e8ddfb790c196c68fba5c0b93b62..0f85e5520c821dcaa7bf6690e7702cb857e3ac54 100644 (file)
@@ -402,7 +402,7 @@ class ArvadosApiTest(run_test_server.TestCaseWithServers):
             pass
         real_logger = logging.getLogger('googleapiclient.http')
         mock_logger = mock.Mock(wraps=real_logger)
-        mock_logger.hasHandlers.return_value = False
+        mock_logger.handlers = logging.getLogger('googleapiclient').handlers
         mock_logger.level = logging.NOTSET
         with mock.patch('logging.getLogger', return_value=mock_logger), \
              mock.patch('time.sleep'), \
@@ -421,7 +421,7 @@ class ArvadosApiTest(run_test_server.TestCaseWithServers):
     def test_configured_logger_untouched(self):
         real_logger = logging.getLogger('googleapiclient.http')
         mock_logger = mock.Mock(wraps=real_logger)
-        mock_logger.hasHandlers.return_value = True
+        mock_logger.handlers = logging.getLogger().handlers
         with mock.patch('logging.getLogger', return_value=mock_logger), \
              mock.patch('time.sleep'):
             try: