Skip to content

fix: Ensure tags values are strings #4459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
event_from_exception,
exc_info_from_error,
logger,
safe_str,
)

import typing
Expand Down Expand Up @@ -1421,7 +1422,9 @@ def _apply_extra_to_event(self, event, hint, options):
def _apply_tags_to_event(self, event, hint, options):
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
if self._tags:
event.setdefault("tags", {}).update(self._tags)
event.setdefault("tags", {}).update(
{k: safe_str(v) for k, v in self._tags.items()}
)

def _apply_contexts_to_event(self, event, hint, options):
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
Expand Down
7 changes: 4 additions & 3 deletions sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
logger,
nanosecond_time,
should_be_treated_as_error,
safe_str,
)

from typing import TYPE_CHECKING
Expand Down Expand Up @@ -313,7 +314,7 @@ def __init__(
self.scope = scope
self.origin = origin
self._measurements = {} # type: Dict[str, MeasurementValue]
self._tags = {} # type: MutableMapping[str, str]
self._tags = {} # type: MutableMapping[str, object]
self._data = {} # type: Dict[str, Any]
self._containing_transaction = containing_transaction
self._flags = {} # type: Dict[str, bool]
Expand Down Expand Up @@ -718,7 +719,7 @@ def to_json(self):

tags = self._tags
if tags:
rv["tags"] = tags
rv["tags"] = {k: safe_str(v) for k, v in tags.items()}

data = {}
data.update(self._flags)
Expand Down Expand Up @@ -1045,7 +1046,7 @@ def finish(
"transaction": self.name,
"transaction_info": {"source": self.source},
"contexts": contexts,
"tags": self._tags,
"tags": {k: safe_str(v) for k, v in self._tags.items()},
"timestamp": self.timestamp,
"start_timestamp": self.start_timestamp,
"spans": finished_spans,
Expand Down
8 changes: 4 additions & 4 deletions tests/integrations/aws_lambda/test_aws_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,10 @@ def test_non_dict_event(
assert transaction_event["request"] == request_data

if batch_size > 1:
assert error_event["tags"]["batch_size"] == batch_size
assert error_event["tags"]["batch_request"] is True
assert transaction_event["tags"]["batch_size"] == batch_size
assert transaction_event["tags"]["batch_request"] is True
assert error_event["tags"]["batch_size"] == str(batch_size)
assert error_event["tags"]["batch_request"] == "True"
assert transaction_event["tags"]["batch_size"] == str(batch_size)
assert transaction_event["tags"]["batch_request"] == "True"


def test_request_data(lambda_client, test_environment):
Expand Down
4 changes: 2 additions & 2 deletions tests/integrations/redis/asyncio/test_redis_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ async def test_async_redis_pipeline(
}
)
assert span["tags"] == {
"redis.transaction": is_transaction,
"redis.is_cluster": False,
"redis.transaction": str(is_transaction),
"redis.is_cluster": "False",
}


Expand Down
6 changes: 3 additions & 3 deletions tests/integrations/redis/cluster/test_redis_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def test_rediscluster_basic(sentry_init, capture_events, send_default_pii, descr
assert span["tags"] == {
"db.operation": "SET",
"redis.command": "SET",
"redis.is_cluster": True,
"redis.is_cluster": "True",
"redis.key": "bar",
}

Expand Down Expand Up @@ -141,8 +141,8 @@ def test_rediscluster_pipeline(
}
)
assert span["tags"] == {
"redis.transaction": False, # For Cluster, this is always False
"redis.is_cluster": True,
"redis.transaction": "False", # For Cluster, this is always False
"redis.is_cluster": "True",
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ async def test_async_basic(sentry_init, capture_events, send_default_pii, descri
}
)
assert span["tags"] == {
"redis.is_cluster": True,
"redis.is_cluster": "True",
"db.operation": "SET",
"redis.command": "SET",
"redis.key": "bar",
Expand Down Expand Up @@ -144,8 +144,8 @@ async def test_async_redis_pipeline(
}
)
assert span["tags"] == {
"redis.transaction": False,
"redis.is_cluster": True,
"redis.transaction": "False",
"redis.is_cluster": "True",
}


Expand Down
4 changes: 2 additions & 2 deletions tests/integrations/redis/test_redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ def test_redis_pipeline(
"first_ten": expected_first_ten,
}
assert span["tags"] == {
"redis.transaction": is_transaction,
"redis.is_cluster": False,
"redis.transaction": str(is_transaction),
"redis.is_cluster": "False",
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ def test_rediscluster_pipeline(
}
)
assert span["tags"] == {
"redis.transaction": False, # For Cluster, this is always False
"redis.is_cluster": True,
"redis.transaction": "False", # For Cluster, this is always False
"redis.is_cluster": "True",
}


Expand Down
82 changes: 48 additions & 34 deletions tests/new_scopes_compat/test_new_scopes_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ def test_configure_scope_sdk1(sentry_init, capture_events):
(event_a, event_b, event_z) = events

# Check against the results the same code returned in SDK 1.x
assert event_a["tags"] == {"A": 1}
assert event_b["tags"] == {"A": 1, "B1": 1, "B2": 1}
assert event_z["tags"] == {"A": 1, "B1": 1, "B2": 1, "Z": 1}
assert event_a["tags"] == {"A": "1"}
assert event_b["tags"] == {"A": "1", "B1": "1", "B2": "1"}
assert event_z["tags"] == {"A": "1", "B1": "1", "B2": "1", "Z": "1"}


def test_push_scope_sdk1(sentry_init, capture_events):
Expand All @@ -64,9 +64,9 @@ def test_push_scope_sdk1(sentry_init, capture_events):
(event_a, event_b, event_z) = events

# Check against the results the same code returned in SDK 1.x
assert event_a["tags"] == {"A": 1}
assert event_b["tags"] == {"A": 1, "B1": 1, "B2": 1}
assert event_z["tags"] == {"A": 1, "Z": 1}
assert event_a["tags"] == {"A": "1"}
assert event_b["tags"] == {"A": "1", "B1": "1", "B2": "1"}
assert event_z["tags"] == {"A": "1", "Z": "1"}


def test_with_hub_sdk1(sentry_init, capture_events):
Expand All @@ -93,9 +93,9 @@ def test_with_hub_sdk1(sentry_init, capture_events):
(event_a, event_b, event_z) = events

# Check against the results the same code returned in SDK 1.x
assert event_a["tags"] == {"A": 1}
assert event_b["tags"] == {"A": 1, "B1": 1, "B2": 1}
assert event_z["tags"] == {"A": 1, "B1": 1, "B2": 1, "Z": 1}
assert event_a["tags"] == {"A": "1"}
assert event_b["tags"] == {"A": "1", "B1": "1", "B2": "1"}
assert event_z["tags"] == {"A": "1", "B1": "1", "B2": "1", "Z": "1"}


def test_with_hub_configure_scope_sdk1(sentry_init, capture_events):
Expand Down Expand Up @@ -127,17 +127,24 @@ def test_with_hub_configure_scope_sdk1(sentry_init, capture_events):
(event_a, event_b, event_c, event_z) = events

# Check against the results the same code returned in SDK 1.x
assert event_a["tags"] == {"A": 1}
assert event_b["tags"] == {"A": 1, "B1": 1, "B2": 1, "B3": 1, "B4": 1}
assert event_c["tags"] == {"A": 1, "B1": 1, "B2": 1, "B3": 1, "B4": 1, "B5": 1}
assert event_a["tags"] == {"A": "1"}
assert event_b["tags"] == {"A": "1", "B1": "1", "B2": "1", "B3": "1", "B4": "1"}
assert event_c["tags"] == {
"A": "1",
"B1": "1",
"B2": "1",
"B3": "1",
"B4": "1",
"B5": "1",
}
assert event_z["tags"] == {
"A": 1,
"B1": 1,
"B2": 1,
"B3": 1,
"B4": 1,
"B5": 1,
"Z": 1,
"A": "1",
"B1": "1",
"B2": "1",
"B3": "1",
"B4": "1",
"B5": "1",
"Z": "1",
}


Expand Down Expand Up @@ -170,10 +177,10 @@ def test_with_hub_push_scope_sdk1(sentry_init, capture_events):
(event_a, event_b, event_c, event_z) = events

# Check against the results the same code returned in SDK 1.x
assert event_a["tags"] == {"A": 1}
assert event_b["tags"] == {"A": 1, "B1": 1, "B2": 1, "B3": 1, "B4": 1}
assert event_c["tags"] == {"A": 1, "B1": 1, "B5": 1}
assert event_z["tags"] == {"A": 1, "B1": 1, "B5": 1, "Z": 1}
assert event_a["tags"] == {"A": "1"}
assert event_b["tags"] == {"A": "1", "B1": "1", "B2": "1", "B3": "1", "B4": "1"}
assert event_c["tags"] == {"A": "1", "B1": "1", "B5": "1"}
assert event_z["tags"] == {"A": "1", "B1": "1", "B5": "1", "Z": "1"}


def test_with_cloned_hub_sdk1(sentry_init, capture_events):
Expand All @@ -200,9 +207,9 @@ def test_with_cloned_hub_sdk1(sentry_init, capture_events):
(event_a, event_b, event_z) = events

# Check against the results the same code returned in SDK 1.x
assert event_a["tags"] == {"A": 1}
assert event_b["tags"] == {"A": 1, "B1": 1, "B2": 1}
assert event_z["tags"] == {"A": 1, "Z": 1}
assert event_a["tags"] == {"A": "1"}
assert event_b["tags"] == {"A": "1", "B1": "1", "B2": "1"}
assert event_z["tags"] == {"A": "1", "Z": "1"}


def test_with_cloned_hub_configure_scope_sdk1(sentry_init, capture_events):
Expand Down Expand Up @@ -234,10 +241,17 @@ def test_with_cloned_hub_configure_scope_sdk1(sentry_init, capture_events):
(event_a, event_b, event_c, event_z) = events

# Check against the results the same code returned in SDK 1.x
assert event_a["tags"] == {"A": 1}
assert event_b["tags"] == {"A": 1, "B1": 1, "B2": 1, "B3": 1, "B4": 1}
assert event_c["tags"] == {"A": 1, "B1": 1, "B2": 1, "B3": 1, "B4": 1, "B5": 1}
assert event_z["tags"] == {"A": 1, "Z": 1}
assert event_a["tags"] == {"A": "1"}
assert event_b["tags"] == {"A": "1", "B1": "1", "B2": "1", "B3": "1", "B4": "1"}
assert event_c["tags"] == {
"A": "1",
"B1": "1",
"B2": "1",
"B3": "1",
"B4": "1",
"B5": "1",
}
assert event_z["tags"] == {"A": "1", "Z": "1"}


def test_with_cloned_hub_push_scope_sdk1(sentry_init, capture_events):
Expand Down Expand Up @@ -269,7 +283,7 @@ def test_with_cloned_hub_push_scope_sdk1(sentry_init, capture_events):
(event_a, event_b, event_c, event_z) = events

# Check against the results the same code returned in SDK 1.x
assert event_a["tags"] == {"A": 1}
assert event_b["tags"] == {"A": 1, "B1": 1, "B2": 1, "B3": 1, "B4": 1}
assert event_c["tags"] == {"A": 1, "B1": 1, "B5": 1}
assert event_z["tags"] == {"A": 1, "Z": 1}
assert event_a["tags"] == {"A": "1"}
assert event_b["tags"] == {"A": "1", "B1": "1", "B2": "1", "B3": "1", "B4": "1"}
assert event_c["tags"] == {"A": "1", "B1": "1", "B5": "1"}
assert event_z["tags"] == {"A": "1", "Z": "1"}
52 changes: 52 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
get_global_scope,
get_current_scope,
get_isolation_scope,
set_tag,
)

from sentry_sdk.client import Client, NonRecordingClient
Expand Down Expand Up @@ -189,6 +190,57 @@ def test_set_tags(sentry_init, capture_events):
}, "Updating tags with empty dict changed tags"


