Coverage for benefits / sentry.py: 80%

55 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-01 15:39 +0000

1import datetime 

2import os 

3import shutil 

4import subprocess 

5 

6import sentry_sdk 

7from sentry_sdk.integrations.django import DjangoIntegration 

8from sentry_sdk.scrubber import DEFAULT_DENYLIST, EventScrubber 

9 

10from benefits import VERSION 

11 

12SENTRY_CSP_REPORT_URI = None 

13 

14 

15def git_available(): 

16 return bool(shutil.which("git")) 

17 

18 

19# https://stackoverflow.com/a/24584384/358804 

20def is_git_directory(path="."): 

21 with open(os.devnull, "w") as dev_null: 

22 return subprocess.call(["git", "-C", path, "status"], stderr=dev_null, stdout=dev_null) == 0 

23 

24 

25# https://stackoverflow.com/a/21901260/358804 

26def get_git_revision_hash(): 

27 return subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("ascii").strip() 

28 

29 

30def get_sha_file_path(): 

31 current_file = os.path.dirname(os.path.abspath(__file__)) 

32 return os.path.join(current_file, "..", "static", "sha.txt") 

33 

34 

35def get_sha_from_file(): 

36 sha_path = get_sha_file_path() 

37 if os.path.isfile(sha_path): 

38 with open(sha_path) as f: 

39 return f.read().strip() 

40 else: 

41 return None 

42 

43 

44def get_release() -> str: 

45 """Returns the first available: the SHA from Git, the value from sha.txt, or the VERSION.""" 

46 

47 if git_available() and is_git_directory(): 

48 return get_git_revision_hash() 

49 else: 

50 sha = get_sha_from_file() 

51 if sha: 

52 return sha 

53 else: 

54 # one of the above *should* always be available, but including this just in case 

55 return VERSION 

56 

57 

58def get_denylist(): 

59 # custom denylist 

60 denylist = DEFAULT_DENYLIST + ["sub", "name"] 

61 return denylist 

62 

63 

64def get_traces_sample_rate(): 

65 try: 

66 rate = float(os.environ.get("SENTRY_TRACES_SAMPLE_RATE", "0.0")) 

67 if rate < 0.0 or rate > 1.0: 

68 print( 

69 f"[{datetime.datetime.now().strftime("%d/%b/%Y %H:%M:%S")}] " 

70 "WARNING benefits.sentry SENTRY_TRACES_SAMPLE_RATE was not in the range [0.0, 1.0], defaulting to 0.0" 

71 ) 

72 rate = 0.0 

73 else: 

74 print( 

75 f"[{datetime.datetime.now().strftime("%d/%b/%Y %H:%M:%S")}] " 

76 f"INFO benefits.sentry SENTRY_TRACES_SAMPLE_RATE set to: {rate}" 

77 ) 

78 except ValueError: 

79 print( 

80 f"[{datetime.datetime.now().strftime("%d/%b/%Y %H:%M:%S")}] " 

81 "WARNING benefits.sentry SENTRY_TRACES_SAMPLE_RATE did not parse to float, defaulting to 0.0" 

82 ) 

83 rate = 0.0 

84 

85 return rate 

86 

87 

88def configure(runtime_environment): 

89 sentry_dsn = os.environ.get("SENTRY_DSN") 

90 sentry_environment = os.environ.get("SENTRY_ENVIRONMENT", runtime_environment) 

91 

92 if sentry_dsn: 92 ↛ 93line 92 didn't jump to line 93 because the condition on line 92 was never true

93 release = get_release() 

94 print( 

95 f"[{datetime.datetime.now().strftime("%d/%b/%Y %H:%M:%S")}] " 

96 f"INFO benefits.sentry Enabling Sentry for environment '{sentry_environment}', " 

97 f"release '{release}'..." 

98 ) 

99 

100 # https://docs.sentry.io/platforms/python/configuration/ 

101 sentry_sdk.init( 

102 dsn=sentry_dsn, 

103 integrations=[ 

104 DjangoIntegration(), 

105 ], 

106 traces_sample_rate=get_traces_sample_rate(), 

107 environment=sentry_environment, 

108 release=release, 

109 in_app_include=["benefits"], 

110 # send_default_pii must be False (the default) for a custom EventScrubber/denylist 

111 # https://docs.sentry.io/platforms/python/data-management/sensitive-data/#event_scrubber 

112 send_default_pii=False, 

113 event_scrubber=EventScrubber(denylist=get_denylist(), recursive=True), 

114 ) 

115 

116 # override the module-level variable when configuration happens, if set 

117 global SENTRY_CSP_REPORT_URI 

118 SENTRY_CSP_REPORT_URI = os.environ.get("SENTRY_REPORT_URI", "") 

119 else: 

120 print( 

121 f"[{datetime.datetime.now().strftime("%d/%b/%Y %H:%M:%S")}] " 

122 "INFO benefits.sentry SENTRY_DSN not set, so won't send events" 

123 )