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

feat(api): Added error_sampler option #2456

Merged
merged 10 commits into from Oct 20, 2023
9 changes: 5 additions & 4 deletions sentry_sdk/client.py
Expand Up @@ -456,10 +456,11 @@ def _should_sample_error(
event, # type: Event

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

event contains information about the logged message via logging?

There is a hint parameter in the before_send that contains this information in log_record

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saippuakauppias Could you please clarify your question? How exactly are you logging the message?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are using the LoggingIntegration, your log message should appear in the event passed to this function.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am using LoggingIntegration and logging all warning messages to sentry. I tried to discard some messages that are not needed and in debug mode found that the path to the file that triggers the message is in hint['log_record'].pathname.

Actually, I need this as a minimum. I would like this feature to have a similar option to filter by module name.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@saippuakauppias we decided to also pass the hint into the events_sampler, so you will be able to make your sampling decision based on information contained in the hint.

):
# type: (...) -> bool
not_in_sample_rate = (
self.options["sample_rate"] < 1.0
and random.random() >= self.options["sample_rate"]
)
try:
sample_rate = self.options["issues_sampler"](event)
szokeasaurusrex marked this conversation as resolved.
Show resolved Hide resolved
except (KeyError, TypeError):
sample_rate = self.options["sample_rate"]
not_in_sample_rate = sample_rate < 1.0 and random.random() >= sample_rate
if not_in_sample_rate:
# because we will not sample this event, record a "lost event".
if self.transport:
Expand Down
1 change: 1 addition & 0 deletions sentry_sdk/consts.py
Expand Up @@ -261,6 +261,7 @@ def __init__(
event_scrubber=None, # type: Optional[sentry_sdk.scrubber.EventScrubber]
max_value_length=DEFAULT_MAX_VALUE_LENGTH, # type: int
enable_backpressure_handling=True, # type: bool
issues_sampler=None, # type: Optional[Callable[[Event], Union[float, bool]]]
):
# type: (...) -> None
pass
Expand Down
41 changes: 41 additions & 0 deletions tests/test_client.py
Expand Up @@ -1196,3 +1196,44 @@ def test_debug_option(
assert "something is wrong" in caplog.text
else:
assert "something is wrong" not in caplog.text


@pytest.mark.parametrize(
("sampler_function_mock", "sample_rate", "expected_events"),
(
# Baseline test with issues_sampler only, both floats and bools
(mock.MagicMock(return_value=1.0), None, 1),
(mock.MagicMock(return_value=0.0), None, 0),
(mock.MagicMock(return_value=True), None, 1),
(mock.MagicMock(return_value=False), None, 0),
# Baseline test with sample_rate only
(None, 0.0, 0),
(None, 1.0, 1),
# issues_sampler takes precedence over sample_rate
(mock.MagicMock(return_value=1.0), 0.0, 1),
(mock.MagicMock(return_value=0.0), 1.0, 0),
),
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also test different rates for different errors. Like ZeroDivisionError: 1.0, RuntimeError: 0.5 and such.

def test_issues_sampler(
sentry_init, capture_events, sampler_function_mock, sample_rate, expected_events
):
sentry_init(issues_sampler=sampler_function_mock, sample_rate=sample_rate)

events = capture_events()

try:
1 / 0
except ZeroDivisionError:
capture_exception()

assert len(events) == expected_events

try:
sampler_function_mock.assert_called_once()

# Ensure one argument (the event) was passed to the sampler function
assert len(sampler_function_mock.call_args[0]) == 1
except AttributeError:
# sampler_function_mock should be None in this case,
# but let's double-check to ensure the test is working correctly
assert sampler_function_mock is None