Repository: googleapis/google-cloud-python

Issue:
Issue with logging and google-auth
The following code:
```
import google.cloud.logging
import logging

from google.cloud.logging.handlers import CloudLoggingHandler

client = google.cloud.logging.Client()
handler = CloudLoggingHandler(client)
cloud_logger = logging.getLogger()
cloud_logger.setLevel(logging.INFO)
cloud_logger.addHandler(handler)
cloud_logger.addHandler(logging.StreamHandler())
cloud_logger.info('hello')
```

works fine with google-cloud-logging==1.1.0 but fails with newer versions (1.2.0 or 1.3.0) :
```
Failed to submit 1 logs.
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/google/cloud/logging/handlers/transports/background_thread.py", line 99, in _safely_commit_batch
    batch.commit()
  File "/usr/local/lib/python2.7/dist-packages/google/cloud/logging/logger.py", line 549, in commit
    client.logging_api.write_entries(entries, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/google/cloud/logging/_http.py", line 163, in write_entries
    self.api_request(method='POST', path='/entries:write', data=data)
  File "/usr/local/lib/python2.7/dist-packages/google/cloud/_http.py", line 290, in api_request
    headers=headers, target_object=_target_object)
  File "/usr/local/lib/python2.7/dist-packages/google/cloud/_http.py", line 183, in _make_request
    return self._do_request(method, url, headers, data, target_object)
  File "/usr/local/lib/python2.7/dist-packages/google/cloud/_http.py", line 212, in _do_request
    url=url, method=method, headers=headers, data=data)
  File "/usr/local/lib/python2.7/dist-packages/google/auth/transport/requests.py", line 175, in request
    self.credentials.before_request(
AttributeError: 'AuthorizedSession' object has no attribute 'credentials'

```


Discussion:
Trying to repro right now.
@tcroiset I can't reproduce. Can you step into a debugger and inspect `self` / `pp self.__dict__` at the moment of failure? I usually just run code in IPython and then hop into the stacktrace with the `%debug` magic.
```
self:

<google.auth.transport.requests.AuthorizedSession object at 0x7fce6777bb90>
```

```
self.__dict__: 

 {
     'cookies': < RequestsCookieJar[] > ,
     'stream': False,
     'hooks': {
         'response': []
     },
     'auth': None,
     'trust_env': True,
     'headers': {
         'Connection': 'keep-alive',
         'Accept-Encoding': 'gzip, deflate',
         'Accept': '*/*',
         'User-Agent': 'python-requests/2.18.4'
     },
     'cert': None,
     'params': {},
     'prefetch': None,
     'verify': True,
     'proxies': {},
     'adapters': OrderedDict([('https://', < requests.adapters.HTTPAdapter object at 0x7fce6777bd50 > ), ('http://', < requests.adapters.HTTPAdapter object at 0x7fce6771a090 > )]),
     'max_redirects': 30
 }
```
@tcroiset Thanks a lot!

@jonparrott Any ideas?
Can I get the output of `pip freeze` @tcroiset?
cachetools==2.0.1
certifi==2017.7.27.1
chardet==3.0.4
colorama==0.3.2
dill==0.2.7.1
enum34==1.1.6
future==0.16.0
futures==3.1.1
gapic-google-cloud-logging-v2==0.91.3
google-auth==1.0.2
google-cloud-core==0.27.1
google-cloud-logging==1.3.0
google-gax==0.15.14
googleapis-common-protos==1.5.2
grpcio==1.6.0
html5lib==0.999
httplib2==0.10.3
idna==2.6
mercurial==3.1.2
oauth2client==3.0.0
ply==3.8
proto-google-cloud-logging-v2==0.91.3
protobuf==3.4.0
pyasn1==0.3.4
pyasn1-modules==0.1.4
requests==2.18.4
rsa==3.4.2
six==1.10.0
urllib3==1.22
virtualenv==15.0.3

