1+ import logging
2+ import json
3+ import re
4+ import os
5+
6+ LOGGING_INITIALIZED = False
7+
8+ # TODO(<add-link>): Update Request / Response messages.
9+ REQUEST_MESSAGE = "Sending request ..."
10+ RESPONSE_MESSAGE = "Receiving response ..."
11+
12+ # TODO(<add-link>): Update this list to support additional logging fields
13+ _recognized_logging_fields = ["httpRequest" , "rpcName" , "serviceName" ] # Additional fields to be Logged.
14+
15+ def logger_configured (logger ):
16+ return logger .hasHandlers () or logger .level != logging .NOTSET
17+
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
25+
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
33+
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.
45+ base_logger = logging .getLogger ("google" )
46+ if not logger_configured (base_logger ):
47+ base_logger .propagate = False
48+
49+ # only returns valid logger scopes (namespaces)
50+ # this list has at most one element.
51+ loggers = parse_logging_scopes (scopes )
52+
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 )
56+
57+ # Set default settings.
58+ default_settings (logger )
59+
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