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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
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