microbootstrap 1.5.2__tar.gz → 1.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/PKG-INFO +1 -1
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/opentelemetry_instrument.py +5 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/sentry_instrument.py +102 -19
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/pyproject.toml +1 -1
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/pyproject.toml.orig +1 -1
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/README.md +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/__init__.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/bootstrappers/__init__.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/bootstrappers/base.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/bootstrappers/fastapi.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/bootstrappers/fastmcp.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/bootstrappers/faststream.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/bootstrappers/litestar.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/config/__init__.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/config/fastapi.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/config/fastmcp.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/config/faststream.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/config/litestar.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/console_writer.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/exceptions.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/granian_server.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/helpers.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/__init__.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/base.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/cors_instrument.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/health_checks_instrument.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/instrument_box.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/logging_instrument.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/prometheus_instrument.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/pyroscope_instrument.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/swagger_instrument.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments_setupper.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/middlewares/__init__.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/middlewares/fastapi.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/middlewares/fastmcp.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/middlewares/litestar.py +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/py.typed +0 -0
- {microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/settings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: microbootstrap
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.3
|
|
4
4
|
Summary: Package for bootstrapping new micro-services
|
|
5
5
|
Keywords: python,microservice,bootstrap,opentelemetry,logging,error-tracing,litestar,fastapi,fastmcp,mcp
|
|
6
6
|
Author: community-of-python
|
{microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/opentelemetry_instrument.py
RENAMED
|
@@ -21,6 +21,7 @@ from opentelemetry.trace import SpanKind, format_span_id, set_tracer_provider
|
|
|
21
21
|
from opentelemetry.util._importlib_metadata import entry_points
|
|
22
22
|
|
|
23
23
|
from microbootstrap.instruments.base import BaseInstrumentConfig, Instrument
|
|
24
|
+
from microbootstrap.instruments.sentry_instrument import snapshot_sentry_opentelemetry_baggage
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
LOGGER_OBJ: typing.Final = structlog.get_logger(__name__)
|
|
@@ -55,6 +56,10 @@ def opentelemetry_baggage_scope(
|
|
|
55
56
|
token: typing.Final = context.attach(baggage_context)
|
|
56
57
|
try:
|
|
57
58
|
yield
|
|
59
|
+
except Exception as exc:
|
|
60
|
+
with contextlib.suppress(Exception):
|
|
61
|
+
snapshot_sentry_opentelemetry_baggage(exc)
|
|
62
|
+
raise
|
|
58
63
|
finally:
|
|
59
64
|
context.detach(token)
|
|
60
65
|
|
{microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/sentry_instrument.py
RENAMED
|
@@ -3,13 +3,14 @@ import contextlib
|
|
|
3
3
|
import functools
|
|
4
4
|
import typing
|
|
5
5
|
import urllib.parse
|
|
6
|
+
from collections.abc import Mapping
|
|
6
7
|
|
|
7
8
|
import orjson
|
|
8
9
|
import pydantic
|
|
9
10
|
import sentry_sdk
|
|
10
11
|
from opentelemetry import baggage
|
|
11
12
|
from sentry_sdk import _types as sentry_types
|
|
12
|
-
from sentry_sdk.integrations import Integration
|
|
13
|
+
from sentry_sdk.integrations import Integration
|
|
13
14
|
|
|
14
15
|
from microbootstrap.instruments.base import BaseInstrumentConfig, Instrument
|
|
15
16
|
|
|
@@ -66,6 +67,71 @@ def enrich_sentry_event_from_structlog_log(event: sentry_types.Event, _hint: sen
|
|
|
66
67
|
|
|
67
68
|
SENTRY_EXTRA_OTEL_TRACE_ID_KEY: typing.Final = "otelTraceID"
|
|
68
69
|
SENTRY_EXTRA_OTEL_TRACE_URL_KEY: typing.Final = "otelTraceURL"
|
|
70
|
+
SENTRY_OTEL_BAGGAGE_SNAPSHOT_ATTRIBUTE: typing.Final = "__microbootstrap_sentry_opentelemetry_baggage__"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@typing.final
|
|
74
|
+
class SentryOpentelemetryBaggageIntegration(Integration):
|
|
75
|
+
identifier = "microbootstrap_opentelemetry_baggage"
|
|
76
|
+
|
|
77
|
+
def __init__(self, baggage_keys: set[str], baggage_url_templates: dict[str, str]) -> None:
|
|
78
|
+
self.baggage_keys: typing.Final = frozenset(baggage_keys)
|
|
79
|
+
self.baggage_url_templates: typing.Final = dict(baggage_url_templates)
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def setup_once() -> None:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def snapshot_sentry_opentelemetry_baggage(exception: BaseException) -> None:
|
|
87
|
+
integration = sentry_sdk.get_client().get_integration(SentryOpentelemetryBaggageIntegration)
|
|
88
|
+
if not isinstance(integration, SentryOpentelemetryBaggageIntegration) or hasattr(
|
|
89
|
+
exception,
|
|
90
|
+
SENTRY_OTEL_BAGGAGE_SNAPSHOT_ATTRIBUTE,
|
|
91
|
+
):
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
configured_keys: typing.Final = integration.baggage_keys.union(integration.baggage_url_templates)
|
|
95
|
+
snapshot: typing.Final = {key: baggage.get_baggage(key) for key in configured_keys}
|
|
96
|
+
with contextlib.suppress(AttributeError, TypeError):
|
|
97
|
+
setattr(exception, SENTRY_OTEL_BAGGAGE_SNAPSHOT_ATTRIBUTE, snapshot)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _find_sentry_opentelemetry_baggage_snapshot(hint: sentry_types.Hint) -> Mapping[str, object | None] | None:
|
|
101
|
+
if not isinstance(hint, Mapping):
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
exc_info: typing.Final = hint.get("exc_info")
|
|
105
|
+
if not isinstance(exc_info, tuple):
|
|
106
|
+
return None
|
|
107
|
+
try:
|
|
108
|
+
_, exception_value, _ = exc_info
|
|
109
|
+
except ValueError:
|
|
110
|
+
return None
|
|
111
|
+
if not isinstance(exception_value, BaseException):
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
exceptions_to_visit: list[BaseException] = [exception_value]
|
|
115
|
+
visited_exceptions: set[int] = set()
|
|
116
|
+
while exceptions_to_visit:
|
|
117
|
+
exception = exceptions_to_visit.pop()
|
|
118
|
+
if id(exception) in visited_exceptions:
|
|
119
|
+
continue
|
|
120
|
+
visited_exceptions.add(id(exception))
|
|
121
|
+
exception_snapshot = getattr(exception, SENTRY_OTEL_BAGGAGE_SNAPSHOT_ATTRIBUTE, None)
|
|
122
|
+
if isinstance(exception_snapshot, Mapping):
|
|
123
|
+
return exception_snapshot
|
|
124
|
+
|
|
125
|
+
nested_exceptions = getattr(exception, "exceptions", ())
|
|
126
|
+
if isinstance(nested_exceptions, tuple):
|
|
127
|
+
exceptions_to_visit.extend(
|
|
128
|
+
reversed([nested for nested in nested_exceptions if isinstance(nested, BaseException)])
|
|
129
|
+
)
|
|
130
|
+
if chained_exception := exception.__cause__ or (
|
|
131
|
+
None if exception.__suppress_context__ else exception.__context__
|
|
132
|
+
):
|
|
133
|
+
exceptions_to_visit.append(chained_exception)
|
|
134
|
+
return None
|
|
69
135
|
|
|
70
136
|
|
|
71
137
|
def add_trace_url_to_event(
|
|
@@ -80,27 +146,30 @@ def enrich_sentry_event_from_opentelemetry_baggage(
|
|
|
80
146
|
baggage_keys: set[str],
|
|
81
147
|
baggage_url_templates: dict[str, str],
|
|
82
148
|
event: sentry_types.Event,
|
|
83
|
-
|
|
149
|
+
hint: sentry_types.Hint,
|
|
84
150
|
) -> sentry_types.Event:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
event.
|
|
151
|
+
configured_keys: typing.Final = baggage_keys.union(baggage_url_templates)
|
|
152
|
+
snapshot: typing.Final = _find_sentry_opentelemetry_baggage_snapshot(hint)
|
|
153
|
+
baggage_values: typing.Final = (
|
|
154
|
+
snapshot if snapshot is not None else {key: baggage.get_baggage(key) for key in configured_keys}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
for key in baggage_keys:
|
|
158
|
+
if (value := baggage_values.get(key)) is not None:
|
|
159
|
+
event.setdefault("tags", {})[key] = str(value)
|
|
160
|
+
elif tags := event.get("tags"):
|
|
161
|
+
tags.pop(key, None)
|
|
95
162
|
|
|
96
163
|
for key, url_template in baggage_url_templates.items():
|
|
97
164
|
placeholder = f"{{{key}}}"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
165
|
+
extra_key = f"{key}_url"
|
|
166
|
+
if (value := baggage_values.get(key)) is not None and placeholder in url_template:
|
|
167
|
+
event.setdefault("extra", {})[extra_key] = url_template.replace(
|
|
168
|
+
placeholder,
|
|
169
|
+
urllib.parse.quote(str(value), safe=""),
|
|
170
|
+
)
|
|
171
|
+
elif extra := event.get("extra"):
|
|
172
|
+
extra.pop(extra_key, None)
|
|
104
173
|
return event
|
|
105
174
|
|
|
106
175
|
|
|
@@ -126,6 +195,17 @@ class SentryInstrument(Instrument[SentryConfig]):
|
|
|
126
195
|
return bool(self.instrument_config.sentry_dsn)
|
|
127
196
|
|
|
128
197
|
def bootstrap(self) -> None:
|
|
198
|
+
baggage_integration: typing.Final = (
|
|
199
|
+
SentryOpentelemetryBaggageIntegration(
|
|
200
|
+
self.instrument_config.sentry_opentelemetry_baggage_keys,
|
|
201
|
+
self.instrument_config.sentry_opentelemetry_baggage_url_templates,
|
|
202
|
+
)
|
|
203
|
+
if (
|
|
204
|
+
self.instrument_config.sentry_opentelemetry_baggage_keys
|
|
205
|
+
or self.instrument_config.sentry_opentelemetry_baggage_url_templates
|
|
206
|
+
)
|
|
207
|
+
else None
|
|
208
|
+
)
|
|
129
209
|
sentry_sdk.init(
|
|
130
210
|
dsn=self.instrument_config.sentry_dsn,
|
|
131
211
|
sample_rate=self.instrument_config.sentry_sample_rate,
|
|
@@ -153,7 +233,10 @@ class SentryInstrument(Instrument[SentryConfig]):
|
|
|
153
233
|
else None,
|
|
154
234
|
self.instrument_config.sentry_before_send,
|
|
155
235
|
),
|
|
156
|
-
integrations=
|
|
236
|
+
integrations=[
|
|
237
|
+
*self.instrument_config.sentry_integrations,
|
|
238
|
+
*([baggage_integration] if baggage_integration else []),
|
|
239
|
+
],
|
|
157
240
|
**self.instrument_config.sentry_additional_params,
|
|
158
241
|
)
|
|
159
242
|
if self.instrument_config.sentry_tags:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/health_checks_instrument.py
RENAMED
|
File without changes
|
|
File without changes
|
{microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/logging_instrument.py
RENAMED
|
File without changes
|
{microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/prometheus_instrument.py
RENAMED
|
File without changes
|
{microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/pyroscope_instrument.py
RENAMED
|
File without changes
|
{microbootstrap-1.5.2 → microbootstrap-1.5.3}/microbootstrap/instruments/swagger_instrument.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|