Custom transports #1484
Replies: 5 comments
-
This use case covers cases where the end system might not have the network interface up in time/permanently, so it switches and stores a backlog of events/envelopes if the network is down and starts sending them upstream if the network is up. This could be useful in embedded scenarios. A further optimization here could be storing the network alive state and signaling from another thread/process that the network is up instead of checking the interface every time. import netifaces
import sentry_sdk
from sentry_sdk.transport import HttpTransport
def is_network_alive():
# check network interface here
# https://stackoverflow.com/questions/17679887/python-check-whether-a-network-interface-is-up
addr = netifaces.ifaddresses('en0')
return netifaces.AF_INET in addr
class NetworkSwitchHttpTransport(HttpTransport):
def __init__(self, options):
self._offline_backlog_envelopes = []
self._offline_backlog_events = []
super().__init__(options)
def capture_envelope(self, envelope):
if is_network_alive():
self.flush_envelopes()
super().capture_envelope(envelope)
else:
self._offline_backlog_envelopes.push(envelope)
def capture_event(self, event):
if is_network_alive():
self.flush_events()
super().capture_event(event)
else:
self._offline_backlog_events.push(envelope)
def flush_envelopes(self):
for envelope in self._offline_backlog_envelopes:
super.capture_envelope(envelope)
self._offline_backlog_envelopes = []
def flush_events(self):
for event in self._offline_backlog_events:
super.capture_event(event)
self._offline_backlog_events = []
sentry_sdk.init(transport=NetworkSwitchHttpTransport) |
Beta Was this translation helpful? Give feedback.
-
Another use-case we used it for is double sending. For us, it was rather specialized because we just sent it to another Sentry instance but hey 🤷♂️ |
Beta Was this translation helpful? Give feedback.
-
See this |
Beta Was this translation helpful? Give feedback.
-
Following up on the double sending use case, this is how one would implement a import sentry_sdk
from sentry_sdk.client import get_options
from sentry_sdk.transport import Transport, make_transport
class MultiplexingTransport(Transport):
""" send event to multiple DSNs """
def __init__(self, dsn1, dsn2, options):
self.transport1 = make_transport(get_options(dsn=dsn1, **options))
self.transport2 = make_transport(get_options(dsn=dsn2, **options))
def capture_envelope(self, envelope):
self.transport1.capture_envelope(envelope)
self.transport2.capture_envelope(envelope)
def capture_event(self, event):
self.transport1.capture_event(event)
self.transport2.capture_event(event)
DSN1="<>"
DSN2="<>"
SDK_OPTIONS = {"traces_sample_rate": 1.0}
sentry_sdk.init(transport=MultiplexingTransport(DSN1, DSN2, **SDK_OPTIONS)) |
Beta Was this translation helpful? Give feedback.
-
Another multiplexing/dispatch transport proof of concept: import random
import sentry_sdk
from sentry_sdk.transport import HttpTransport
class Transport1(HttpTransport):
def __init__(self, options):
options["dsn"] = "<dsn1>"
super().__init__(options)
class Transport2(HttpTransport):
def __init__(self, options):
options["dsn"] = "<dsn2>"
super().__init__(options)
class DispatchTransport(HttpTransport):
def __init__(self, options):
super().__init__(options)
self.transports = {
"transport1": Transport1(options),
"transport2": Transport2(options),
}
def select_transport(self, envelope=None, event=None):
# XXX logic to route to a specific transport based on envelope contents
# goes here
transport = random.choice(list(self.transports.values()))
return transport
def capture_event(self, event):
transport = self.select_transport(event=event)
transport.capture_event(event)
def capture_envelope(self, envelope):
transport = self.select_transport(envelope=envelope)
# fix DSC public_key since otherwise it'll use the default DSN's public key which is wrong
dsc = envelope.headers.get("trace")
if dsc and transport.parsed_dsn:
dsc["public_key"] = transport.parsed_dsn.public_key
return transport.capture_envelope(envelope)
def is_healthy(self):
return all(transport.is_healthy() for transport in self.transports.values())
def flush(self, timeout, callback=None):
for transport in self.transports.values():
transport.flush(timeout, callback)
def record_lost_event(self, *args, **kwargs):
for transport in self.transports.values():
transport.record_lost_event(*args, **kwargs)
sentry_sdk.init(
# ...usual options
dsn="<dsn1>", # there needs to be a "default" dsn to make this work at all
transport=DispatchTransport,
)
def do():
1 / 0
do() |
Beta Was this translation helpful? Give feedback.
-
This thread will serve as a place to collect use cases for custom transports.
In general, a custom transport is best implemented as a class that derives from
sentry_sdk.transport.Transport
that implements the twocapture_(event|envelope)
methods.Beta Was this translation helpful? Give feedback.
All reactions