|
| 1 | +from typing import Dict |
| 2 | + |
| 3 | +import os |
| 4 | +import re |
| 5 | +from abc import ABC |
| 6 | + |
| 7 | +import sentry_sdk |
| 8 | +from sentry_sdk.integrations.excepthook import ExcepthookIntegration |
| 9 | +from sentry_sdk.scrubber import DEFAULT_DENYLIST |
| 10 | + |
| 11 | +WHITELISTED_MODULES = ["giskard", "pandas", "numpy"] |
| 12 | +DISABLE_SENTRY_ENV_VARIABLE_NAME = "GSK_DISABLE_SENTRY" |
| 13 | + |
| 14 | + |
| 15 | +def _lower_text_only_var_name(var_name: str): |
| 16 | + # Remove underscore and digits and put to lower case |
| 17 | + return re.sub(r"[0-9_]+", "", var_name.lower()) |
| 18 | + |
| 19 | + |
| 20 | +SENSITIVE_VAR_NAME = [ |
| 21 | + _lower_text_only_var_name(var_name) for var_name in DEFAULT_DENYLIST + ["df", "batch", "requirement"] |
| 22 | +] |
| 23 | + |
| 24 | + |
| 25 | +def _scrub_nonsensitive_frame(frame): |
| 26 | + if "vars" in frame: |
| 27 | + frame["vars"] = { |
| 28 | + var_name: "[Filtered]" if _lower_text_only_var_name(var_name) in SENSITIVE_VAR_NAME else value |
| 29 | + for var_name, value in frame["vars"].items() |
| 30 | + } |
| 31 | + |
| 32 | + return frame |
| 33 | + |
| 34 | + |
| 35 | +def _scrub_sensitive_frame(frame): |
| 36 | + if "vars" in frame: |
| 37 | + frame["vars"] = {var_name: "[Filtered]" for var_name in frame["vars"].keys()} |
| 38 | + |
| 39 | + # Anonymize code |
| 40 | + frame["pre_context"] = ["[filtered_pre_context]"] |
| 41 | + frame["context_line"] = ["[filtered_context_line]"] |
| 42 | + frame["post_context"] = ["[filtered_pre_context]"] |
| 43 | + |
| 44 | + return frame |
| 45 | + |
| 46 | + |
| 47 | +def scrub_frame(frame): |
| 48 | + is_nonsensitive = ( |
| 49 | + "module" in frame and frame["module"] is not None and frame["module"].split(".")[0] in WHITELISTED_MODULES |
| 50 | + ) |
| 51 | + |
| 52 | + return _scrub_nonsensitive_frame(frame) if is_nonsensitive else _scrub_sensitive_frame(frame) |
| 53 | + |
| 54 | + |
| 55 | +def scrub_stacktrace(stacktrace): |
| 56 | + if "frames" not in stacktrace: |
| 57 | + return stacktrace |
| 58 | + |
| 59 | + stacktrace["frames"] = [scrub_frame(frame) for frame in stacktrace["frames"]] |
| 60 | + |
| 61 | + return stacktrace |
| 62 | + |
| 63 | + |
| 64 | +def scrub_event(event, _hint) -> Dict[str, ABC]: |
| 65 | + if "exception" not in event: |
| 66 | + return event |
| 67 | + |
| 68 | + exception = event["exception"] |
| 69 | + if "values" not in exception: |
| 70 | + return event |
| 71 | + |
| 72 | + for value in exception["values"]: |
| 73 | + if "stacktrace" in value: |
| 74 | + value["stacktrace"] = scrub_stacktrace(value["stacktrace"]) |
| 75 | + |
| 76 | + return event |
| 77 | + |
| 78 | + |
| 79 | +def configure_sentry(): |
| 80 | + if os.getenv(DISABLE_SENTRY_ENV_VARIABLE_NAME, "False").lower() in ("true", "1", "yes", "t", "y"): |
| 81 | + return None |
| 82 | + |
| 83 | + sentry_sdk.init( |
| 84 | + # DSN is safe to be publicly available: https://docs.sentry.io/product/sentry-basics/concepts/dsn-explainer/ |
| 85 | + dsn="https://a5d33bfa91bc3da9af2e7d32e19ff89d@o4505952637943808.ingest.sentry.io/4506789759025152", |
| 86 | + enable_tracing=True, |
| 87 | + integrations=[ExcepthookIntegration(always_run=True)], |
| 88 | + before_send=scrub_event, |
| 89 | + ) |
0 commit comments