@pytest.mark.parametrize(
("key", "value", "expected"),
[
("int", 123, "123"),
("float", 123.456, "123.456"),
("bool", True, "True"),
("none", None, "None"),
("list", [1, 2, 3], "[1, 2, 3]"),
],
)
def test_set_tag_converts_to_string(sentry_init, capture_events, key, value, expected):
"""Test that the api.set_tag function converts values to strings."""
sentry_init()
events = capture_events()

set_tag(key, value)
raise_and_capture()

(event,) = events
tags = event.get("tags", {})

assert tags[key] == expected


def test_set_tags_converts_to_string(sentry_init, capture_events):
"""Test that the api.set_tags function converts values to strings."""
sentry_init()
events = capture_events()

set_tags(
{
"int": 456,
"float": 789.012,
"bool": False,
"tuple": (1, 2, 3),
"string": "already_string",
}
)

raise_and_capture()

(*_, event) = events
tags = event.get("tags", {})

assert tags["int"] == "456"
assert tags["float"] == "789.012"
assert tags["bool"] == "False"
assert tags["tuple"] == "(1, 2, 3)"
assert tags["string"] == "already_string"


def test_configure_scope_deprecation():
with pytest.warns(DeprecationWarning):
with configure_scope():
Expand Down
Loading
Loading