diff --git a/google/cloud/logging_v2/client.py b/google/cloud/logging_v2/client.py index f196f443..17d85340 100644 --- a/google/cloud/logging_v2/client.py +++ b/google/cloud/logging_v2/client.py @@ -52,6 +52,7 @@ _GAE_RESOURCE_TYPE = "gae_app" _GKE_RESOURCE_TYPE = "k8s_container" +_GCF_RESOURCE_TYPE = "cloud_function" class Client(ClientWithProject): diff --git a/google/cloud/logging_v2/handlers/_monitored_resources.py b/google/cloud/logging_v2/handlers/_monitored_resources.py index 4bc30d4f..bd05c252 100644 --- a/google/cloud/logging_v2/handlers/_monitored_resources.py +++ b/google/cloud/logging_v2/handlers/_monitored_resources.py @@ -163,7 +163,7 @@ def _create_global_resource(project): return Resource(type="global", labels={"project_id": project}) -def detect_resource(project): +def detect_resource(project=""): """Return the default monitored resource based on the local environment. Args: project (str): The project ID to pass on to the resource diff --git a/google/cloud/logging_v2/handlers/handlers.py b/google/cloud/logging_v2/handlers/handlers.py index c2ad6f35..feeac917 100644 --- a/google/cloud/logging_v2/handlers/handlers.py +++ b/google/cloud/logging_v2/handlers/handlers.py @@ -23,6 +23,8 @@ EXCLUDED_LOGGER_DEFAULTS = ("google.cloud", "google.auth", "google_auth_httplib2") +_CLEAR_HANDLER_RESOURCE_TYPES = ("gae_app", "cloud_function") + class CloudLoggingHandler(logging.StreamHandler): """Handler that directly makes Cloud Logging API calls. @@ -160,6 +162,11 @@ def setup_logging( """ all_excluded_loggers = set(excluded_loggers + EXCLUDED_LOGGER_DEFAULTS) logger = logging.getLogger() + + # remove built-in handlers on App Engine or Cloud Functions environments + if detect_resource().type in _CLEAR_HANDLER_RESOURCE_TYPES: + logger.handlers.clear() + logger.setLevel(log_level) logger.addHandler(handler) for logger_name in all_excluded_loggers: diff --git a/tests/environment b/tests/environment index a7dd0271..265a954c 160000 --- a/tests/environment +++ b/tests/environment @@ -1 +1 @@ -Subproject commit a7dd027166b8df1980881a94ba8473065497701d +Subproject commit 265a954c13ab30188adbf42a9e63d2bb7b9969ab diff --git a/tests/unit/handlers/test_handlers.py b/tests/unit/handlers/test_handlers.py index 0e7c63cc..51e2f070 100644 --- a/tests/unit/handlers/test_handlers.py +++ b/tests/unit/handlers/test_handlers.py @@ -14,8 +14,14 @@ import logging import unittest +from unittest.mock import patch import mock +from google.cloud.logging_v2.handlers._monitored_resources import ( + _FUNCTION_ENV_VARS, + _GAE_ENV_VARS, +) + class TestCloudLoggingHandler(unittest.TestCase): @@ -165,6 +171,49 @@ def test_setup_logging_excludes(self): self.assertNotIn(handler, excluded_logger.handlers) self.assertFalse(excluded_logger.propagate) + @patch.dict("os.environ", {envar: "1" for envar in _FUNCTION_ENV_VARS}) + def test_remove_handlers_gcf(self): + logger = logging.getLogger() + # add fake handler + added_handler = logging.StreamHandler() + logger.addHandler(added_handler) + + handler = _Handler(logging.INFO) + self._call_fut(handler) + self.assertNotIn(added_handler, logger.handlers) + # handler should be removed from logger + self.assertEqual(len(logger.handlers), 1) + + @patch.dict("os.environ", {envar: "1" for envar in _GAE_ENV_VARS}) + def test_remove_handlers_gae(self): + logger = logging.getLogger() + # add fake handler + added_handler = logging.StreamHandler() + logger.addHandler(added_handler) + + handler = _Handler(logging.INFO) + self._call_fut(handler) + self.assertNotIn(added_handler, logger.handlers) + # handler should be removed from logger + self.assertEqual(len(logger.handlers), 1) + + def test_keep_handlers_others(self): + # mock non-cloud environment + patch = mock.patch( + "google.cloud.logging_v2.handlers._monitored_resources.retrieve_metadata_server", + return_value=None, + ) + with patch: + # add fake handler + added_handler = logging.StreamHandler() + logger = logging.getLogger() + logger.addHandler(added_handler) + + handler = _Handler(logging.INFO) + self._call_fut(handler) + # added handler should remain in logger + self.assertIn(added_handler, logger.handlers) + def setUp(self): self._handlers_cache = logging.getLogger().handlers[:]