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 )