diff --git a/tests/test_attributes.py b/tests/test_attributes.py index b4ba2d77a7..40b31fa7f1 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -117,3 +117,41 @@ def test_remove_attribute(sentry_init, capture_envelopes): (metric,) = metrics assert "some.attribute" not in metric["attributes"] + + +def test_scope_attributes_preserialized(sentry_init, capture_envelopes): + def before_send_metric(metric, _): + # Scope attrs show up serialized in before_send + assert isinstance(metric["attributes"]["instance"], str) + assert isinstance(metric["attributes"]["dictionary"], str) + + return metric + + sentry_init(before_send_metric=before_send_metric) + + envelopes = capture_envelopes() + + class Cat: + pass + + instance = Cat() + dictionary = {"color": "tortoiseshell"} + + with sentry_sdk.new_scope() as scope: + scope.set_attribute("instance", instance) + scope.set_attribute("dictionary", dictionary) + + # Scope attrs are stored preserialized + assert isinstance(scope._attributes["instance"], str) + assert isinstance(scope._attributes["dictionary"], str) + + sentry_sdk.metrics.count("test", 1) + + sentry_sdk.get_client().flush() + + metrics = envelopes_to_metrics(envelopes) + (metric,) = metrics + + # Attrs originally from the scope are serialized when sent + assert isinstance(metric["attributes"]["instance"], str) + assert isinstance(metric["attributes"]["dictionary"], str) diff --git a/tests/test_logs.py b/tests/test_logs.py index 3b60651631..f2ddab7888 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -597,3 +597,40 @@ def test_log_attributes_override_scope_attributes(sentry_init, capture_envelopes assert log["attributes"]["durable.attribute"] == "value1" assert log["attributes"]["temp.attribute"] == "value2" + + +@minimum_python_37 +def test_attributes_preserialized_in_before_send(sentry_init, capture_envelopes): + """We don't surface references to objects in attributes.""" + + def before_send_log(log, _): + assert isinstance(log["attributes"]["instance"], str) + assert isinstance(log["attributes"]["dictionary"], str) + + return log + + sentry_init(enable_logs=True, before_send_log=before_send_log) + + envelopes = capture_envelopes() + + class Cat: + pass + + instance = Cat() + dictionary = {"color": "tortoiseshell"} + + sentry_sdk.logger.warning( + "Hello world!", + attributes={ + "instance": instance, + "dictionary": dictionary, + }, + ) + + get_client().flush() + + logs = envelopes_to_logs(envelopes) + (log,) = logs + + assert isinstance(log["attributes"]["instance"], str) + assert isinstance(log["attributes"]["dictionary"], str) diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 240ed18a37..d64ce748e4 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -335,3 +335,40 @@ def test_metric_attributes_override_scope_attributes(sentry_init, capture_envelo assert metric["attributes"]["durable.attribute"] == "value1" assert metric["attributes"]["temp.attribute"] == "value2" + + +def test_attributes_preserialized_in_before_send(sentry_init, capture_envelopes): + """We don't surface references to objects in attributes.""" + + def before_send_metric(metric, _): + assert isinstance(metric["attributes"]["instance"], str) + assert isinstance(metric["attributes"]["dictionary"], str) + + return metric + + sentry_init(before_send_metric=before_send_metric) + + envelopes = capture_envelopes() + + class Cat: + pass + + instance = Cat() + dictionary = {"color": "tortoiseshell"} + + sentry_sdk.metrics.count( + "test.counter", + 1, + attributes={ + "instance": instance, + "dictionary": dictionary, + }, + ) + + get_client().flush() + + metrics = envelopes_to_metrics(envelopes) + (metric,) = metrics + + assert isinstance(metric["attributes"]["instance"], str) + assert isinstance(metric["attributes"]["dictionary"], str)