Skip to content

Commit

Permalink
Add breadcrumb ordering
Browse files Browse the repository at this point in the history
Introduced a new feature to order breadcrumbs by timestamp in ascending order. This is achieved by adding a new function, `order_by_utc_time`, which sorts the breadcrumbs based on their timestamps.
  • Loading branch information
drew2a committed May 22, 2024
1 parent 828bb79 commit 75f6da1
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 10 deletions.
9 changes: 7 additions & 2 deletions src/tribler/core/sentry_reporter/sentry_reporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import sys
from collections import defaultdict
from contextlib import contextmanager
from contextlib import contextmanager, suppress
from contextvars import ContextVar
from enum import Enum, auto
from hashlib import md5
Expand All @@ -17,7 +17,7 @@
from tribler.core import version
from tribler.core.sentry_reporter.sentry_tools import (
get_first_item,
get_value
get_value, order_by_utc_time
)


Expand Down Expand Up @@ -386,6 +386,11 @@ def _before_send(self, event: Optional[Dict], hint: Optional[Dict]) -> Optional[
if self.scrubber:
event = self.scrubber.scrub_event(event)

# order breadcrumbs by timestamp in ascending order
if breadcrumbs := event.get('breadcrumbs'):
with suppress(Exception):
event['breadcrumbs'] = order_by_utc_time(breadcrumbs)

return event

# pylint: disable=unused-argument
Expand Down
24 changes: 23 additions & 1 deletion src/tribler/core/sentry_reporter/sentry_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"""
import re
from dataclasses import dataclass
from typing import Optional
from datetime import datetime
from typing import Dict, List, Optional

from faker import Faker

Expand Down Expand Up @@ -121,3 +122,24 @@ def obfuscate_string(s: str, part_of_speech: str = 'noun') -> str:
faker = Faker(locale='en_US')
faker.seed_instance(s)
return faker.word(part_of_speech=part_of_speech)


def order_by_utc_time(breadcrumbs: Optional[List[Dict]]):
""" Order breadcrumbs by timestamp in ascending order.
Args:
breadcrumbs: List of breadcrumbs
Returns:
Ordered list of breadcrumbs
"""
if not breadcrumbs:
return breadcrumbs

Check warning on line 137 in src/tribler/core/sentry_reporter/sentry_tools.py

View check run for this annotation

Codecov / codecov/patch

src/tribler/core/sentry_reporter/sentry_tools.py#L137

Added line #L137 was not covered by tests

def add_datetime_timestamp(crumb):
timestamp_iso = crumb['timestamp']
dt = datetime.fromisoformat(timestamp_iso.rstrip('Z'))
return dt, crumb

timestamped = map(add_datetime_timestamp, breadcrumbs)
return [crumb for timestamp, crumb in sorted(timestamped)]
22 changes: 16 additions & 6 deletions src/tribler/core/sentry_reporter/tests/test_sentry_reporter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from collections import defaultdict
from copy import deepcopy
from unittest.mock import MagicMock, Mock, patch

Expand Down Expand Up @@ -30,7 +29,10 @@ def sentry_reporter():
return SentryReporter()


@patch('tribler.core.sentry_reporter.sentry_reporter.sentry_sdk.init')
TARGET = 'tribler.core.sentry_reporter.sentry_reporter'


@patch(f'{TARGET}.sentry_sdk.init')
def test_init(mocked_init: Mock, sentry_reporter: SentryReporter):
# test that `init` method set all necessary variables and calls `sentry_sdk.init()`
sentry_reporter.init(sentry_url='url', release_version='release', scrubber=SentryScrubber(),
Expand All @@ -40,14 +42,14 @@ def test_init(mocked_init: Mock, sentry_reporter: SentryReporter):
mocked_init.assert_called_once()


@patch('tribler.core.sentry_reporter.sentry_reporter.ignore_logger')
@patch(f'{TARGET}.ignore_logger')
def test_ignore_logger(mocked_ignore_logger: Mock, sentry_reporter: SentryReporter):
# test that `ignore_logger` calls `ignore_logger` from sentry_sdk
sentry_reporter.ignore_logger('logger name')
mocked_ignore_logger.assert_called_with('logger name')


@patch('tribler.core.sentry_reporter.sentry_reporter.sentry_sdk.add_breadcrumb')
@patch(f'{TARGET}.sentry_sdk.add_breadcrumb')
def test_add_breadcrumb(mocked_add_breadcrumb: Mock, sentry_reporter: SentryReporter):
# test that `add_breadcrumb` passes all necessary arguments to `sentry_sdk`
assert sentry_reporter.add_breadcrumb('message', 'category', 'level', named_arg='some')
Expand All @@ -71,15 +73,15 @@ def test_get_confirmation_no_qt(sentry_reporter: SentryReporter):
assert not sentry_reporter.get_confirmation(Exception('test'))


@patch('tribler.core.sentry_reporter.sentry_reporter.sentry_sdk.capture_exception')
@patch(f'{TARGET}.sentry_sdk.capture_exception')
def test_capture_exception(mocked_capture_exception: Mock, sentry_reporter: SentryReporter):
# test that `capture_exception` passes an exception to `sentry_sdk`
exception = Exception('test')
sentry_reporter.capture_exception(exception)
mocked_capture_exception.assert_called_with(exception)


@patch('tribler.core.sentry_reporter.sentry_reporter.sentry_sdk.capture_exception')
@patch(f'{TARGET}.sentry_sdk.capture_exception')
def test_event_from_exception(mocked_capture_exception: Mock, sentry_reporter: SentryReporter):
# test that `event_from_exception` returns '{}' in case of an empty exception
assert sentry_reporter.event_from_exception(None) == {}
Expand Down Expand Up @@ -204,6 +206,14 @@ def test_before_send_scrubber_doesnt_exists(sentry_reporter: SentryReporter):
assert sentry_reporter._before_send({'some': 'event'}, None)


@patch(f'{TARGET}.order_by_utc_time', Mock(side_effect=ValueError))
def test_before_send_exception_in_order_by_utc_time(sentry_reporter: SentryReporter):
# test that in case of an exception in `order_by_utc_time`, the event will be sent
sentry_reporter.global_strategy = SentryStrategy.SEND_ALLOWED
event = {'some': 'event'}
assert sentry_reporter._before_send(event, None) == event


def test_send_defaults(sentry_reporter):
assert sentry_reporter.send_event(event={}) == DEFAULT_EVENT

Expand Down
23 changes: 22 additions & 1 deletion src/tribler/core/sentry_reporter/tests/test_sentry_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
get_last_item,
get_value,
modify_value,
obfuscate_string, )
obfuscate_string, order_by_utc_time, )


def test_first():
Expand Down Expand Up @@ -111,3 +111,24 @@ def test_extract_dict():
@pytest.mark.parametrize('given, expected', OBFUSCATED_STRINGS)
def test_obfuscate_string(given, expected):
assert obfuscate_string(given) == expected


def test_order_by_utc_time():
# Test order by timestamp
breadcrumbs = [
{
"timestamp": "2016-04-20T20:55:53.887Z",
"message": "3",
},
{
"timestamp": "2016-04-20T20:55:53.845Z",
"message": "1",
},
{
"timestamp": "2016-04-20T20:55:53.847Z",
"message": "2",
},
]
ordered_breadcrumbs = order_by_utc_time(breadcrumbs)
messages = [d['message'] for d in ordered_breadcrumbs]
assert messages == ['1', '2', '3']

0 comments on commit 75f6da1

Please sign in to comment.