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: Send to Spotlight sidecar #2524

Merged
merged 8 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 19 additions & 3 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from sentry_sdk.profiler import has_profiling_enabled, setup_profiler
from sentry_sdk.scrubber import EventScrubber
from sentry_sdk.monitor import Monitor
from sentry_sdk.spotlight import setup_spotlight

from sentry_sdk._types import TYPE_CHECKING

Expand Down Expand Up @@ -268,6 +269,10 @@
],
)

self.spotlight = None
if self.options.get("spotlight"):
self.spotlight = setup_spotlight(self.options)

Check warning on line 274 in sentry_sdk/client.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/client.py#L274

Added line #L274 was not covered by tests

sdk_name = get_sdk_name(list(self.integrations.keys()))
SDK_INFO["name"] = sdk_name
logger.debug("Setting SDK name to '%s'", sdk_name)
Expand Down Expand Up @@ -548,8 +553,6 @@
if disable_capture_event.get(False):
return None

if self.transport is None:
return None
if hint is None:
hint = {}
event_id = event.get("event_id")
Expand Down Expand Up @@ -591,7 +594,11 @@
# If tracing is enabled all events should go to /envelope endpoint.
# If no tracing is enabled only transactions, events with attachments, and checkins should go to the /envelope endpoint.
should_use_envelope_endpoint = (
tracing_enabled or is_transaction or is_checkin or bool(attachments)
tracing_enabled
or is_transaction
or is_checkin
or bool(attachments)
or bool(self.spotlight)
)
if should_use_envelope_endpoint:
headers = {
Expand All @@ -616,9 +623,18 @@
for attachment in attachments or ():
envelope.add_item(attachment.to_envelope_item())

if self.spotlight:
self.spotlight.capture_envelope(envelope)

Check warning on line 627 in sentry_sdk/client.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/client.py#L627

Added line #L627 was not covered by tests

if self.transport is None:
return None

Check warning on line 630 in sentry_sdk/client.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/client.py#L630

Added line #L630 was not covered by tests
antonpirker marked this conversation as resolved.
Show resolved Hide resolved

self.transport.capture_envelope(envelope)

else:
if self.transport is None:
return None

Check warning on line 636 in sentry_sdk/client.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/client.py#L636

Added line #L636 was not covered by tests

# All other events go to the legacy /store/ endpoint (will be removed in the future).
self.transport.capture_event(event_opt)

Expand Down
1 change: 1 addition & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ def __init__(
max_value_length=DEFAULT_MAX_VALUE_LENGTH, # type: int
enable_backpressure_handling=True, # type: bool
error_sampler=None, # type: Optional[Callable[[Event, Hint], Union[float, bool]]]
spotlight=None, # type: Optional[Union[bool, str]]
):
# type: (...) -> None
pass
Expand Down
51 changes: 51 additions & 0 deletions sentry_sdk/spotlight.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import io
import urllib3

from sentry_sdk._types import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Any
from typing import Dict
from typing import Optional

Check warning on line 9 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L7-L9

Added lines #L7 - L9 were not covered by tests

from sentry_sdk.utils import logger
from sentry_sdk.envelope import Envelope


class SpotlightClient(object):
def __init__(self, url):
# type: (str) -> None
self.url = url
self.http = urllib3.PoolManager()

Check warning on line 19 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L18-L19

Added lines #L18 - L19 were not covered by tests

def capture_envelope(self, envelope):
# type: (Envelope) -> None
body = io.BytesIO()
envelope.serialize_into(body)
try:
req = self.http.request(

Check warning on line 26 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L23-L26

Added lines #L23 - L26 were not covered by tests
url=self.url,
body=body.getvalue(),
method="POST",
headers={
"Content-Type": "application/x-sentry-envelope",
},
)
req.close()
except Exception as e:
logger.exception(str(e))

Check warning on line 36 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L34-L36

Added lines #L34 - L36 were not covered by tests


def setup_spotlight(options):
# type: (Dict[str, Any]) -> Optional[SpotlightClient]

url = options.get("spotlight")

Check warning on line 42 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L42

Added line #L42 was not covered by tests

if isinstance(url, str):
pass

Check warning on line 45 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L45

Added line #L45 was not covered by tests
elif url is True:
url = "http://localhost:8969/stream"

Check warning on line 47 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L47

Added line #L47 was not covered by tests
else:
return None

Check warning on line 49 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L49

Added line #L49 was not covered by tests

return SpotlightClient(url)

Check warning on line 51 in sentry_sdk/spotlight.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/spotlight.py#L51

Added line #L51 was not covered by tests
56 changes: 56 additions & 0 deletions tests/test_spotlight.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import pytest

from sentry_sdk import Hub, capture_exception


@pytest.fixture
def capture_spotlight_envelopes(monkeypatch):
def inner():
envelopes = []
test_spotlight = Hub.current.client.spotlight
old_capture_envelope = test_spotlight.capture_envelope

def append_envelope(envelope):
envelopes.append(envelope)
return old_capture_envelope(envelope)

monkeypatch.setattr(test_spotlight, "capture_envelope", append_envelope)
return envelopes

return inner


def test_spotlight_off_by_default(sentry_init):
sentry_init()
assert Hub.current.client.spotlight is None


def test_spotlight_default_url(sentry_init):
sentry_init(spotlight=True)

spotlight = Hub.current.client.spotlight
assert spotlight is not None
assert spotlight.url == "http://localhost:8969/stream"


def test_spotlight_custom_url(sentry_init):
sentry_init(spotlight="http://foobar@test.com/132")

spotlight = Hub.current.client.spotlight
assert spotlight is not None
assert spotlight.url == "http://foobar@test.com/132"


def test_spotlight_envelope(sentry_init, capture_spotlight_envelopes):
sentry_init(spotlight=True)
envelopes = capture_spotlight_envelopes()

try:
raise ValueError("aha!")
except Exception:
capture_exception()

(envelope,) = envelopes
payload = envelope.items[0].payload.json

assert payload["exception"]["values"][0]["value"] == "aha!"