diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap.py b/lite_bootstrap/bootstraps/fastapi_bootstrap.py deleted file mode 100644 index d1980b6..0000000 --- a/lite_bootstrap/bootstraps/fastapi_bootstrap.py +++ /dev/null @@ -1,51 +0,0 @@ -import dataclasses -import typing - -import fastapi -from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor -from sentry_sdk.integrations.asgi import SentryAsgiMiddleware - -from lite_bootstrap.bootstraps.base import BaseBootstrap -from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument -from lite_bootstrap.instruments.sentry_instrument import SentryInstrument - - -@dataclasses.dataclass(kw_only=True) -class FastAPIOpenTelemetryInstrument(OpenTelemetryInstrument): - excluded_urls: list[str] = dataclasses.field(default_factory=list) - app: fastapi.FastAPI = dataclasses.field(init=False) - - def bootstrap(self) -> None: - super().bootstrap() - FastAPIInstrumentor.instrument_app( - app=self.app, - tracer_provider=self.tracer_provider, - excluded_urls=",".join(self.excluded_urls), - ) - - def teardown(self) -> None: - FastAPIInstrumentor.uninstrument_app(self.app) - super().teardown() - - -@dataclasses.dataclass(kw_only=True) -class FastAPISentryInstrument(SentryInstrument): - app: fastapi.FastAPI = dataclasses.field(init=False) - - def bootstrap(self) -> None: - super().bootstrap() - self.app.add_middleware(SentryAsgiMiddleware) # type: ignore[arg-type] - - def teardown(self) -> None: - FastAPIInstrumentor.uninstrument_app(self.app) - super().teardown() - - -@dataclasses.dataclass(kw_only=True, slots=True, frozen=True) -class FastAPIBootstrap(BaseBootstrap): - app: fastapi.FastAPI - instruments: typing.Sequence[FastAPIOpenTelemetryInstrument | FastAPISentryInstrument] - - def __post_init__(self) -> None: - for one_instrument in self.instruments: - one_instrument.app = self.app diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py b/lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py new file mode 100644 index 0000000..51595eb --- /dev/null +++ b/lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py @@ -0,0 +1,25 @@ +import dataclasses +import typing + +import fastapi + +from lite_bootstrap.bootstraps.base import BaseBootstrap +from lite_bootstrap.bootstraps.fastapi_bootstrap.opentelemetry_instrument import FastAPIOpenTelemetryInstrument +from lite_bootstrap.bootstraps.fastapi_bootstrap.sentry_instrument import FastAPISentryInstrument + + +__all__ = [ + "FastAPIBootstrap", + "FastAPIOpenTelemetryInstrument", + "FastAPISentryInstrument", +] + + +@dataclasses.dataclass(kw_only=True, slots=True, frozen=True) +class FastAPIBootstrap(BaseBootstrap): + app: fastapi.FastAPI + instruments: typing.Sequence[FastAPIOpenTelemetryInstrument | FastAPISentryInstrument] + + def __post_init__(self) -> None: + for one_instrument in self.instruments: + one_instrument.app = self.app diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py b/lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py new file mode 100644 index 0000000..bd7e15a --- /dev/null +++ b/lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py @@ -0,0 +1,28 @@ +import contextlib +import dataclasses + +import fastapi + +from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument + + +with contextlib.suppress(ImportError): + from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor + + +@dataclasses.dataclass(kw_only=True) +class FastAPIOpenTelemetryInstrument(OpenTelemetryInstrument): + excluded_urls: list[str] = dataclasses.field(default_factory=list) + app: fastapi.FastAPI = dataclasses.field(init=False) + + def bootstrap(self) -> None: + super().bootstrap() + FastAPIInstrumentor.instrument_app( + app=self.app, + tracer_provider=self.tracer_provider, + excluded_urls=",".join(self.excluded_urls), + ) + + def teardown(self) -> None: + FastAPIInstrumentor.uninstrument_app(self.app) + super().teardown() diff --git a/lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py b/lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py new file mode 100644 index 0000000..a6772b0 --- /dev/null +++ b/lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py @@ -0,0 +1,19 @@ +import contextlib +import dataclasses + +import fastapi + +from lite_bootstrap.instruments.sentry_instrument import SentryInstrument + + +with contextlib.suppress(ImportError): + from sentry_sdk.integrations.asgi import SentryAsgiMiddleware + + +@dataclasses.dataclass(kw_only=True) +class FastAPISentryInstrument(SentryInstrument): + app: fastapi.FastAPI = dataclasses.field(init=False) + + def bootstrap(self) -> None: + super().bootstrap() + self.app.add_middleware(SentryAsgiMiddleware) # type: ignore[arg-type] diff --git a/lite_bootstrap/instruments/opentelemetry_instrument.py b/lite_bootstrap/instruments/opentelemetry_instrument.py index 03ca14b..8974336 100644 --- a/lite_bootstrap/instruments/opentelemetry_instrument.py +++ b/lite_bootstrap/instruments/opentelemetry_instrument.py @@ -1,16 +1,18 @@ +import contextlib import dataclasses import typing -import pydantic -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter -from opentelemetry.instrumentation.instrumentor import BaseInstrumentor # type: ignore[attr-defined] -from opentelemetry.sdk import resources -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor - from lite_bootstrap.instruments.base import BaseInstrument +with contextlib.suppress(ImportError): + from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter + from opentelemetry.instrumentation.instrumentor import BaseInstrumentor # type: ignore[attr-defined] + from opentelemetry.sdk import resources + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import BatchSpanProcessor + + @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) class InstrumentorWithParams: instrumentor: BaseInstrumentor @@ -24,7 +26,7 @@ class OpenTelemetryInstrument(BaseInstrument): container_name: str | None = None endpoint: str | None = None namespace: str | None = None - insecure: bool = pydantic.Field(default=True) + insecure: bool = True instrumentors: list[InstrumentorWithParams | BaseInstrumentor] = dataclasses.field(default_factory=list) tracer_provider: TracerProvider = dataclasses.field(init=False) diff --git a/lite_bootstrap/instruments/sentry_instrument.py b/lite_bootstrap/instruments/sentry_instrument.py index 8b0c4cb..69fe2b2 100644 --- a/lite_bootstrap/instruments/sentry_instrument.py +++ b/lite_bootstrap/instruments/sentry_instrument.py @@ -1,17 +1,19 @@ +import contextlib import dataclasses import typing -import pydantic -import sentry_sdk -from sentry_sdk.integrations import Integration - from lite_bootstrap.instruments.base import BaseInstrument +with contextlib.suppress(ImportError): + import sentry_sdk + from sentry_sdk.integrations import Integration + + @dataclasses.dataclass(kw_only=True, slots=True) class SentryInstrument(BaseInstrument): dsn: str | None = None - sample_rate: float = pydantic.Field(default=1.0, le=1.0, ge=0.0) + sample_rate: float = dataclasses.field(default=1.0) traces_sample_rate: float | None = None environment: str | None = None max_breadcrumbs: int = 15