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

1import os 

2 

3import structlog 

4 

5from .types import CLOUD_LOGGING_KEY, ERROR_EVENT_TYPE, SOURCE_LOCATION_KEY 

6 

7 

8def add_service_context(logger, method_name, event_dict): 

9 """Add a service context in which an error has occurred. 

10 

11 This is part of the Error Reporting API, so it's only added when an error happens. 

12 """ 

13 

14 event_type = event_dict[CLOUD_LOGGING_KEY].get("@type") 

15 if event_type != ERROR_EVENT_TYPE: 

16 return event_dict 

17 

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 } 

23 

24 # https://cloud.google.com/error-reporting/reference/rest/v1beta1/ServiceContext 

25 event_dict[CLOUD_LOGGING_KEY]["serviceContext"] = service_context 

26 

27 return event_dict 

28 

29 

30class ReportException: 

31 """Transform exception into a Google Cloud Error Reporting event.""" 

32 

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 

35 

36 def __init__(self, log_level="CRITICAL"): 

37 self.log_level = log_level 

38 

39 def setup(self): 

40 return [ 

41 # structlog.processors.ExceptionRenderer(self.format_exception), 

42 structlog.processors.format_exc_info, 

43 self.__call__, 

44 ] 

45 

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 

50 

51 event_dict[CLOUD_LOGGING_KEY]["@type"] = ERROR_EVENT_TYPE 

52 event_dict[CLOUD_LOGGING_KEY]["severity"] = self.log_level 

53 

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 

58 

59 return event_dict 

60 

61 

62class ReportError: 

63 """Report to Google Cloud Error Reporting specific log severities""" 

64 

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 

67 

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 = [] 

71 

72 self.severities = severities 

73 

74 def setup(self): 

75 return [self.__call__] 

76 

77 def _build_service_context(self): 

78 import os 

79 

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 } 

86 

87 return service_context 

88 

89 def __call__(self, logger, method_name, event_dict): 

90 severity = event_dict[CLOUD_LOGGING_KEY]["severity"] 

91 

92 if severity not in self.severities: 

93 return event_dict 

94 

95 event_dict[CLOUD_LOGGING_KEY]["@type"] = ERROR_EVENT_TYPE 

96 

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 :(" 

105 

106 event_dict[CLOUD_LOGGING_KEY]["serviceContext"] = self._build_service_context() 

107 

108 return event_dict