Repository: googleapis/google-cloud-python

Issue:
[Logging] Move the code path of calling get_gae_labels() to emit()
Related with #3359. To make it easier to set the labels in each request without explicitly calling `handler.get_gae_labels()` or calling `client.setup_logging()` inside the request which causes a lot overhead, it's better to move the code path of getting the gae labels to the `emit()` function in the logging handler. And change the `AppEngineHandler` to extend the `logging.StreamHandler` directly instead of extending the `CloudLoggingHandler`.


Discussion:

Resolving patch:
diff --git a/logging/google/cloud/logging/handlers/app_engine.py b/logging/google/cloud/logging/handlers/app_engine.py
--- a/logging/google/cloud/logging/handlers/app_engine.py
+++ b/logging/google/cloud/logging/handlers/app_engine.py
@@ -18,10 +18,10 @@
 and labels for App Engine logs.
 """
 
+import logging
 import os
 
 from google.cloud.logging.handlers._helpers import get_trace_id
-from google.cloud.logging.handlers.handlers import CloudLoggingHandler
 from google.cloud.logging.handlers.transports import BackgroundThreadTransport
 from google.cloud.logging.resource import Resource
 
@@ -34,7 +34,7 @@
 _TRACE_ID_LABEL = 'appengine.googleapis.com/trace_id'
 
 
-class AppEngineHandler(CloudLoggingHandler):
+class AppEngineHandler(logging.StreamHandler):
     """A logging handler that sends App Engine-formatted logs to Stackdriver.
 
     :type client: :class:`~google.cloud.logging.client.Client`
@@ -48,13 +48,13 @@ class AppEngineHandler(CloudLoggingHandler):
     """
 
     def __init__(self, client,
+                 name=_DEFAULT_GAE_LOGGER_NAME,
                  transport=BackgroundThreadTransport):
-        super(AppEngineHandler, self).__init__(
-            client,
-            name=_DEFAULT_GAE_LOGGER_NAME,
-            transport=transport,
-            resource=self.get_gae_resource(),
-            labels=self.get_gae_labels())
+        super(AppEngineHandler, self).__init__()
+        self.name = name
+        self.client = client
+        self.transport = transport(client, name)
+        self.resource = self.get_gae_resource()
 
     def get_gae_resource(self):
         """Return the GAE resource using the environment variables.
@@ -88,3 +88,20 @@ def get_gae_labels(self):
             gae_labels[_TRACE_ID_LABEL] = trace_id
 
         return gae_labels
+
+    def emit(self, record):
+        """Actually log the specified logging record.
+
+        Overrides the default emit behavior of ``StreamHandler``.
+
+        See https://docs.python.org/2/library/logging.html#handler-objects
+
+        :type record: :class:`logging.LogRecord`
+        :param record: The record to be logged.
+        """
+        message = super(AppEngineHandler, self).format(record)
+        self.transport.send(
+            record,
+            message,
+            resource=self.resource,
+            labels=self.get_gae_labels())
diff --git a/logging/google/cloud/logging/handlers/handlers.py b/logging/google/cloud/logging/handlers/handlers.py
--- a/logging/google/cloud/logging/handlers/handlers.py
+++ b/logging/google/cloud/logging/handlers/handlers.py
@@ -35,6 +35,8 @@ class CloudLoggingHandler(logging.StreamHandler):
     route Python standard logging messages directly to the Stackdriver
     Logging API.
 
+    This handler is used when not in GAE or GKE environment.
+
     This handler supports both an asynchronous and synchronous transport.
 
     :type client: :class:`google.cloud.logging.client`


Test patch:
diff --git a/logging/tests/unit/handlers/test_app_engine.py b/logging/tests/unit/handlers/test_app_engine.py
--- a/logging/tests/unit/handlers/test_app_engine.py
+++ b/logging/tests/unit/handlers/test_app_engine.py
@@ -46,7 +46,6 @@ def test_constructor(self):
         self.assertEqual(handler.resource.labels['project_id'], 'test_project')
         self.assertEqual(handler.resource.labels['module_id'], 'test_service')
         self.assertEqual(handler.resource.labels['version_id'], 'test_version')
-        self.assertEqual(handler.labels, {})
 
     def test_emit(self):
         client = mock.Mock(project=self.PROJECT, spec=['project'])
@@ -74,11 +73,10 @@ def _get_gae_labels_helper(self, trace_id):
         # The handler actually calls ``get_gae_labels()``.
         with get_trace_patch as mock_get_trace:
             handler = self._make_one(client, transport=_Transport)
-            mock_get_trace.assert_called_once_with()
 
             gae_labels = handler.get_gae_labels()
             self.assertEqual(mock_get_trace.mock_calls,
-                             [mock.call(), mock.call()])
+                             [mock.call()])
 
         return gae_labels