Coverage for structlog_gcp/processors.py: 100%
36 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-04-02 12:16 +0200
« prev ^ index » next coverage.py v7.2.2, created at 2023-04-02 12:16 +0200
1# https://cloud.google.com/functions/docs/monitoring/logging#writing_structured_logs
2# https://cloud.google.com/logging/docs/agent/logging/configuration#process-payload
3# https://cloud.google.com/logging/docs/structured-logging#special-payload-fields
5import structlog.processors
7from .types import CLOUD_LOGGING_KEY, SOURCE_LOCATION_KEY
10class CoreCloudLogging:
11 """Initialize the Google Cloud Logging event message"""
13 def setup(self):
14 return [
15 # If some value is in bytes, decode it to a unicode str.
16 structlog.processors.UnicodeDecoder(),
17 # Add a timestamp in ISO 8601 format.
18 structlog.processors.TimeStamper(fmt="iso"),
19 self.__call__,
20 ]
22 def __call__(self, logger, method_name, event_dict):
23 value = {
24 "message": event_dict.pop("event"),
25 "time": event_dict.pop("timestamp"),
26 }
28 event_dict[CLOUD_LOGGING_KEY] = value
29 return event_dict
32class FormatAsCloudLogging:
33 """Finalize the Google Cloud Logging event message and replace the logging event"""
35 def setup(self):
36 return [
37 self.__call__,
38 structlog.processors.JSONRenderer(),
39 ]
41 def __call__(self, logger, method_name, event_dict):
42 event = event_dict.pop(CLOUD_LOGGING_KEY)
44 if event_dict:
45 event["logging.googleapis.com/labels"] = event_dict
47 return event
50class LogSeverity:
51 """Set the severity using the Google Cloud Logging severities"""
53 def __init__(self):
54 self.default = "notset"
56 # From Python's logging level to Google level
57 self.mapping = {
58 "notset": "DEFAULT", # The log entry has no assigned severity level.
59 "debug": "DEBUG", # Debug or trace information.
60 "info": "INFO", # Routine information, such as ongoing status or performance.
61 # "notice": "NOTICE", # Normal but significant events, such as start up, shut down, or a configuration change.
62 "warn": "WARNING", # Warning events might cause problems.
63 "warning": "WARNING", # Warning events might cause problems.
64 "error": "ERROR", # Error events are likely to cause problems.
65 "critical": "CRITICAL", # Critical events cause more severe problems or outages.
66 # "alert": "ALERT", # A person must take an action immediately.
67 # "emergency": "EMERGENCY", # One or more systems are unusable.
68 }
70 def setup(self):
71 return [
72 # Add log level to event dict.
73 structlog.processors.add_log_level,
74 self.__call__,
75 ]
77 def __call__(self, logger, method_name, event_dict):
78 """Format a Python log level value as a GCP log severity.
80 See: https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity
81 """
83 log_level = event_dict.pop("level")
84 severity = self.mapping.get(log_level, self.default)
86 event_dict[CLOUD_LOGGING_KEY]["severity"] = severity
87 return event_dict
90class CodeLocation:
91 """Inject the location of the logging message into the logs"""
93 def setup(self):
94 # Add callsite parameters.
95 call_site_proc = structlog.processors.CallsiteParameterAdder(
96 parameters=[
97 structlog.processors.CallsiteParameter.PATHNAME,
98 structlog.processors.CallsiteParameter.MODULE,
99 structlog.processors.CallsiteParameter.FUNC_NAME,
100 structlog.processors.CallsiteParameter.LINENO,
101 ]
102 )
103 return [call_site_proc, self.__call__]
105 def __call__(self, logger, method_name, event_dict):
106 location = {
107 "file": event_dict.pop("pathname"),
108 "line": str(event_dict.pop("lineno")),
109 "function": f"{event_dict.pop('module')}:{event_dict.pop('func_name')}",
110 }
112 event_dict[CLOUD_LOGGING_KEY][SOURCE_LOCATION_KEY] = location
114 return event_dict