Skip to content
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

fix: Fix breadcrumb timestamp casting and its tests #3546

Merged
merged 27 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b9e2c16
tests: Fix breadcrumb ordering tests
BYK Sep 18, 2024
23034b6
Also parse Z with strptime
antonpirker Sep 19, 2024
2eec36b
blacken
BYK Sep 19, 2024
66af626
parse timezone with %z so +00:00 also works (py3.6)
BYK Sep 19, 2024
c42b7ab
Revert "parse timezone with %z so +00:00 also works (py3.6)"
BYK Sep 19, 2024
b12efb8
Revert "blacken"
BYK Sep 19, 2024
8773060
Revert "Also parse Z with strptime"
BYK Sep 19, 2024
08d7d0c
add note about py3.6 and .replace('Z','') ugliness
BYK Sep 19, 2024
e817ac9
sigh, python3.6
BYK Sep 19, 2024
89e697e
try to fix edge case
BYK Sep 19, 2024
9d86048
fix edge case and tests
BYK Sep 19, 2024
603139e
add logging
BYK Sep 19, 2024
b9b1a48
resort to print
BYK Sep 19, 2024
e211b11
hopefully fix it once and for all
BYK Sep 19, 2024
ba2b90b
add more tests
BYK Sep 19, 2024
0c1f681
timezones
BYK Sep 19, 2024
a7f7776
fix cohere errors
BYK Sep 19, 2024
9f410a2
remove type ignore (though it was used locally for me?)
BYK Sep 19, 2024
9f0b114
split off cohere fix
BYK Sep 19, 2024
aedde93
Merge branch 'master' into byk/tests/timezones
sentrivana Sep 20, 2024
54b407a
remove forgotton print
BYK Sep 20, 2024
c2d00cd
simplify code and fix edge-case
BYK Sep 21, 2024
6064185
mionr fixes and unit tests
BYK Sep 21, 2024
5746856
Merge branch 'master' into byk/tests/timezones
BYK Sep 21, 2024
157c7d1
change log msg to debug
sentrivana Sep 23, 2024
7234416
Merge branch 'master' into byk/tests/timezones
sentrivana Sep 23, 2024
9099d97
Merge branch 'master' into byk/tests/timezones
sentrivana Sep 23, 2024
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: 3 additions & 2 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ def start_transaction(
transaction=None,
instrumenter=INSTRUMENTER.SENTRY,
custom_sampling_context=None,
**kwargs
**kwargs,
):
# type: (Optional[Transaction], str, Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan]
"""
Expand Down Expand Up @@ -1324,7 +1324,8 @@ def _apply_breadcrumbs_to_event(self, event, hint, options):
crumb["timestamp"] = datetime_from_isoformat(crumb["timestamp"])

event["breadcrumbs"]["values"].sort(key=lambda crumb: crumb["timestamp"])
except Exception:
except Exception as err:
logger.debug("Error when sorting breadcrumbs", exc_info=err)
pass

def _apply_user_to_event(self, event, hint, options):
Expand Down
22 changes: 19 additions & 3 deletions sentry_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,29 @@ def format_timestamp(value):
return utctime.strftime("%Y-%m-%dT%H:%M:%S.%fZ")


ISO_TZ_SEPARATORS = frozenset(("+", "-"))


def datetime_from_isoformat(value):
# type: (str) -> datetime
try:
return datetime.fromisoformat(value)
except AttributeError:
result = datetime.fromisoformat(value)
except (AttributeError, ValueError):
# py 3.6
return datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
timestamp_format = (
"%Y-%m-%dT%H:%M:%S.%f" if "." in value else "%Y-%m-%dT%H:%M:%S"
)
if value.endswith("Z"):
value = value[:-1] + "+0000"

if value[-6] in ISO_TZ_SEPARATORS:
timestamp_format += "%z"
value = value[:-3] + value[-2:]
elif value[-5] in ISO_TZ_SEPARATORS:
timestamp_format += "%z"

result = datetime.strptime(value, timestamp_format)
return result.astimezone(timezone.utc)


def event_hint_with_exc_info(exc_info=None):
Expand Down
39 changes: 24 additions & 15 deletions tests/test_basics.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import pytest
from sentry_sdk.client import Client
from sentry_sdk.utils import datetime_from_isoformat
from tests.conftest import patch_start_tracing_child

import sentry_sdk
Expand Down Expand Up @@ -397,11 +398,12 @@ def test_breadcrumbs(sentry_init, capture_events):
def test_breadcrumb_ordering(sentry_init, capture_events):
sentry_init()
events = capture_events()
now = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0)

timestamps = [
datetime.datetime.now() - datetime.timedelta(days=10),
datetime.datetime.now() - datetime.timedelta(days=8),
datetime.datetime.now() - datetime.timedelta(days=12),
now - datetime.timedelta(days=10),
now - datetime.timedelta(days=8),
now - datetime.timedelta(days=12),
]

for timestamp in timestamps:
Expand All @@ -417,41 +419,48 @@ def test_breadcrumb_ordering(sentry_init, capture_events):

assert len(event["breadcrumbs"]["values"]) == len(timestamps)
timestamps_from_event = [
datetime.datetime.strptime(
x["timestamp"].replace("Z", ""), "%Y-%m-%dT%H:%M:%S.%f"
)
for x in event["breadcrumbs"]["values"]
datetime_from_isoformat(x["timestamp"]) for x in event["breadcrumbs"]["values"]
]
assert timestamps_from_event == sorted(timestamps)


def test_breadcrumb_ordering_different_types(sentry_init, capture_events):
sentry_init()
events = capture_events()
now = datetime.datetime.now(datetime.timezone.utc)

timestamps = [
datetime.datetime.now() - datetime.timedelta(days=10),
datetime.datetime.now() - datetime.timedelta(days=8),
datetime.datetime.now() - datetime.timedelta(days=12),
now - datetime.timedelta(days=10),
now - datetime.timedelta(days=8),
now.replace(microsecond=0) - datetime.timedelta(days=12),
now - datetime.timedelta(days=9),
now - datetime.timedelta(days=13),
now.replace(microsecond=0) - datetime.timedelta(days=11),
]

breadcrumb_timestamps = [
timestamps[0],
timestamps[1].isoformat(),
datetime.datetime.strftime(timestamps[2], "%Y-%m-%dT%H:%M:%S") + "Z",
datetime.datetime.strftime(timestamps[3], "%Y-%m-%dT%H:%M:%S.%f") + "+00:00",
datetime.datetime.strftime(timestamps[4], "%Y-%m-%dT%H:%M:%S.%f") + "+0000",
datetime.datetime.strftime(timestamps[5], "%Y-%m-%dT%H:%M:%S.%f") + "-0000",
]

for i, timestamp in enumerate(timestamps):
add_breadcrumb(
message="Authenticated at %s" % timestamp,
category="auth",
level="info",
timestamp=timestamp if i % 2 == 0 else timestamp.isoformat(),
timestamp=breadcrumb_timestamps[i],
)

capture_exception(ValueError())
(event,) = events

assert len(event["breadcrumbs"]["values"]) == len(timestamps)
timestamps_from_event = [
datetime.datetime.strptime(
x["timestamp"].replace("Z", ""), "%Y-%m-%dT%H:%M:%S.%f"
)
for x in event["breadcrumbs"]["values"]
datetime_from_isoformat(x["timestamp"]) for x in event["breadcrumbs"]["values"]
]
assert timestamps_from_event == sorted(timestamps)

Expand Down
50 changes: 50 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from sentry_sdk.utils import (
Components,
Dsn,
datetime_from_isoformat,
env_to_bool,
format_timestamp,
get_current_thread_meta,
Expand Down Expand Up @@ -61,6 +62,55 @@ def _normalize_distribution_name(name):
return re.sub(r"[-_.]+", "-", name).lower()


@pytest.mark.parametrize(
("input_str", "expected_output"),
(
(
"2021-01-01T00:00:00.000000Z",
datetime(2021, 1, 1, tzinfo=timezone.utc),
), # UTC time
(
"2021-01-01T00:00:00.000000",
datetime(2021, 1, 1, tzinfo=datetime.now().astimezone().tzinfo),
), # No TZ -- assume UTC
(
"2021-01-01T00:00:00Z",
datetime(2021, 1, 1, tzinfo=timezone.utc),
), # UTC - No milliseconds
(
"2021-01-01T00:00:00.000000+00:00",
datetime(2021, 1, 1, tzinfo=timezone.utc),
),
(
"2021-01-01T00:00:00.000000-00:00",
datetime(2021, 1, 1, tzinfo=timezone.utc),
),
(
"2021-01-01T00:00:00.000000+0000",
datetime(2021, 1, 1, tzinfo=timezone.utc),
),
(
"2021-01-01T00:00:00.000000-0000",
datetime(2021, 1, 1, tzinfo=timezone.utc),
),
(
"2020-12-31T00:00:00.000000+02:00",
datetime(2020, 12, 31, tzinfo=timezone(timedelta(hours=2))),
), # UTC+2 time
(
"2020-12-31T00:00:00.000000-0200",
datetime(2020, 12, 31, tzinfo=timezone(timedelta(hours=-2))),
), # UTC-2 time
(
"2020-12-31T00:00:00-0200",
datetime(2020, 12, 31, tzinfo=timezone(timedelta(hours=-2))),
), # UTC-2 time - no milliseconds
),
)
def test_datetime_from_isoformat(input_str, expected_output):
assert datetime_from_isoformat(input_str) == expected_output, input_str


@pytest.mark.parametrize(
"env_var_value,strict,expected",
[
Expand Down
Loading