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_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_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 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 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 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_event_hash_variant(insta_snapshot, testcase): with open(os.path.join(_fixture_path, testcase + '.json')) as f: input = json.load(f) config = FingerprintingRules.from_json({ 'rules': input.pop('_fingerprinting_rules'), 'version': 1, }) mgr = EventManager(data=input) mgr.normalize() data = mgr.get_data() data.setdefault('fingerprint', ['{{ default }}']) apply_server_fingerprinting(data, config) evt = Event(data=data, platform=data['platform']) def dump_variant(v): rv = v.as_dict() for key in 'component', 'description', 'hash', 'config': rv.pop(key, None) return rv insta_snapshot({ 'config': config.to_json(), 'fingerprint': data['fingerprint'], 'variants': {k: dump_variant(v) for (k, v) in evt.get_grouping_variants().items()}, })
def test_event_hash_variant(insta_snapshot, testcase): with open(os.path.join(_fixture_path, testcase + ".json")) as f: input = json.load(f) config = FingerprintingRules.from_json( {"rules": input.pop("_fingerprinting_rules"), "version": 1} ) mgr = EventManager(data=input) mgr.normalize() data = mgr.get_data() data.setdefault("fingerprint", ["{{ default }}"]) apply_server_fingerprinting(data, config) evt = Event(data=data, platform=data["platform"]) def dump_variant(v): rv = v.as_dict() for key in "component", "description", "hash", "config": rv.pop(key, None) return rv insta_snapshot( { "config": config.to_json(), "fingerprint": data["fingerprint"], "variants": {k: dump_variant(v) for (k, v) in evt.get_grouping_variants().items()}, } )
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 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 create_event(self, grouping_config=None): input = dict(self.data) config = FingerprintingRules.from_json( {"rules": input.pop("_fingerprinting_rules"), "version": 1} ) mgr = EventManager(data=input, grouping_config=grouping_config) mgr.normalize() data = mgr.get_data() data.setdefault("fingerprint", ["{{ default }}"]) apply_server_fingerprinting(data, config) data.update(materialize_metadata(data)) evt = eventstore.create_event(data=data) return config, evt
def test_event_hash_variant(insta_snapshot, testcase): with open(os.path.join(_fixture_path, testcase + '.json')) as f: input = json.load(f) config = FingerprintingRules.from_json({ 'rules': input.pop('_fingerprinting_rules'), 'version': 1, }) mgr = EventManager(data=input) mgr.normalize() data = mgr.get_data() data.setdefault('fingerprint', ['{{ default }}']) apply_server_fingerprinting(data, config) insta_snapshot({ 'config': config.to_json(), 'fingerprint': data['fingerprint'], })
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 test_parsing_errors(): with pytest.raises(InvalidFingerprintingConfig): FingerprintingRules.from_config_string("invalid.message:foo -> bar")