Skip to content

Commit d0a45cb

Browse files
committed
Merge branch 'debug-logging' of github.com:googleapis/python-api-core into debug-logging
2 parents 7b02d26 + 8514389 commit d0a45cb

File tree

2 files changed

+73
-33
lines changed

2 files changed

+73
-33
lines changed

google/api_core/client_logging.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
_LOGGING_INITIALIZED = False
88
_BASE_LOGGER_NAME = "google"
99

10+
# Fields to be included in the StructuredLogFormatter.
11+
#
1012
# TODO(https://github.com/googleapis/python-api-core/issues/761): Update this list to support additional logging fields.
1113
_recognized_logging_fields = [
1214
"httpRequest",
@@ -29,8 +31,21 @@ def logger_configured(logger) -> bool:
2931
)
3032

3133

32-
# TODO(https://github.com/googleapis/python-api-core/issues/763): Add documentation.
34+
# TODO(https://github.com/googleapis/python-api-core/issues/763): Expand documentation.
3335
def initialize_logging():
36+
"""Initializes "google" loggers, partly based on the environment variable
37+
38+
Initializes the "google" logger and any loggers (at the "google"
39+
level or lower) specified by the environment variable
40+
GOOGLE_SDK_PYTHON_LOGGING_SCOPE, as long as none of these loggers
41+
were previously configured. If any such loggers (including the
42+
"google" logger) are initialized, they are set to NOT propagate
43+
log events up to their parent loggers.
44+
45+
This initialization is executed only once, and hence the
46+
environment variable is only processed the first time this
47+
function is called.
48+
"""
3449
global _LOGGING_INITIALIZED
3550
if _LOGGING_INITIALIZED:
3651
return
@@ -39,8 +54,18 @@ def initialize_logging():
3954
_LOGGING_INITIALIZED = True
4055

4156

42-
# TODO(https://github.com/googleapis/python-api-core/issues/763): Add documentation.
57+
# TODO(https://github.com/googleapis/python-api-core/issues/763): Expand documentation.
4358
def parse_logging_scopes(scopes: Optional[str] = None) -> List[str]:
59+
"""Returns a list of logger names.
60+
61+
Splits the single string of comma-separated logger names into a list of individual logger name strings.
62+
63+
Args:
64+
scopes: The name of a single logger. (In the future, this will be a comma-separated list of multiple loggers.)
65+
66+
Returns:
67+
A list of all the logger names in scopes.
68+
"""
4469
if not scopes:
4570
return []
4671
# TODO(https://github.com/googleapis/python-api-core/issues/759): check if the namespace is a valid namespace.
@@ -50,8 +75,9 @@ def parse_logging_scopes(scopes: Optional[str] = None) -> List[str]:
5075
return namespaces
5176

5277

53-
# TODO(https://github.com/googleapis/python-api-core/issues/763): Add documentation.
78+
# TODO(https://github.com/googleapis/python-api-core/issues/763): Expand documentation.
5479
def configure_defaults(logger):
80+
"""Configures `logger` to emit structured info to stdout."""
5581
if not logger_configured(logger):
5682
console_handler = logging.StreamHandler()
5783
logger.setLevel("DEBUG")
@@ -61,8 +87,22 @@ def configure_defaults(logger):
6187
logger.addHandler(console_handler)
6288

6389

64-
# TODO(https://github.com/googleapis/python-api-core/issues/763): Add documentation.
90+
# TODO(https://github.com/googleapis/python-api-core/issues/763): Expand documentation.
6591
def setup_logging(scopes: str=""):
92+
"""Sets up logging for the specified `scopes`.
93+
94+
If the loggers specified in `scopes` have not been previously
95+
configured, this will configure them to emit structured log
96+
entries to stdout, and to not propagate their log events to their
97+
parent loggers. Additionally, if the "google" logger (whether it
98+
was specified in `scopes` or not) was not previously configured,
99+
it will also configure it to not propagate log events to the root
100+
logger.
101+
102+
Args:
103+
scopes: The name of a single logger. (In the future, this will be a comma-separated list of multiple loggers.)
104+
105+
"""
66106

67107
# only returns valid logger scopes (namespaces)
68108
# this list has at most one element.

tests/unit/test_client_logging.py

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,62 +17,62 @@ def reset_logger(scope):
1717

1818

1919
def test_setup_logging_w_no_scopes():
20-
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foo"):
20+
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
2121
setup_logging()
22-
base_logger = logging.getLogger("foo")
22+
base_logger = logging.getLogger("foogle")
2323
assert base_logger.handlers == []
2424
assert not base_logger.propagate
2525
assert base_logger.level == logging.NOTSET
2626

27-
reset_logger("foo")
27+
reset_logger("foogle")
2828

