lite-bootstrap 1.2.2__tar.gz → 1.3.0__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.
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/PKG-INFO +16 -9
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/README.md +7 -6
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/faststream_bootstrapper.py +8 -0
- lite_bootstrap-1.3.0/lite_bootstrap/import_checker.py +40 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/logging_factory.py +20 -5
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/logging_instrument.py +3 -3
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/opentelemetry_instrument.py +49 -14
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/pyproject.toml +15 -5
- lite_bootstrap-1.2.2/lite_bootstrap/import_checker.py +0 -19
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/__init__.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/__init__.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/base.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/fastmcp_bootstrapper.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/free_bootstrapper.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/litestar_bootstrapper.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/exceptions.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/helpers/__init__.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/helpers/fastapi_helpers.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/helpers/path.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/__init__.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/base.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/cors_instrument.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/healthchecks_instrument.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/prometheus_instrument.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/pyroscope_instrument.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/sentry_instrument.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/swagger_instrument.py +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/py.typed +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/fastapi_docs/redoc.standalone.js +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/fastapi_docs/swagger-ui-bundle.js +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/fastapi_docs/swagger-ui.css +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/litestar_docs/asyncapi-web-component.js +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/litestar_docs/default.min.css +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/litestar_docs/swagger-ui-bundle.js +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/litestar_docs/swagger-ui-standalone-preset.js +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/litestar_docs/swagger-ui.css +0 -0
- {lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lite-bootstrap
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Lightweight bootstrap for production-ready Python microservices
|
|
5
5
|
Keywords: microservices,bootstrap,opentelemetry,sentry,error-tracing,fastapi,litestar,faststream,structlog,fastmcp,python
|
|
6
6
|
Author: Artur Shiriev
|
|
@@ -15,7 +15,6 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.14
|
|
16
16
|
Classifier: Typing :: Typed
|
|
17
17
|
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
-
Requires-Dist: orjson
|
|
19
18
|
Requires-Dist: fastapi ; extra == 'fastapi'
|
|
20
19
|
Requires-Dist: lite-bootstrap[fastapi-sentry,fastapi-otl,fastapi-logging,fastapi-metrics,pyroscope] ; extra == 'fastapi-all'
|
|
21
20
|
Requires-Dist: lite-bootstrap[fastapi,logging] ; extra == 'fastapi-logging'
|
|
@@ -46,10 +45,15 @@ Requires-Dist: lite-bootstrap[litestar,otl] ; extra == 'litestar-otl'
|
|
|
46
45
|
Requires-Dist: opentelemetry-instrumentation-asgi>=0.46b0 ; extra == 'litestar-otl'
|
|
47
46
|
Requires-Dist: lite-bootstrap[litestar,sentry] ; extra == 'litestar-sentry'
|
|
48
47
|
Requires-Dist: structlog ; extra == 'logging'
|
|
48
|
+
Requires-Dist: orjson ; extra == 'orjson'
|
|
49
49
|
Requires-Dist: opentelemetry-api ; extra == 'otl'
|
|
50
50
|
Requires-Dist: opentelemetry-sdk ; extra == 'otl'
|
|
51
|
-
Requires-Dist: opentelemetry-exporter-otlp ; extra == 'otl'
|
|
51
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc ; extra == 'otl'
|
|
52
52
|
Requires-Dist: opentelemetry-instrumentation ; extra == 'otl'
|
|
53
|
+
Requires-Dist: opentelemetry-api ; extra == 'otl-http'
|
|
54
|
+
Requires-Dist: opentelemetry-sdk ; extra == 'otl-http'
|
|
55
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http ; extra == 'otl-http'
|
|
56
|
+
Requires-Dist: opentelemetry-instrumentation ; extra == 'otl-http'
|
|
53
57
|
Requires-Dist: pyroscope-io ; extra == 'pyroscope'
|
|
54
58
|
Requires-Dist: sentry-sdk ; extra == 'sentry'
|
|
55
59
|
Requires-Python: >=3.10, <4
|
|
@@ -81,7 +85,9 @@ Provides-Extra: litestar-metrics
|
|
|
81
85
|
Provides-Extra: litestar-otl
|
|
82
86
|
Provides-Extra: litestar-sentry
|
|
83
87
|
Provides-Extra: logging
|
|
88
|
+
Provides-Extra: orjson
|
|
84
89
|
Provides-Extra: otl
|
|
90
|
+
Provides-Extra: otl-http
|
|
85
91
|
Provides-Extra: pyroscope
|
|
86
92
|
Provides-Extra: sentry
|
|
87
93
|
Description-Content-Type: text/markdown
|
|
@@ -109,6 +115,7 @@ Description-Content-Type: text/markdown
|
|
|
109
115
|
`lite-bootstrap` wires production observability — OpenTelemetry, Prometheus, Sentry, and structlog — into FastAPI, Litestar, and FastStream services in a few lines, with no boilerplate.
|
|
110
116
|
|
|
111
117
|
With `lite-bootstrap`, you receive an application with lightweight built-in support for:
|
|
118
|
+
|
|
112
119
|
- `sentry`
|
|
113
120
|
- `prometheus`
|
|
114
121
|
- `opentelemetry`
|
|
@@ -120,11 +127,11 @@ With `lite-bootstrap`, you receive an application with lightweight built-in supp
|
|
|
120
127
|
|
|
121
128
|
Those instruments can be bootstrapped for:
|
|
122
129
|
|
|
123
|
-
- [LiteStar](https://lite-bootstrap.modern-python.org/integrations/litestar)
|
|
124
|
-
- [FastStream](https://lite-bootstrap.modern-python.org/integrations/faststream)
|
|
125
|
-
- [FastAPI](https://lite-bootstrap.modern-python.org/integrations/fastapi)
|
|
126
|
-
- [FastMCP](https://lite-bootstrap.modern-python.org/integrations/fastmcp)
|
|
127
|
-
- [services and scripts without frameworks](https://lite-bootstrap.modern-python.org/integrations/free)
|
|
130
|
+
- [LiteStar](https://lite-bootstrap.modern-python.org/integrations/litestar/)
|
|
131
|
+
- [FastStream](https://lite-bootstrap.modern-python.org/integrations/faststream/)
|
|
132
|
+
- [FastAPI](https://lite-bootstrap.modern-python.org/integrations/fastapi/)
|
|
133
|
+
- [FastMCP](https://lite-bootstrap.modern-python.org/integrations/fastmcp/)
|
|
134
|
+
- [services and scripts without frameworks](https://lite-bootstrap.modern-python.org/integrations/free/)
|
|
128
135
|
|
|
129
136
|
## Lifecycle constraints
|
|
130
137
|
|
|
@@ -155,7 +162,7 @@ The following ideas were borrowed:
|
|
|
155
162
|
The following intentionally differ:
|
|
156
163
|
|
|
157
164
|
- **Configuration**: `lite-bootstrap` uses frozen `dataclass` configs (no `pydantic` / `pydantic-settings` runtime dependency), which is what makes it "lite". `microbootstrap` configures everything through `pydantic-settings` models.
|
|
158
|
-
- **Granular extras**: `lite-bootstrap`
|
|
165
|
+
- **Granular extras**: `lite-bootstrap` has **no** mandatory runtime dependencies; every instrument (`sentry`, `otl`, `logging`, `pyroscope`) and every framework (`fastapi`, `litestar`, `faststream`) is its own extra, plus an opt-in `orjson` speedup for logging, per-pair combos (`fastapi-sentry`, `litestar-otl`, `faststream-metrics`, …) and `*-all` rollups. You install only what you actually use. It also runs on free-threaded CPython (3.13t/3.14t) — see [`architecture/free-threading.md`](architecture/free-threading.md). `microbootstrap` bundles the full observability stack (opentelemetry, sentry-sdk, structlog, pyroscope-io, rich, pydantic-settings, …) as base dependencies and only splits framework packages into extras.
|
|
159
166
|
- **Scope**: `lite-bootstrap` is deliberately narrow — only instrument wiring. It does not include a Granian server runner or a console writer.
|
|
160
167
|
|
|
161
168
|
## 📚 [Documentation](https://lite-bootstrap.modern-python.org)
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
`lite-bootstrap` wires production observability — OpenTelemetry, Prometheus, Sentry, and structlog — into FastAPI, Litestar, and FastStream services in a few lines, with no boilerplate.
|
|
22
22
|
|
|
23
23
|
With `lite-bootstrap`, you receive an application with lightweight built-in support for:
|
|
24
|
+
|
|
24
25
|
- `sentry`
|
|
25
26
|
- `prometheus`
|
|
26
27
|
- `opentelemetry`
|
|
@@ -32,11 +33,11 @@ With `lite-bootstrap`, you receive an application with lightweight built-in supp
|
|
|
32
33
|
|
|
33
34
|
Those instruments can be bootstrapped for:
|
|
34
35
|
|
|
35
|
-
- [LiteStar](https://lite-bootstrap.modern-python.org/integrations/litestar)
|
|
36
|
-
- [FastStream](https://lite-bootstrap.modern-python.org/integrations/faststream)
|
|
37
|
-
- [FastAPI](https://lite-bootstrap.modern-python.org/integrations/fastapi)
|
|
38
|
-
- [FastMCP](https://lite-bootstrap.modern-python.org/integrations/fastmcp)
|
|
39
|
-
- [services and scripts without frameworks](https://lite-bootstrap.modern-python.org/integrations/free)
|
|
36
|
+
- [LiteStar](https://lite-bootstrap.modern-python.org/integrations/litestar/)
|
|
37
|
+
- [FastStream](https://lite-bootstrap.modern-python.org/integrations/faststream/)
|
|
38
|
+
- [FastAPI](https://lite-bootstrap.modern-python.org/integrations/fastapi/)
|
|
39
|
+
- [FastMCP](https://lite-bootstrap.modern-python.org/integrations/fastmcp/)
|
|
40
|
+
- [services and scripts without frameworks](https://lite-bootstrap.modern-python.org/integrations/free/)
|
|
40
41
|
|
|
41
42
|
## Lifecycle constraints
|
|
42
43
|
|
|
@@ -67,7 +68,7 @@ The following ideas were borrowed:
|
|
|
67
68
|
The following intentionally differ:
|
|
68
69
|
|
|
69
70
|
- **Configuration**: `lite-bootstrap` uses frozen `dataclass` configs (no `pydantic` / `pydantic-settings` runtime dependency), which is what makes it "lite". `microbootstrap` configures everything through `pydantic-settings` models.
|
|
70
|
-
- **Granular extras**: `lite-bootstrap`
|
|
71
|
+
- **Granular extras**: `lite-bootstrap` has **no** mandatory runtime dependencies; every instrument (`sentry`, `otl`, `logging`, `pyroscope`) and every framework (`fastapi`, `litestar`, `faststream`) is its own extra, plus an opt-in `orjson` speedup for logging, per-pair combos (`fastapi-sentry`, `litestar-otl`, `faststream-metrics`, …) and `*-all` rollups. You install only what you actually use. It also runs on free-threaded CPython (3.13t/3.14t) — see [`architecture/free-threading.md`](architecture/free-threading.md). `microbootstrap` bundles the full observability stack (opentelemetry, sentry-sdk, structlog, pyroscope-io, rich, pydantic-settings, …) as base dependencies and only splits framework packages into extras.
|
|
71
72
|
- **Scope**: `lite-bootstrap` is deliberately narrow — only instrument wiring. It does not include a Granian server runner or a console writer.
|
|
72
73
|
|
|
73
74
|
## 📚 [Documentation](https://lite-bootstrap.modern-python.org)
|
|
@@ -43,6 +43,11 @@ class FastStreamTelemetryMiddlewareProtocol(typing.Protocol):
|
|
|
43
43
|
include_messages_counters: bool = True,
|
|
44
44
|
) -> None: ...
|
|
45
45
|
|
|
46
|
+
# A constructed instance is passed to broker.add_middleware, which expects a
|
|
47
|
+
# faststream BrokerMiddleware — a builder callable. Declared with Any to keep
|
|
48
|
+
# lite-bootstrap decoupled from faststream's internal middleware types.
|
|
49
|
+
def __call__(self, msg: typing.Any, /, *, context: typing.Any) -> typing.Any: ... # noqa: ANN401
|
|
50
|
+
|
|
46
51
|
|
|
47
52
|
@typing.runtime_checkable
|
|
48
53
|
class FastStreamPrometheusMiddlewareProtocol(typing.Protocol):
|
|
@@ -55,6 +60,9 @@ class FastStreamPrometheusMiddlewareProtocol(typing.Protocol):
|
|
|
55
60
|
received_messages_size_buckets: typing.Sequence[float] | None = None,
|
|
56
61
|
) -> None: ...
|
|
57
62
|
|
|
63
|
+
# See FastStreamTelemetryMiddlewareProtocol.__call__.
|
|
64
|
+
def __call__(self, msg: typing.Any, /, *, context: typing.Any) -> typing.Any: ... # noqa: ANN401
|
|
65
|
+
|
|
58
66
|
|
|
59
67
|
def _make_asgi_faststream() -> "AsgiFastStream":
|
|
60
68
|
return AsgiFastStream()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from importlib.util import find_spec
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _safe_find_spec(module_name: str) -> bool:
|
|
5
|
+
"""Return whether a dotted module is importable, treating a missing parent as absent.
|
|
6
|
+
|
|
7
|
+
find_spec imports the dotted name's parent first; a present-but-incomplete
|
|
8
|
+
namespace (e.g. opentelemetry-api installed without opentelemetry-instrumentation)
|
|
9
|
+
otherwise raises ModuleNotFoundError, crashing `import lite_bootstrap`.
|
|
10
|
+
"""
|
|
11
|
+
try:
|
|
12
|
+
return find_spec(module_name) is not None
|
|
13
|
+
except ModuleNotFoundError:
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
is_opentelemetry_installed = find_spec("opentelemetry") is not None
|
|
18
|
+
# opentelemetry-api provides the `opentelemetry` namespace (trace, metrics) without the
|
|
19
|
+
# sdk. The OTel instrument imports opentelemetry.sdk.* and needs this stricter check;
|
|
20
|
+
# api-only consumers (logging trace-injection, framework get_tracer_provider) use the
|
|
21
|
+
# flag above. See architecture/instruments.md.
|
|
22
|
+
is_opentelemetry_sdk_installed = _safe_find_spec("opentelemetry.sdk")
|
|
23
|
+
is_sentry_installed = find_spec("sentry_sdk") is not None
|
|
24
|
+
is_structlog_installed = find_spec("structlog") is not None
|
|
25
|
+
is_prometheus_client_installed = find_spec("prometheus_client") is not None
|
|
26
|
+
is_fastapi_installed = find_spec("fastapi") is not None
|
|
27
|
+
is_litestar_installed = find_spec("litestar") is not None
|
|
28
|
+
is_faststream_installed = find_spec("faststream") is not None
|
|
29
|
+
is_prometheus_fastapi_instrumentator_installed = find_spec("prometheus_fastapi_instrumentator") is not None
|
|
30
|
+
is_fastapi_opentelemetry_installed = is_opentelemetry_installed and _safe_find_spec(
|
|
31
|
+
"opentelemetry.instrumentation.fastapi"
|
|
32
|
+
)
|
|
33
|
+
is_litestar_opentelemetry_installed = (
|
|
34
|
+
is_opentelemetry_installed and is_litestar_installed and _safe_find_spec("opentelemetry.instrumentation.asgi")
|
|
35
|
+
)
|
|
36
|
+
is_otlp_grpc_exporter_installed = _safe_find_spec("opentelemetry.exporter.otlp.proto.grpc.trace_exporter")
|
|
37
|
+
is_otlp_http_exporter_installed = _safe_find_spec("opentelemetry.exporter.otlp.proto.http.trace_exporter")
|
|
38
|
+
is_pyroscope_installed = find_spec("pyroscope") is not None
|
|
39
|
+
is_fastmcp_installed = find_spec("fastmcp") is not None
|
|
40
|
+
is_orjson_installed = find_spec("orjson") is not None
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import dataclasses
|
|
2
|
+
import json
|
|
2
3
|
import logging
|
|
3
4
|
import logging.handlers
|
|
4
5
|
import sys
|
|
5
6
|
import typing
|
|
6
7
|
|
|
7
|
-
import orjson
|
|
8
|
-
|
|
9
8
|
from lite_bootstrap import import_checker
|
|
10
9
|
|
|
11
10
|
|
|
11
|
+
if import_checker.is_orjson_installed:
|
|
12
|
+
import orjson
|
|
13
|
+
|
|
14
|
+
|
|
12
15
|
ScopeType = typing.MutableMapping[str, typing.Any]
|
|
13
16
|
|
|
14
17
|
|
|
@@ -31,10 +34,22 @@ class _MemoryLoggerFactoryConfig:
|
|
|
31
34
|
log_stream: typing.Any = sys.stdout
|
|
32
35
|
|
|
33
36
|
|
|
34
|
-
def
|
|
37
|
+
def _dumps_orjson(value: typing.Any, **kwargs: typing.Any) -> str: # noqa: ANN401
|
|
35
38
|
return orjson.dumps(value, **kwargs).decode()
|
|
36
39
|
|
|
37
40
|
|
|
41
|
+
def _dumps_stdlib(value: typing.Any, **kwargs: typing.Any) -> str: # noqa: ANN401
|
|
42
|
+
# Match orjson's compact, UTF-8 output shape so log lines stay byte-identical.
|
|
43
|
+
return json.dumps(value, separators=(",", ":"), ensure_ascii=False, **kwargs)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# orjson has no free-threaded wheels and refuses to build on ft; fall back to the
|
|
47
|
+
# stdlib json accelerator (always ft-native) when it is absent.
|
|
48
|
+
# See architecture/free-threading.md.
|
|
49
|
+
_serialize_log_to_string = _dumps_orjson if import_checker.is_orjson_installed else _dumps_stdlib
|
|
50
|
+
_json_loads = orjson.loads if import_checker.is_orjson_installed else json.loads
|
|
51
|
+
|
|
52
|
+
|
|
38
53
|
# Meta-keys the producer's structlog processor chain emits at the top level of every
|
|
39
54
|
# rendered line (see lite_bootstrap/instruments/logging_instrument.py). They are not
|
|
40
55
|
# user-supplied extra and are stripped from StructuredLogPayload.extra. If you add a
|
|
@@ -68,8 +83,8 @@ class StructuredLogPayload:
|
|
|
68
83
|
if not formatted.startswith("{"):
|
|
69
84
|
return None
|
|
70
85
|
try:
|
|
71
|
-
loaded =
|
|
72
|
-
except orjson.JSONDecodeError
|
|
86
|
+
loaded = _json_loads(formatted)
|
|
87
|
+
except json.JSONDecodeError: # orjson.JSONDecodeError subclasses this
|
|
73
88
|
return None
|
|
74
89
|
if not isinstance(loaded, dict): # pragma: no cover - JSON starting with "{" is always an object
|
|
75
90
|
return None
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/logging_instrument.py
RENAMED
|
@@ -11,7 +11,7 @@ from lite_bootstrap.instruments.logging_factory import (
|
|
|
11
11
|
RequestProtocol,
|
|
12
12
|
ScopeType,
|
|
13
13
|
_MemoryLoggerFactoryConfig,
|
|
14
|
-
|
|
14
|
+
_serialize_log_to_string,
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
|
|
@@ -117,7 +117,7 @@ class LoggingInstrument(BaseInstrument[LoggingConfig]):
|
|
|
117
117
|
structlog.stdlib.filter_by_level,
|
|
118
118
|
*self.structlog_pre_chain_processors,
|
|
119
119
|
*self.bootstrap_config.logging_extra_processors,
|
|
120
|
-
structlog.processors.JSONRenderer(serializer=
|
|
120
|
+
structlog.processors.JSONRenderer(serializer=_serialize_log_to_string),
|
|
121
121
|
]
|
|
122
122
|
|
|
123
123
|
@property
|
|
@@ -152,7 +152,7 @@ class LoggingInstrument(BaseInstrument[LoggingConfig]):
|
|
|
152
152
|
processors=[
|
|
153
153
|
structlog.stdlib.ProcessorFormatter.remove_processors_meta,
|
|
154
154
|
*self.bootstrap_config.logging_extra_processors,
|
|
155
|
-
structlog.processors.JSONRenderer(serializer=
|
|
155
|
+
structlog.processors.JSONRenderer(serializer=_serialize_log_to_string),
|
|
156
156
|
],
|
|
157
157
|
logger=root_logger,
|
|
158
158
|
)
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/opentelemetry_instrument.py
RENAMED
|
@@ -6,21 +6,29 @@ import urllib.parse
|
|
|
6
6
|
import warnings
|
|
7
7
|
|
|
8
8
|
from lite_bootstrap import import_checker
|
|
9
|
-
from lite_bootstrap.exceptions import TeardownError
|
|
9
|
+
from lite_bootstrap.exceptions import InstrumentDependencyMissingWarning, TeardownError
|
|
10
10
|
from lite_bootstrap.instruments.base import BaseConfig, BaseInstrument
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
if typing.TYPE_CHECKING:
|
|
14
14
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
15
15
|
|
|
16
|
-
if import_checker.
|
|
16
|
+
if import_checker.is_opentelemetry_sdk_installed:
|
|
17
17
|
from opentelemetry.context import Context
|
|
18
|
-
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
19
18
|
from opentelemetry.sdk import resources
|
|
20
19
|
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor, TracerProvider
|
|
21
20
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter, SimpleSpanProcessor
|
|
22
21
|
from opentelemetry.trace import Span, format_span_id, set_tracer_provider
|
|
23
22
|
|
|
23
|
+
if import_checker.is_otlp_grpc_exporter_installed:
|
|
24
|
+
# opentelemetry-api can be present without the grpc otlp exporter package (e.g.
|
|
25
|
+
# lite-bootstrap[fastmcp] pulls bare opentelemetry-api transitively); this must
|
|
26
|
+
# stay a separate guard from is_opentelemetry_installed above.
|
|
27
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter as OTLPGrpcSpanExporter
|
|
28
|
+
|
|
29
|
+
if import_checker.is_otlp_http_exporter_installed:
|
|
30
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter as OTLPHttpSpanExporter
|
|
31
|
+
|
|
24
32
|
if import_checker.is_pyroscope_installed:
|
|
25
33
|
import pyroscope
|
|
26
34
|
|
|
@@ -51,6 +59,7 @@ class OpenTelemetryConfig(OpenTelemetryServiceFieldsConfig):
|
|
|
51
59
|
)
|
|
52
60
|
opentelemetry_endpoint: str | None = None
|
|
53
61
|
opentelemetry_insecure: bool = True
|
|
62
|
+
opentelemetry_exporter_protocol: typing.Literal["grpc", "http"] = "grpc"
|
|
54
63
|
opentelemetry_instrumentors: list[typing.Union[InstrumentorWithParams, "BaseInstrumentor"]] = dataclasses.field(
|
|
55
64
|
default_factory=list
|
|
56
65
|
)
|
|
@@ -70,6 +79,8 @@ class OpenTelemetryConfig(OpenTelemetryServiceFieldsConfig):
|
|
|
70
79
|
|
|
71
80
|
def _parse_remote_insecure_host(self) -> str | None:
|
|
72
81
|
"""Return the host name if the endpoint is insecure AND non-local; else None."""
|
|
82
|
+
if self.opentelemetry_exporter_protocol != "grpc":
|
|
83
|
+
return None
|
|
73
84
|
if not self.opentelemetry_endpoint or not self.opentelemetry_insecure:
|
|
74
85
|
return None
|
|
75
86
|
if self.opentelemetry_endpoint.startswith("unix://"):
|
|
@@ -86,7 +97,7 @@ class OpenTelemetryConfig(OpenTelemetryServiceFieldsConfig):
|
|
|
86
97
|
return host
|
|
87
98
|
|
|
88
99
|
|
|
89
|
-
if import_checker.
|
|
100
|
+
if import_checker.is_opentelemetry_sdk_installed and import_checker.is_pyroscope_installed:
|
|
90
101
|
_OTEL_PROFILE_ID_KEY: typing.Final = "pyroscope.profile.id"
|
|
91
102
|
_PYROSCOPE_SPAN_ID_KEY: typing.Final = "span_id"
|
|
92
103
|
_PYROSCOPE_SPAN_NAME_KEY: typing.Final = "span_name"
|
|
@@ -126,7 +137,7 @@ class OpenTelemetryInstrument(BaseInstrument[OpenTelemetryConfig]):
|
|
|
126
137
|
"""
|
|
127
138
|
|
|
128
139
|
not_ready_message = "opentelemetry_endpoint is empty and opentelemetry_log_traces is False"
|
|
129
|
-
missing_dependency_message = "opentelemetry is not installed"
|
|
140
|
+
missing_dependency_message = "opentelemetry-sdk is not installed"
|
|
130
141
|
_tracer_provider: "TracerProvider | None" = dataclasses.field(
|
|
131
142
|
default_factory=lambda: None, init=False, repr=False, compare=False
|
|
132
143
|
)
|
|
@@ -140,7 +151,9 @@ class OpenTelemetryInstrument(BaseInstrument[OpenTelemetryConfig]):
|
|
|
140
151
|
|
|
141
152
|
@staticmethod
|
|
142
153
|
def check_dependencies() -> bool:
|
|
143
|
-
|
|
154
|
+
# The instrument imports from both the api (opentelemetry.trace/.context) and
|
|
155
|
+
# the sdk (opentelemetry.sdk.*), so it needs both distributions present.
|
|
156
|
+
return import_checker.is_opentelemetry_installed and import_checker.is_opentelemetry_sdk_installed
|
|
144
157
|
|
|
145
158
|
def _build_excluded_urls(self) -> set[str]:
|
|
146
159
|
excluded_urls: set[str] = set(self.bootstrap_config.opentelemetry_excluded_urls)
|
|
@@ -176,15 +189,37 @@ class OpenTelemetryInstrument(BaseInstrument[OpenTelemetryConfig]):
|
|
|
176
189
|
tracer_provider.add_span_processor(PyroscopeSpanProcessor())
|
|
177
190
|
if self.bootstrap_config.opentelemetry_log_traces:
|
|
178
191
|
tracer_provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter(formatter=_format_span)))
|
|
179
|
-
if self.bootstrap_config.opentelemetry_endpoint:
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
192
|
+
if self.bootstrap_config.opentelemetry_endpoint:
|
|
193
|
+
if self.bootstrap_config.opentelemetry_exporter_protocol == "grpc":
|
|
194
|
+
if import_checker.is_otlp_grpc_exporter_installed:
|
|
195
|
+
tracer_provider.add_span_processor(
|
|
196
|
+
BatchSpanProcessor(
|
|
197
|
+
OTLPGrpcSpanExporter(
|
|
198
|
+
endpoint=self.bootstrap_config.opentelemetry_endpoint,
|
|
199
|
+
insecure=self.bootstrap_config.opentelemetry_insecure,
|
|
200
|
+
),
|
|
201
|
+
),
|
|
202
|
+
)
|
|
203
|
+
else:
|
|
204
|
+
warnings.warn(
|
|
205
|
+
"opentelemetry_endpoint is set but the gRPC OTLP exporter is not installed; "
|
|
206
|
+
"spans will not be exported. Install lite-bootstrap[otl].",
|
|
207
|
+
category=InstrumentDependencyMissingWarning,
|
|
208
|
+
stacklevel=2,
|
|
209
|
+
)
|
|
210
|
+
elif import_checker.is_otlp_http_exporter_installed:
|
|
211
|
+
tracer_provider.add_span_processor(
|
|
212
|
+
BatchSpanProcessor(
|
|
213
|
+
OTLPHttpSpanExporter(endpoint=self.bootstrap_config.opentelemetry_endpoint),
|
|
185
214
|
),
|
|
186
|
-
)
|
|
187
|
-
|
|
215
|
+
)
|
|
216
|
+
else:
|
|
217
|
+
warnings.warn(
|
|
218
|
+
"opentelemetry_endpoint is set but the HTTP OTLP exporter is not installed; "
|
|
219
|
+
"spans will not be exported. Install lite-bootstrap[otl-http].",
|
|
220
|
+
category=InstrumentDependencyMissingWarning,
|
|
221
|
+
stacklevel=2,
|
|
222
|
+
)
|
|
188
223
|
for one_instrumentor in self.bootstrap_config.opentelemetry_instrumentors:
|
|
189
224
|
if isinstance(one_instrumentor, InstrumentorWithParams):
|
|
190
225
|
one_instrumentor.instrumentor.instrument(
|
|
@@ -19,10 +19,8 @@ classifiers = [
|
|
|
19
19
|
"Typing :: Typed",
|
|
20
20
|
"Topic :: Software Development :: Libraries",
|
|
21
21
|
]
|
|
22
|
-
version = "1.
|
|
23
|
-
dependencies = [
|
|
24
|
-
"orjson",
|
|
25
|
-
]
|
|
22
|
+
version = "1.3.0"
|
|
23
|
+
dependencies = []
|
|
26
24
|
|
|
27
25
|
[project.urls]
|
|
28
26
|
Homepage = "https://lite-bootstrap.modern-python.org"
|
|
@@ -32,6 +30,9 @@ Issues = "https://github.com/modern-python/lite-bootstrap/issues"
|
|
|
32
30
|
Changelog = "https://github.com/modern-python/lite-bootstrap/releases"
|
|
33
31
|
|
|
34
32
|
[project.optional-dependencies]
|
|
33
|
+
orjson = [
|
|
34
|
+
"orjson",
|
|
35
|
+
]
|
|
35
36
|
sentry = [
|
|
36
37
|
"sentry-sdk",
|
|
37
38
|
]
|
|
@@ -41,7 +42,13 @@ pyroscope = [
|
|
|
41
42
|
otl = [
|
|
42
43
|
"opentelemetry-api",
|
|
43
44
|
"opentelemetry-sdk",
|
|
44
|
-
"opentelemetry-exporter-otlp",
|
|
45
|
+
"opentelemetry-exporter-otlp-proto-grpc",
|
|
46
|
+
"opentelemetry-instrumentation",
|
|
47
|
+
]
|
|
48
|
+
otl-http = [
|
|
49
|
+
"opentelemetry-api",
|
|
50
|
+
"opentelemetry-sdk",
|
|
51
|
+
"opentelemetry-exporter-otlp-proto-http",
|
|
45
52
|
"opentelemetry-instrumentation",
|
|
46
53
|
]
|
|
47
54
|
logging = [
|
|
@@ -170,6 +177,9 @@ ignore = [
|
|
|
170
177
|
isort.lines-after-imports = 2
|
|
171
178
|
isort.no-lines-before = ["standard-library", "local-folder"]
|
|
172
179
|
|
|
180
|
+
[tool.ruff.lint.per-file-ignores]
|
|
181
|
+
"scripts/*.py" = ["INP001"] # standalone scripts, not an importable package
|
|
182
|
+
|
|
173
183
|
[tool.pytest.ini_options]
|
|
174
184
|
addopts = "--cov=. --cov-report term-missing --cov-fail-under=100"
|
|
175
185
|
asyncio_mode = "auto"
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from importlib.util import find_spec
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
is_opentelemetry_installed = find_spec("opentelemetry") is not None
|
|
5
|
-
is_sentry_installed = find_spec("sentry_sdk") is not None
|
|
6
|
-
is_structlog_installed = find_spec("structlog") is not None
|
|
7
|
-
is_prometheus_client_installed = find_spec("prometheus_client") is not None
|
|
8
|
-
is_fastapi_installed = find_spec("fastapi") is not None
|
|
9
|
-
is_litestar_installed = find_spec("litestar") is not None
|
|
10
|
-
is_faststream_installed = find_spec("faststream") is not None
|
|
11
|
-
is_prometheus_fastapi_instrumentator_installed = find_spec("prometheus_fastapi_instrumentator") is not None
|
|
12
|
-
is_fastapi_opentelemetry_installed = (
|
|
13
|
-
is_opentelemetry_installed and find_spec("opentelemetry.instrumentation.fastapi") is not None
|
|
14
|
-
)
|
|
15
|
-
is_litestar_opentelemetry_installed = (
|
|
16
|
-
is_opentelemetry_installed and is_litestar_installed and find_spec("opentelemetry.instrumentation.asgi") is not None
|
|
17
|
-
)
|
|
18
|
-
is_pyroscope_installed = find_spec("pyroscope") is not None
|
|
19
|
-
is_fastmcp_installed = find_spec("fastmcp") is not None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/fastapi_bootstrapper.py
RENAMED
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/fastmcp_bootstrapper.py
RENAMED
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/free_bootstrapper.py
RENAMED
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/bootstrappers/litestar_bootstrapper.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
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/healthchecks_instrument.py
RENAMED
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/prometheus_instrument.py
RENAMED
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/pyroscope_instrument.py
RENAMED
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/sentry_instrument.py
RENAMED
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/instruments/swagger_instrument.py
RENAMED
|
File without changes
|
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/fastapi_docs/redoc.standalone.js
RENAMED
|
File without changes
|
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/fastapi_docs/swagger-ui.css
RENAMED
|
File without changes
|
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/litestar_docs/default.min.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lite_bootstrap-1.2.2 → lite_bootstrap-1.3.0}/lite_bootstrap/static/litestar_docs/swagger-ui.css
RENAMED
|
File without changes
|
|
File without changes
|