Skip to content

feat: Send to Spotlight sidecar #2524

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

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
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 @@ 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!"