Skip to content

Commit f8cb7fc

Browse files
committed
updates as per new design
1 parent 08ef13a commit f8cb7fc

File tree

1 file changed

+58
-69
lines changed

1 file changed

+58
-69
lines changed

google/api_core/client_logging.py

Lines changed: 58 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,73 @@
11
import logging
22
import json
33
import re
4+
import os
45

5-
# NOTE: We can essentially just use `json.dumps` and make structured logging our default format.
6-
class DefaultFormatter(logging.Formatter):
7-
def format(self, record):
8-
# Base format with time, severity, and main message
9-
# base_message = f"{self.formatTime(record)} - {record.levelname} - {record.name} - {record.getMessage()}"
10-
base_message = super().format(record)
11-
12-
# Add any extra fields below:
13-
extra_info = []
14-
if hasattr(record, 'client_id'):
15-
extra_info.append(f"client_id={record.client_id}")
16-
if hasattr(record, 'response_id'):
17-
extra_info.append(f"client_id={record.client_id}")
18-
if hasattr(record, 'httpRequest'):
19-
extra_info.append(f"httpRequest={record.httpRequest}")
20-
if hasattr(record, 'json_fields'):
21-
for key, val in record.json_fields.items():
22-
extra_info.append(f"{key}={val}")
23-
24-
# Combine base message with extra fields
25-
if extra_info:
26-
base_message += " | " + " - ".join(extra_info)
27-
28-
return base_message
6+
LOGGING_INITIALIZED = False
297

8+
# TODO(<add-link>): Update Request / Response messages.
9+
REQUEST_MESSAGE = "Sending request ..."
10+
RESPONSE_MESSAGE = "Receiving response ..."
3011

31-
# NOTE: Option 1: Allow users to configure log levels.
32-
# def setup_logging(log_level, namespace="google"):
33-
34-
# # NOTE: A logger with namespace="google" is only configured if all of the below conditions hold true:
35-
# # - A root logger is not configured.
36-
# # - N/A: A logger with namespace="google" is not already configured (This statement is removed.)
37-
# # - GOOGLE_SDK_PYTHON_LOGGING_LEVEL is set.
38-
# if not logging.getLogger().hasHandlers() and log_level:
12+
# TODO(<add-link>): Update this list to support additional logging fields
13+
_recognized_logging_fields = ["httpRequest", "rpcName", "serviceName"] # Additional fields to be Logged.
3914

40-
# # define a module for our repositories
41-
# logger = logging.getLogger(namespace)
42-
# try:
43-
# logger.setLevel(log_level)
44-
# except ValueError:
45-
# logger.setLevel("WARNING")
46-
# logger.warning(f"Configured log level `{log_level}` is incorrect. Defaulting to WARNING.")
15+
def logger_configured(logger):
16+
return logger.hasHandlers() or logger.level != logging.NOTSET
4717

48-
# # Default settings
49-
# console_handler = logging.StreamHandler()
50-
# formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
51-
# console_handler.setFormatter(formatter)
52-
# logger.addHandler(console_handler)
18+
def initialize_logging():
19+
global LOGGING_INITIALIZED
20+
if LOGGING_INITIALIZED:
21+
return
22+
scopes = os.getenv("GOOGLE_SDK_PYTHON_LOGGING_SCOPE")
23+
setup_logging(scopes)
24+
LOGGING_INITIALIZED = True
5325

26+
def parse_logging_scopes(scopes):
27+
if not scopes:
28+
return []
29+
# TODO(<add-link>): check if the namespace is a valid namespace.
30+
# TODO(<add-link>): parse a list of namespaces. Current flow expects a single string for now.
31+
namespaces = [scopes]
32+
return namespaces
5433

55-
# NOTE: Option 2: Allow users to configure log systems.
56-
def setup_logging(namespace):
57-
58-
# Instantiate a base logger unconditionally to avoid propogating logs to the root logger.
34+
def default_settings(logger):
35+
if not logger_configured(logger):
36+
console_handler = logging.StreamHandler()
37+
logger.setLevel("DEBUG")
38+
logger.propagate = False
39+
formatter = StructuredLogFormatter()
40+
console_handler.setFormatter(formatter)
41+
logger.addHandler(console_handler)
42+
43+
def setup_logging(scopes):
44+
# disable log propagation at base logger level to the root logger only if a base logger is not already configured via code changes.
5945
base_logger = logging.getLogger("google")
60-
base_logger.propagate = False
46+
if not logger_configured(base_logger):
47+
base_logger.propagate = False
6148

62-
# If a namespace is not provided, we don't need to do anything.
63-
if not namespace:
64-
return
49+
# only returns valid logger scopes (namespaces)
50+
# this list has at most one element.
51+
loggers = parse_logging_scopes(scopes)
6552

66-
# If the provided namespace does not start with "google", we don't need to do anything.
67-
# We can update the regex to be more strict about the format of the namespace.
68-
match = re.search(f"google", namespace)
69-
if not match:
70-
# TODO: raise an error? silently ignore?
71-
return
72-
53+
for namespace in loggers:
54+
# This will either create a module level logger or get the reference of the base logger instantiated above.
55+
logger = logging.getLogger(namespace)
7356

74-
# This will either create a module level logger or get the reference of the base logger instantiated above.
75-
logger = logging.getLogger(namespace)
57+
# Set default settings.
58+
default_settings(logger)
7659

77-
# Set default settings.
78-
if not logger.hasHandlers() and logger.level == logging.NOTSET:
79-
console_handler = logging.StreamHandler()
80-
logger.setLevel("DEBUG")
81-
formatter = DefaultFormatter('%(asctime)s %(levelname)s %(name)s %(message)s')
82-
# formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
83-
console_handler.setFormatter(formatter)
84-
logger.addHandler(console_handler)
60+
class StructuredLogFormatter(logging.Formatter):
61+
def format(self, record):
62+
log_obj = {
63+
'timestamp': self.formatTime(record),
64+
'severity': record.levelname,
65+
'name': record.name,
66+
'message': record.getMessage(),
67+
}
68+
69+
for field_name in _recognized_logging_fields:
70+
value = getattr(record, field_name, None)
71+
if value is not None:
72+
log_obj[field_name] = value
73+
return json.dumps(log_obj)

0 commit comments

Comments
 (0)