Skip to content

Commit

Permalink
feat: Send to Spotlight sidecar (#2524)
Browse files Browse the repository at this point in the history
Add Spotlight option to SDK. This allows sending envelopes to the Spotlight sidecar.

---------

Co-authored-by: Neel Shah <[email protected]>
Co-authored-by: Anton Pirker <[email protected]>
  • Loading branch information
3 people authored Nov 23, 2023
1 parent ea55387 commit 088431e
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 3 deletions.
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 @@ def _capture_envelope(envelope):
],
)

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

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 @@ def capture_event(
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 @@ def capture_event(
# 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 @@ def capture_event(
for attachment in attachments or ():
envelope.add_item(attachment.to_envelope_item())

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

if self.transport is None:
return None

self.transport.capture_envelope(envelope)

else:
if self.transport is None:
return None

# 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

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()

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


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

url = options.get("spotlight")

if isinstance(url, str):
pass
elif url is True:
url = "http://localhost:8969/stream"
else:
return None

return SpotlightClient(url)
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://[email protected]/132")

spotlight = Hub.current.client.spotlight
assert spotlight is not None
assert spotlight.url == "http://[email protected]/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!"

0 comments on commit 088431e

Please sign in to comment.