Coverage for structlog_gcp/errors.py: 93%
47 statements
« prev ^ index » next coverage.py v7.2.2, created at 2023-04-02 11:04 +0000
« prev ^ index » next coverage.py v7.2.2, created at 2023-04-02 11:04 +0000
1import os
3import structlog
5from .types import CLOUD_LOGGING_KEY, ERROR_EVENT_TYPE, SOURCE_LOCATION_KEY
8def add_service_context(logger, method_name, event_dict):
9 """Add a service context in which an error has occurred.
11 This is part of the Error Reporting API, so it's only added when an error happens.
12 """
14 event_type = event_dict[CLOUD_LOGGING_KEY].get("@type")
15 if event_type != ERROR_EVENT_TYPE:
16 return event_dict
18 service_context = {
19 # https://cloud.google.com/functions/docs/configuring/env-var#runtime_environment_variables_set_automatically
20 "service": os.environ.get("K_SERVICE", "unknown service"),
21 "version": os.environ.get("K_REVISION", "unknown version"),
22 }
24 # https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext
25 event_dict[CLOUD_LOGGING_KEY]["serviceContext"] = service_context
27 return event_dict
30class ReportException:
31 """Transform exception into a Google Cloud Error Reporting event."""
33 # https://cloud.google.com/error-reporting/reference/rest/v1beta1/projects.events/report
34 # https://cloud.google.com/error-reporting/docs/formatting-error-messages#log-entry-examples
36 def __init__(self, log_level="CRITICAL"):
37 self.log_level = log_level
39 def setup(self):
40 return [
41 # structlog.processors.ExceptionRenderer(self.format_exception),
42 structlog.processors.format_exc_info,
43 self.__call__,
44 ]
46 def __call__(self, logger, method_name, event_dict):
47 exception = event_dict.pop("exception", None)
48 if exception is None:
49 return event_dict
51 event_dict[CLOUD_LOGGING_KEY]["@type"] = ERROR_EVENT_TYPE
52 event_dict[CLOUD_LOGGING_KEY]["severity"] = self.log_level
54 # https://cloud.google.com/error-reporting/docs/formatting-error-messages
55 message = event_dict[CLOUD_LOGGING_KEY]["message"]
56 error_message = f"{message}\n{exception}"
57 event_dict[CLOUD_LOGGING_KEY]["stack_trace"] = error_message
59 return event_dict
62class ReportError:
63 """Report to Google Cloud Error Reporting specific log severities"""
65 # https://cloud.google.com/error-reporting/reference/rest/v1beta1/projects.events/report
66 # https://cloud.google.com/error-reporting/docs/formatting-error-messages#log-entry-examples
68 def __init__(self, severities=None):
69 if severities is None: 69 ↛ 70line 69 didn't jump to line 70, because the condition on line 69 was never true
70 severities = []
72 self.severities = severities
74 def setup(self):
75 return [self.__call__]
77 def _build_service_context(self):
78 import os
80 # https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext
81 service_context = {
82 # https://cloud.google.com/functions/docs/configuring/env-var#runtime_environment_variables_set_automatically
83 "service": os.environ.get("K_SERVICE", "unknown service"),
84 "version": os.environ.get("K_REVISION", "unknown version"),
85 }
87 return service_context
89 def __call__(self, logger, method_name, event_dict):
90 severity = event_dict[CLOUD_LOGGING_KEY]["severity"]
92 if severity not in self.severities:
93 return event_dict
95 event_dict[CLOUD_LOGGING_KEY]["@type"] = ERROR_EVENT_TYPE
97 if SOURCE_LOCATION_KEY in event_dict[CLOUD_LOGGING_KEY]: 97 ↛ 104line 97 didn't jump to line 104, because the condition on line 97 was never false
98 # https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorContext
99 error_context = {
100 "reportLocation": event_dict[CLOUD_LOGGING_KEY][SOURCE_LOCATION_KEY],
101 }
102 event_dict[CLOUD_LOGGING_KEY]["context"] = error_context
103 else:
104 event_dict[CLOUD_LOGGING_KEY]["context"] = "no location :("
106 event_dict[CLOUD_LOGGING_KEY]["serviceContext"] = self._build_service_context()
108 return event_dict