2929

3030
def test_setup_logging_w_base_scope():
31-
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foo"):
32-
setup_logging("foo")
33-
base_logger = logging.getLogger("foo")
31+
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
32+
setup_logging("foogle")
33+
base_logger = logging.getLogger("foogle")
3434
assert isinstance(base_logger.handlers[0], logging.StreamHandler)
3535
assert not base_logger.propagate
3636
assert base_logger.level == logging.DEBUG
3737

38-
reset_logger("foo")
38+
reset_logger("foogle")
3939

4040

4141
def test_setup_logging_w_configured_scope():
42-
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foo"):
43-
base_logger = logging.getLogger("foo")
42+
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
43+
base_logger = logging.getLogger("foogle")
4444
base_logger.propagate = False
45-
setup_logging("foo")
45+
setup_logging("foogle")
4646
assert base_logger.handlers == []
4747
assert not base_logger.propagate
4848
assert base_logger.level == logging.NOTSET
4949

50-
reset_logger("foo")
50+
reset_logger("foogle")
5151

5252

5353
def test_setup_logging_w_module_scope():
54-
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foo"):
55-
setup_logging("foo.bar")
54+
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
55+
setup_logging("foogle.bar")
5656

57-
base_logger = logging.getLogger("foo")
57+
base_logger = logging.getLogger("foogle")
5858
assert base_logger.handlers == []
5959
assert not base_logger.propagate
6060
assert base_logger.level == logging.NOTSET
6161

62-
module_logger = logging.getLogger("foo.bar")
62+
module_logger = logging.getLogger("foogle.bar")
6363
assert isinstance(module_logger.handlers[0], logging.StreamHandler)
6464
assert not module_logger.propagate
6565
assert module_logger.level == logging.DEBUG
6666

67-
reset_logger("foo")
68-
reset_logger("foo.bar")
67+
reset_logger("foogle")
68+
reset_logger("foogle.bar")
6969

7070

7171
def test_setup_logging_w_incorrect_scope():
72-
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foo"):
72+
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
7373
setup_logging("abc")
7474

75-
base_logger = logging.getLogger("foo")
75+
base_logger = logging.getLogger("foogle")
7676
assert base_logger.handlers == []
7777
assert not base_logger.propagate
7878
assert base_logger.level == logging.NOTSET
@@ -83,22 +83,22 @@ def test_setup_logging_w_incorrect_scope():
8383
assert not logger.propagate
8484
assert logger.level == logging.DEBUG
8585

86-
reset_logger("foo")
86+
reset_logger("foogle")
8787
reset_logger("abc")
8888

8989

9090
def test_initialize_logging():
9191

92-
with mock.patch("os.getenv", return_value="foo.bar"):
93-
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foo"):
92+
with mock.patch("os.getenv", return_value="foogle.bar"):
93+
with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
9494
initialize_logging()
9595

96-
base_logger = logging.getLogger("foo")
96+
base_logger = logging.getLogger("foogle")
9797
assert base_logger.handlers == []
9898
assert not base_logger.propagate
9999
assert base_logger.level == logging.NOTSET
100100

101-
module_logger = logging.getLogger("foo.bar")
101+
module_logger = logging.getLogger("foogle.bar")
102102
assert isinstance(module_logger.handlers[0], logging.StreamHandler)
103103
assert not module_logger.propagate
104104
assert module_logger.level == logging.DEBUG
@@ -112,17 +112,17 @@ def test_initialize_logging():
112112
assert base_logger.propagate
113113
assert module_logger.propagate
114114

115-
reset_logger("foo")
116-
reset_logger("foo.bar")
115+
reset_logger("foogle")
116+
reset_logger("foogle.bar")
117117

118118

119119
def test_structured_log_formatter():
120120
# TODO(https://github.com/googleapis/python-api-core/issues/761): Test additional fields when implemented.
121121
record = logging.LogRecord(
122-
name="foo",
122+
name="Appelation",
123123
level=logging.DEBUG,
124124
msg="This is a test message.",
125-
pathname="foo/bar",
125+
pathname="some/path",
126126
lineno=25,
127127
args=None,
128128
exc_info=None,
@@ -134,7 +134,7 @@ def test_structured_log_formatter():
134134
formatted_msg = StructuredLogFormatter().format(record)
135135
parsed_msg = json.loads(formatted_msg)
136136

137-
assert parsed_msg["name"] == "foo"
137+
assert parsed_msg["name"] == "Appelation"
138138
assert parsed_msg["severity"] == "DEBUG"
139139
assert parsed_msg["message"] == "This is a test message."
140140
assert parsed_msg["rpcName"] == "bar"

0 commit comments

Comments
 (0)