Hrm I can't repro either. Can you tell me a bit more info?

* Python version
* OS
* Which type of credentials you're using (`import google.auth; print(google.auth.default())`)
Here is the dockerfile I use
(It's based on an appengine image because in my full code, it's an appengine app with a worker and a monitor like https://cloud.google.com/python/getting-started/tutorial-app)

```
FROM gcr.io/google_appengine/python

RUN apt-get update && apt-get install -y emacs
ADD requirements.txt /app/
RUN pip install -r requirements.txt

ADD . /app
ADD MyServiceAccount.json /cred/
ENV GOOGLE_APPLICATION_CREDENTIALS "/cred/MyServiceAccount.json"
ENV GOOGLE_CLOUD_DISABLE_GRPC true
CMD python my.py
```
Hum, I just noticed that it is linked with GOOGLE_CLOUD_DISABLE_GRPC.
If I remove it, the error doesn't occur.

Ah, it's probably [this line](https://github.com/GoogleCloudPlatform/google-cloud-python/blob/master/logging/google/cloud/logging/handlers/transports/background_thread.py#L257) which is a relic from when httplib2 was used as it wasn't threadsafe. We can probably just remove that.

@liyanhui1228 Can you take this?
Sure! I can take this. Sorry for the late reply, I'm looking into this right now.

Resolving patch:
diff --git a/logging/google/cloud/logging/handlers/transports/background_thread.py b/logging/google/cloud/logging/handlers/transports/background_thread.py
--- a/logging/google/cloud/logging/handlers/transports/background_thread.py
+++ b/logging/google/cloud/logging/handlers/transports/background_thread.py
@@ -20,7 +20,6 @@
 from __future__ import print_function
 
 import atexit
-import copy
 import logging
 import threading
 
@@ -254,9 +253,7 @@ class BackgroundThreadTransport(Transport):
 
     def __init__(self, client, name, grace_period=_DEFAULT_GRACE_PERIOD,
                  batch_size=_DEFAULT_MAX_BATCH_SIZE):
-        http = copy.deepcopy(client._http)
-        self.client = client.__class__(
-            client.project, client._credentials, http)
+        self.client = client
         logger = self.client.logger(name)
         self.worker = _Worker(logger)
         self.worker.start()


Test patch:
diff --git a/logging/tests/system.py b/logging/tests/system.py
--- a/logging/tests/system.py
+++ b/logging/tests/system.py
@@ -414,30 +414,32 @@ def test_create_sink_storage_bucket(self):
         self.assertTrue(sink.exists())
 
     def test_create_sink_pubsub_topic(self):
-        from google.cloud.iam import OWNER_ROLE
-        from google.cloud.pubsub import client as pubsub_client
+        import uuid
+
+        from google.cloud import pubsub_v1
 
         SINK_NAME = 'test-create-sink-topic%s' % (_RESOURCE_ID,)
-        TOPIC_NAME = 'logging-test-sink%s' % (_RESOURCE_ID,)
+        TOPIC_NAME = '%s-%s' % ('systest', str(uuid.uuid4())[0:8])
 
         # Create the destination topic, and set up the IAM policy to allow
         # Stackdriver Logging to write into it.
-        pubsub_client = pubsub_client.Client()
-        topic = pubsub_client.topic(TOPIC_NAME)
-        topic.create()
-        self.to_delete.append(topic)
-        policy = topic.get_iam_policy()
-        new_owners = set([policy.group('cloud-logs@google.com')])
-        new_owners.update(policy.owners)
-        policy[OWNER_ROLE] = new_owners
-        topic.set_iam_policy(policy)
-
-        TOPIC_URI = 'pubsub.googleapis.com/%s' % (topic.full_name,)
+        publisher = pubsub_v1.PublisherClient()
+        topic_path = publisher.topic_path(Config.CLIENT.project, TOPIC_NAME)
+        publisher.create_topic(topic_path)
+
+        policy = publisher.get_iam_policy(topic_path)
+        policy.bindings.add(
+            role='roles/owner',
+            members=['group:cloud-logs@google.com']
+        )
+        publisher.set_iam_policy(topic_path, policy)
+
+        TOPIC_URI = 'pubsub.googleapis.com/%s' % (topic_path,)
 
         sink = Config.CLIENT.sink(SINK_NAME, DEFAULT_FILTER, TOPIC_URI)
         self.assertFalse(sink.exists())
         sink.create()
-        self.to_delete.append(sink)
+        publisher.delete_topic(topic_path)
         self.assertTrue(sink.exists())
 
     def _init_bigquery_dataset(self):
diff --git a/logging/tests/unit/test_client.py b/logging/tests/unit/test_client.py
--- a/logging/tests/unit/test_client.py
+++ b/logging/tests/unit/test_client.py
@@ -560,23 +560,18 @@ def test_list_metrics_with_paging(self):
         })
 
     def test_get_default_handler_app_engine(self):
