Skip to content

Commit a8f7ee6

Browse files
authored
Merge pull request #10 from modern-python/5-feature-bootstrap-without-framework
5 feature bootstrap without framework
2 parents 1149e07 + f298635 commit a8f7ee6

File tree

9 files changed

+123
-113
lines changed

9 files changed

+123
-113
lines changed

Justfile

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,3 @@ publish:
2121
rm -rf dist
2222
uv build
2323
uv publish --token $PYPI_TOKEN
24-
25-
hook:
26-
uv run pre-commit install
27-
28-
unhook:
29-
uv run pre-commit uninstall
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import contextlib
2+
import dataclasses
3+
import typing
4+
5+
from lite_bootstrap.bootstraps.base import BaseBootstrap
6+
from lite_bootstrap.instruments.healthchecks_instrument import HealthChecksInstrument, HealthCheckTypedDict
7+
from lite_bootstrap.instruments.logging_instrument import LoggingInstrument
8+
from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument
9+
from lite_bootstrap.instruments.sentry_instrument import SentryInstrument
10+
from lite_bootstrap.service_config import ServiceConfig
11+
12+
13+
with contextlib.suppress(ImportError):
14+
import fastapi
15+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
16+
from opentelemetry.trace import get_tracer_provider
17+
18+
19+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
20+
class FastAPIHealthChecksInstrument(HealthChecksInstrument):
21+
enabled: bool = True
22+
path: str = "/health/"
23+
include_in_schema: bool = False
24+
25+
def build_fastapi_health_check_router(self, service_config: ServiceConfig) -> fastapi.APIRouter:
26+
fastapi_router = fastapi.APIRouter(
27+
tags=["probes"],
28+
include_in_schema=self.include_in_schema,
29+
)
30+
31+
@fastapi_router.get(self.path)
32+
async def health_check_handler() -> HealthCheckTypedDict:
33+
return self.render_health_check_data(service_config)
34+
35+
return fastapi_router
36+
37+
def bootstrap(self, service_config: ServiceConfig, application: fastapi.FastAPI | None = None) -> None:
38+
if application:
39+
application.include_router(self.build_fastapi_health_check_router(service_config))
40+
41+
42+
@dataclasses.dataclass(kw_only=True, frozen=True)
43+
class FastAPILoggingInstrument(LoggingInstrument): ...
44+
45+
46+
@dataclasses.dataclass(kw_only=True, frozen=True)
47+
class FastAPIOpenTelemetryInstrument(OpenTelemetryInstrument):
48+
excluded_urls: list[str] = dataclasses.field(default_factory=list)
49+
50+
def bootstrap(self, service_config: ServiceConfig, application: fastapi.FastAPI | None = None) -> None:
51+
super().bootstrap(service_config, application)
52+
FastAPIInstrumentor.instrument_app(
53+
app=application,
54+
tracer_provider=get_tracer_provider(),
55+
excluded_urls=",".join(self.excluded_urls),
56+
)
57+
58+
def teardown(self, application: fastapi.FastAPI | None = None) -> None:
59+
if application:
60+
FastAPIInstrumentor.uninstrument_app(application)
61+
super().teardown()
62+
63+
64+
@dataclasses.dataclass(kw_only=True, frozen=True)
65+
class FastAPISentryInstrument(SentryInstrument): ...
66+
67+
68+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
69+
class FastAPIBootstrap(BaseBootstrap[fastapi.FastAPI]):
70+
application: fastapi.FastAPI
71+
instruments: typing.Sequence[
72+
FastAPIOpenTelemetryInstrument
73+
| FastAPISentryInstrument
74+
| FastAPIHealthChecksInstrument
75+
| FastAPILoggingInstrument
76+
]
77+
service_config: ServiceConfig

lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py

Lines changed: 0 additions & 33 deletions
This file was deleted.

lite_bootstrap/bootstraps/fastapi_bootstrap/healthchecks_instrument.py

Lines changed: 0 additions & 30 deletions
This file was deleted.

lite_bootstrap/bootstraps/fastapi_bootstrap/logging_instrument.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py

Lines changed: 0 additions & 30 deletions
This file was deleted.

lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import dataclasses
2+
import typing
3+
4+
from lite_bootstrap.bootstraps.base import BaseBootstrap
5+
from lite_bootstrap.instruments.logging_instrument import LoggingInstrument
6+
from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument
7+
from lite_bootstrap.instruments.sentry_instrument import SentryInstrument
8+
from lite_bootstrap.service_config import ServiceConfig
9+
10+
11+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
12+
class FreeBootstrap(BaseBootstrap[None]):
13+
application: None = None
14+
instruments: typing.Sequence[OpenTelemetryInstrument | SentryInstrument | LoggingInstrument]
15+
service_config: ServiceConfig

tests/test_free_bootstrap.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import structlog
2+
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
3+
4+
from lite_bootstrap.bootstraps.free_bootstrap import FreeBootstrap
5+
from lite_bootstrap.instruments.logging_instrument import LoggingInstrument
6+
from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument
7+
from lite_bootstrap.instruments.sentry_instrument import SentryInstrument
8+
from lite_bootstrap.service_config import ServiceConfig
9+
from tests.conftest import CustomInstrumentor
10+
11+
12+
logger = structlog.getLogger(__name__)
13+
14+
15+
def test_free_bootstrap(service_config: ServiceConfig) -> None:
16+
free_bootstrap = FreeBootstrap(
17+
service_config=service_config,
18+
instruments=[
19+
OpenTelemetryInstrument(
20+
endpoint="otl",
21+
instrumentors=[CustomInstrumentor()],
22+
span_exporter=ConsoleSpanExporter(),
23+
),
24+
SentryInstrument(
25+
dsn="https://testdsn@localhost/1",
26+
),
27+
LoggingInstrument(logging_buffer_capacity=0),
28+
],
29+
)
30+
free_bootstrap.bootstrap()
31+
logger.info("testing logging", key="value")

0 commit comments

Comments
 (0)