def validate_fingerprintingRules(self, attrs, source): if not attrs[source]: return attrs try: FingerprintingRules.from_config_string(attrs[source]) except InvalidFingerprintingConfig as e: raise serializers.ValidationError(e.message) return attrs
def validate_fingerprintingRules(self, value): if not value: return value try: FingerprintingRules.from_config_string(value) except InvalidFingerprintingConfig as e: raise serializers.ValidationError(str(e)) return value
def test_discover_field_parsing(insta_snapshot): rules = FingerprintingRules.from_config_string(""" # This is a config error.type:DatabaseUnavailable -> DatabaseUnavailable stack.function:assertion_failed stack.module:foo -> AssertionFailed, foo app:true -> aha app:true -> {{ default }} """) assert rules._to_config_structure() == { "rules": [ { "matchers": [["type", "DatabaseUnavailable"]], "fingerprint": ["DatabaseUnavailable"] }, { "matchers": [["function", "assertion_failed"], ["module", "foo"]], "fingerprint": ["AssertionFailed", "foo"], }, { "matchers": [["app", "true"]], "fingerprint": ["aha"] }, { "matchers": [["app", "true"]], "fingerprint": ["{{ default }}"] }, ], "version": 1, } assert (FingerprintingRules._from_config_structure( rules._to_config_structure())._to_config_structure() == rules._to_config_structure())
def test_automatic_argument_splitting(): rules = FingerprintingRules.from_config_string(""" logger:test -> logger-{{ logger }} logger:test -> logger-, {{ logger }} logger:test2 -> logger-{{ logger }}-{{ level }} logger:test2 -> logger-, {{ logger }}, -, {{ level }} """) assert rules._to_config_structure() == { "rules": [ { "matchers": [["logger", "test"]], "fingerprint": ["logger-", "{{ logger }}"] }, { "matchers": [["logger", "test"]], "fingerprint": ["logger-", "{{ logger }}"] }, { "matchers": [["logger", "test2"]], "fingerprint": ["logger-", "{{ logger }}", "-", "{{ level }}"], }, { "matchers": [["logger", "test2"]], "fingerprint": ["logger-", "{{ logger }}", "-", "{{ level }}"], }, ], "version": 1, }
def test_basic_parsing(insta_snapshot): rules = FingerprintingRules.from_config_string(''' # This is a config type:DatabaseUnavailable -> DatabaseUnavailable function:assertion_failed module:foo -> AssertionFailed, foo app:true -> aha app:true -> {{ default }} ''') assert rules._to_config_structure() == { 'rules': [ {'matchers': [['type', 'DatabaseUnavailable']], 'fingerprint': ['DatabaseUnavailable']}, {'matchers': [['function', 'assertion_failed'], ['module', 'foo']], 'fingerprint': ['AssertionFailed', 'foo']}, {'matchers': [['app', 'true']], 'fingerprint': ['aha']}, {'matchers': [['app', 'true']], 'fingerprint': ['{{ default }}']}, ], 'version': 1 } assert FingerprintingRules._from_config_structure( rules._to_config_structure())._to_config_structure() == rules._to_config_structure()
def test_basic_parsing(insta_snapshot): rules = FingerprintingRules.from_config_string( """ # This is a config type:DatabaseUnavailable -> DatabaseUnavailable function:assertion_failed module:foo -> AssertionFailed, foo app:true -> aha app:true -> {{ default }} !path:**/foo/** -> everything !"path":**/foo/** -> everything logger:sentry.* -> logger-, {{ logger }} """ ) assert rules._to_config_structure() == { "rules": [ {"matchers": [["type", "DatabaseUnavailable"]], "fingerprint": ["DatabaseUnavailable"]}, { "matchers": [["function", "assertion_failed"], ["module", "foo"]], "fingerprint": ["AssertionFailed", "foo"], }, {"matchers": [["app", "true"]], "fingerprint": ["aha"]}, {"matchers": [["app", "true"]], "fingerprint": ["{{ default }}"]}, {"matchers": [["!path", "**/foo/**"]], "fingerprint": ["everything"]}, {"matchers": [["!path", "**/foo/**"]], "fingerprint": ["everything"]}, {"matchers": [["logger", "sentry.*"]], "fingerprint": ["logger-", "{{ logger }}"]}, ], "version": 1, } assert ( FingerprintingRules._from_config_structure( rules._to_config_structure() )._to_config_structure() == rules._to_config_structure() )
def test_rule_export(): rules = FingerprintingRules.from_config_string(""" logger:sentry.* -> logger, {{ logger }}, title="Message from {{ logger }}" """) assert rules.rules[0].to_json() == { "attributes": { "title": "Message from {{ logger }}" }, "fingerprint": ["logger", "{{ logger }}"], "matchers": [["logger", "sentry.*"]], }
def get_fingerprinting_config_for_project(project): from sentry.grouping.fingerprinting import FingerprintingRules, \ InvalidFingerprintingConfig rules = project.get_option('sentry:fingerprinting_rules') if not rules: return FingerprintingRules([]) from sentry.utils.cache import cache from sentry.utils.hashlib import md5_text cache_key = 'fingerprinting-rules:' + md5_text(rules).hexdigest() rv = cache.get(cache_key) if rv is not None: return FingerprintingRules.from_json(rv) try: rv = FingerprintingRules.from_config_string(rules) except InvalidFingerprintingConfig: rv = FingerprintingRules([]) cache.set(cache_key, rv.to_json()) return rv
def get_fingerprinting_config_for_project(project): from sentry.grouping.fingerprinting import FingerprintingRules, \ InvalidFingerprintingConfig rules = project.get_option('sentry:fingerprinting_rules') if not rules: return FingerprintingRules([]) from sentry.utils.cache import cache from sentry.utils.hashlib import md5_text cache_key = 'fingerprinting-rules:' + md5_text(rules).hexdigest() rv = cache.get(cache_key) if rv is not None: return FingerprintingRules.from_json(rv) try: rv = FingerprintingRules.from_config_string( rules or '') except InvalidFingerprintingConfig: rv = FingerprintingRules([]) cache.set(cache_key, rv.to_json()) return rv
def test_basic_parsing(insta_snapshot): rules = FingerprintingRules.from_config_string(''' # This is a config type:DatabaseUnavailable -> DatabaseUnavailable function:assertion_failed module:foo -> AssertionFailed, foo app:true -> aha ''') assert rules._to_config_structure() == { 'rules': [ {'matchers': [['type', 'DatabaseUnavailable']], 'fingerprint': ['DatabaseUnavailable']}, {'matchers': [['function', 'assertion_failed'], ['module', 'foo']], 'fingerprint': ['AssertionFailed', 'foo']}, {'matchers': [['app', 'true']], 'fingerprint': ['aha']}, ], 'version': 1 } assert FingerprintingRules._from_config_structure( rules._to_config_structure())._to_config_structure() == rules._to_config_structure()
def test_parsing_errors(): with pytest.raises(InvalidFingerprintingConfig): FingerprintingRules.from_config_string("invalid.message:foo -> bar")