-        import requests
         import os
         from google.cloud._testing import _Monkey
         from google.cloud.logging.client import _APPENGINE_FLEXIBLE_ENV_VM
         from google.cloud.logging.handlers import AppEngineHandler
 
-        http_mock = mock.Mock(spec=requests.Session)
         credentials = _make_credentials()
-        deepcopy = mock.Mock(return_value=http_mock)
 
         with _Monkey(os, environ={_APPENGINE_FLEXIBLE_ENV_VM: 'True'}):
-            with mock.patch('copy.deepcopy', new=deepcopy):
-                client = self._make_one(project=self.PROJECT,
-                                        credentials=credentials,
-                                        _use_grpc=False)
-                handler = client.get_default_handler()
-                deepcopy.assert_called_once_with(client._http)
+            client = self._make_one(project=self.PROJECT,
+                                    credentials=credentials,
+                                    _use_grpc=False)
+            handler = client.get_default_handler()
 
         self.assertIsInstance(handler, AppEngineHandler)
 
@@ -598,39 +593,28 @@ def test_get_default_handler_container_engine(self):
         self.assertIsInstance(handler, ContainerEngineHandler)
 
     def test_get_default_handler_general(self):
-        import requests
         from google.cloud.logging.handlers import CloudLoggingHandler
 
-        http_mock = mock.Mock(spec=requests.Session)
         credentials = _make_credentials()
-        deepcopy = mock.Mock(return_value=http_mock)
 
-        with mock.patch('copy.deepcopy', new=deepcopy):
-            client = self._make_one(project=self.PROJECT,
-                                    credentials=credentials,
-                                    _use_grpc=False)
-            handler = client.get_default_handler()
-            deepcopy.assert_called_once_with(client._http)
+        client = self._make_one(project=self.PROJECT,
+                                credentials=credentials,
+                                _use_grpc=False)
+        handler = client.get_default_handler()
 
         self.assertIsInstance(handler, CloudLoggingHandler)
 
     def test_setup_logging(self):
-        import requests
-
-        http_mock = mock.Mock(spec=requests.Session)
-        deepcopy = mock.Mock(return_value=http_mock)
         setup_logging = mock.Mock(spec=[])
 
         credentials = _make_credentials()
 
-        with mock.patch('copy.deepcopy', new=deepcopy):
-            with mock.patch('google.cloud.logging.client.setup_logging',
-                            new=setup_logging):
-                client = self._make_one(project=self.PROJECT,
-                                        credentials=credentials,
-                                        _use_grpc=False)
-                client.setup_logging()
-                deepcopy.assert_called_once_with(client._http)
+        with mock.patch('google.cloud.logging.client.setup_logging',
+                        new=setup_logging):
+            client = self._make_one(project=self.PROJECT,
+                                    credentials=credentials,
+                                    _use_grpc=False)
+            client.setup_logging()
 
         setup_logging.assert_called()