Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Infer default resource in logger #315

Merged
merged 13 commits into from
Jun 18, 2021
Merged
9 changes: 7 additions & 2 deletions google/cloud/logging_v2/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,21 @@ def metrics_api(self):
self._metrics_api = JSONMetricsAPI(self)
return self._metrics_api

def logger(self, name):
def logger(self, name, *, labels=None, resource=None):
"""Creates a logger bound to the current client.

Args:
name (str): The name of the logger to be constructed.
resource (Optional[~logging_v2.Resource]): a monitored resource object
representing the resource the code was run on. If not given, will
be inferred from the environment.
labels (Optional[dict]): Mapping of default labels for entries written
via this logger.

Returns:
~logging_v2.logger.Logger: Logger created with the current client.
"""
return Logger(name, client=self)
return Logger(name, client=self, labels=labels, resource=resource)

def list_entries(
self,
Expand Down
8 changes: 5 additions & 3 deletions google/cloud/logging_v2/handlers/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import json
import logging

from google.cloud.logging_v2.logger import _GLOBAL_RESOURCE
from google.cloud.logging_v2.handlers.transports import BackgroundThreadTransport
from google.cloud.logging_v2.handlers._monitored_resources import detect_resource
from google.cloud.logging_v2.handlers._helpers import get_request_data
Expand Down Expand Up @@ -139,7 +138,7 @@ def __init__(
*,
name=DEFAULT_LOGGER_NAME,
transport=BackgroundThreadTransport,
resource=_GLOBAL_RESOURCE,
resource=None,
labels=None,
stream=None,
):
Expand All @@ -158,11 +157,14 @@ def __init__(
:class:`.BackgroundThreadTransport`. The other
option is :class:`.SyncTransport`.
resource (~logging_v2.resource.Resource):
Resource for this Handler. Defaults to ``global``.
Resource for this Handler. If not given, will be inferred from the environment.
labels (Optional[dict]): Additional labels to attach to logs.
stream (Optional[IO]): Stream to be used by the handler.
"""
super(CloudLoggingHandler, self).__init__(stream)
if not resource:
# infer the correct monitored resource from the local environment
resource = detect_resource(client.project)
self.name = name
self.client = client
self.transport = transport(client, name)
Expand Down
11 changes: 8 additions & 3 deletions google/cloud/logging_v2/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from google.cloud.logging_v2.entries import StructEntry
from google.cloud.logging_v2.entries import TextEntry
from google.cloud.logging_v2.resource import Resource
from google.cloud.logging_v2.handlers._monitored_resources import detect_resource


_GLOBAL_RESOURCE = Resource(type="global", labels={})
Expand Down Expand Up @@ -48,19 +49,23 @@ class Logger(object):
See https://cloud.google.com/logging/docs/reference/v2/rest/v2/projects.logs
"""

def __init__(self, name, client, *, labels=None, resource=_GLOBAL_RESOURCE):
def __init__(self, name, client, *, labels=None, resource=None):
"""
Args:
name (str): The name of the logger.
client (~logging_v2.client.Client):
A client which holds credentials and project configuration
for the logger (which requires a project).
resource (~logging_v2.Resource): a monitored resource object
representing the resource the code was run on.
resource (Optional[~logging_v2.Resource]): a monitored resource object
representing the resource the code was run on. If not given, will
be inferred from the environment.
labels (Optional[dict]): Mapping of default labels for entries written
via this logger.

"""
if not resource:
# infer the correct monitored resource from the local environment
resource = detect_resource(client.project)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this default to "global" in case the environment wasn't detected? (i.e. the code is running in dataflow job)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, global is the fallback for detect_resource

self.name = name
self._client = client
self.labels = labels
Expand Down
7 changes: 5 additions & 2 deletions tests/unit/handlers/test_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,9 @@ def _make_one(self, *args, **kw):

def test_ctor_defaults(self):
import sys
from google.cloud.logging_v2.logger import _GLOBAL_RESOURCE
from google.cloud.logging_v2.handlers._monitored_resources import (
_create_global_resource,
)
from google.cloud.logging_v2.handlers.handlers import DEFAULT_LOGGER_NAME

patch = mock.patch(
Expand All @@ -252,7 +254,8 @@ def test_ctor_defaults(self):
self.assertIsInstance(handler.transport, _Transport)
self.assertIs(handler.transport.client, client)
self.assertEqual(handler.transport.name, DEFAULT_LOGGER_NAME)
self.assertEqual(handler.resource, _GLOBAL_RESOURCE)
global_resource = _create_global_resource(self.PROJECT)
self.assertEqual(handler.resource, global_resource)
self.assertIsNone(handler.labels)
self.assertIs(handler.stream, sys.stderr)

Expand Down
8 changes: 7 additions & 1 deletion tests/unit/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,14 +239,20 @@ def make_api(client_obj):

def test_logger(self):
from google.cloud.logging import Logger
from google.cloud.logging_v2.logger import _GLOBAL_RESOURCE

creds = _make_credentials()
client = self._make_one(project=self.PROJECT, credentials=creds)
logger = client.logger(self.LOGGER_NAME)
labels = {"test": "true"}
logger = client.logger(
self.LOGGER_NAME, resource=_GLOBAL_RESOURCE, labels=labels
)
self.assertIsInstance(logger, Logger)
self.assertEqual(logger.name, self.LOGGER_NAME)
self.assertIs(logger.client, client)
self.assertEqual(logger.project, self.PROJECT)
self.assertEqual(logger.default_resource, _GLOBAL_RESOURCE)
self.assertEqual(logger.labels, labels)

def test_list_entries_defaults(self):
from google.cloud.logging import TextEntry
Expand Down
40 changes: 33 additions & 7 deletions tests/unit/test_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,15 @@ def test_batch_w_alternate_client(self):
self.assertIs(batch.client, client2)

def test_log_empty_defaults_w_default_labels(self):
from google.cloud.logging_v2.handlers._monitored_resources import (
detect_resource,
)

DEFAULT_LABELS = {"foo": "spam"}
ENTRIES = [
{
"logName": "projects/%s/logs/%s" % (self.PROJECT, self.LOGGER_NAME),
"resource": {"type": "global", "labels": {}},
"resource": detect_resource(self.PROJECT)._to_dict(),
"labels": DEFAULT_LABELS,
}
]
Expand Down Expand Up @@ -170,7 +174,11 @@ def test_log_empty_w_explicit(self):
self.assertEqual(api._write_entries_called_with, (ENTRIES, None, None, None))

def test_log_text_defaults(self):
RESOURCE = {"type": "global", "labels": {}}
from google.cloud.logging_v2.handlers._monitored_resources import (
detect_resource,
)

RESOURCE = detect_resource(self.PROJECT)._to_dict()
TEXT = "TEXT"
ENTRIES = [
{
Expand All @@ -188,8 +196,12 @@ def test_log_text_defaults(self):
self.assertEqual(api._write_entries_called_with, (ENTRIES, None, None, None))

def test_log_text_w_unicode_and_default_labels(self):
from google.cloud.logging_v2.handlers._monitored_resources import (
detect_resource,
)

TEXT = "TEXT"
RESOURCE = {"type": "global", "labels": {}}
RESOURCE = detect_resource(self.PROJECT)._to_dict()
DEFAULT_LABELS = {"foo": "spam"}
ENTRIES = [
{
Expand Down Expand Up @@ -265,8 +277,12 @@ def test_log_text_explicit(self):
self.assertEqual(api._write_entries_called_with, (ENTRIES, None, None, None))

def test_log_struct_defaults(self):
from google.cloud.logging_v2.handlers._monitored_resources import (
detect_resource,
)

STRUCT = {"message": "MESSAGE", "weather": "cloudy"}
RESOURCE = {"type": "global", "labels": {}}
RESOURCE = detect_resource(self.PROJECT)._to_dict()
ENTRIES = [
{
"logName": "projects/%s/logs/%s" % (self.PROJECT, self.LOGGER_NAME),
Expand All @@ -283,8 +299,12 @@ def test_log_struct_defaults(self):
self.assertEqual(api._write_entries_called_with, (ENTRIES, None, None, None))

def test_log_struct_w_default_labels(self):
from google.cloud.logging_v2.handlers._monitored_resources import (
detect_resource,
)

STRUCT = {"message": "MESSAGE", "weather": "cloudy"}
RESOURCE = {"type": "global", "labels": {}}
RESOURCE = detect_resource(self.PROJECT)._to_dict()
DEFAULT_LABELS = {"foo": "spam"}
ENTRIES = [
{
Expand Down Expand Up @@ -360,6 +380,9 @@ def test_log_struct_w_explicit(self):
self.assertEqual(api._write_entries_called_with, (ENTRIES, None, None, None))

def test_log_proto_defaults(self):
from google.cloud.logging_v2.handlers._monitored_resources import (
detect_resource,
)
import json
from google.protobuf.json_format import MessageToJson
from google.protobuf.struct_pb2 import Struct, Value
Expand All @@ -369,7 +392,7 @@ def test_log_proto_defaults(self):
{
"logName": "projects/%s/logs/%s" % (self.PROJECT, self.LOGGER_NAME),
"protoPayload": json.loads(MessageToJson(message)),
"resource": {"type": "global", "labels": {}},
"resource": detect_resource(self.PROJECT)._to_dict(),
}
]
client = _Client(self.PROJECT)
Expand All @@ -381,6 +404,9 @@ def test_log_proto_defaults(self):
self.assertEqual(api._write_entries_called_with, (ENTRIES, None, None, None))

def test_log_proto_w_default_labels(self):
from google.cloud.logging_v2.handlers._monitored_resources import (
detect_resource,
)
import json
from google.protobuf.json_format import MessageToJson
from google.protobuf.struct_pb2 import Struct, Value
Expand All @@ -391,7 +417,7 @@ def test_log_proto_w_default_labels(self):
{
"logName": "projects/%s/logs/%s" % (self.PROJECT, self.LOGGER_NAME),
"protoPayload": json.loads(MessageToJson(message)),
"resource": {"type": "global", "labels": {}},
"resource": detect_resource(self.PROJECT)._to_dict(),
"labels": DEFAULT_LABELS,
}
]
Expand Down