lite-bootstrap 0.2.0__tar.gz → 0.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-0.2.0 → lite_bootstrap-0.3.0}/PKG-INFO +1 -1
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/bootstraps/base.py +7 -3
- lite_bootstrap-0.3.0/lite_bootstrap/bootstraps/fastapi_bootstrap/__init__.py +28 -0
- lite_bootstrap-0.3.0/lite_bootstrap/bootstraps/fastapi_bootstrap/healthchecks_instrument.py +30 -0
- lite_bootstrap-0.3.0/lite_bootstrap/bootstraps/fastapi_bootstrap/opentelemetry_instrument.py +29 -0
- lite_bootstrap-0.3.0/lite_bootstrap/bootstraps/fastapi_bootstrap/sentry_instrument.py +7 -0
- lite_bootstrap-0.3.0/lite_bootstrap/instruments/base.py +13 -0
- lite_bootstrap-0.3.0/lite_bootstrap/instruments/healthchecks_instrument.py +30 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/instruments/opentelemetry_instrument.py +26 -27
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/instruments/sentry_instrument.py +13 -10
- lite_bootstrap-0.3.0/lite_bootstrap/service_config.py +8 -0
- lite_bootstrap-0.3.0/lite_bootstrap/types.py +4 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/tests/conftest.py +11 -9
- lite_bootstrap-0.3.0/tests/instruments/__init__.py +0 -0
- lite_bootstrap-0.3.0/tests/instruments/test_opentelemetry_instrument.py +31 -0
- lite_bootstrap-0.3.0/tests/instruments/test_sentry_instrument.py +10 -0
- lite_bootstrap-0.3.0/tests/test_fastapi_bootstrap.py +41 -0
- lite_bootstrap-0.2.0/lite_bootstrap/bootstraps/fastapi_bootstrap.py +0 -51
- lite_bootstrap-0.2.0/lite_bootstrap/instruments/base.py +0 -12
- lite_bootstrap-0.2.0/tests/test_fastapi_bootstrap.py +0 -33
- lite_bootstrap-0.2.0/tests/test_opentelemetry_bootstrap.py +0 -24
- lite_bootstrap-0.2.0/tests/test_sentry_bootstrap.py +0 -9
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/.github/workflows/ci.yml +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/.github/workflows/publish.yml +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/.gitignore +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/Justfile +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/README.md +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/__init__.py +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/bootstraps/__init__.py +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/instruments/__init__.py +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/py.typed +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/pyproject.toml +0 -0
- {lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/tests/__init__.py +0 -0
|
@@ -2,17 +2,21 @@ import abc
|
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
4
|
from lite_bootstrap.instruments.base import BaseInstrument
|
|
5
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
6
|
+
from lite_bootstrap.types import ApplicationT
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
class BaseBootstrap(abc.ABC):
|
|
9
|
+
class BaseBootstrap(abc.ABC, typing.Generic[ApplicationT]):
|
|
10
|
+
application: ApplicationT
|
|
8
11
|
instruments: typing.Sequence[BaseInstrument]
|
|
12
|
+
service_config: ServiceConfig
|
|
9
13
|
|
|
10
14
|
def bootstrap(self) -> None:
|
|
11
15
|
for one_instrument in self.instruments:
|
|
12
16
|
if one_instrument.is_ready():
|
|
13
|
-
one_instrument.bootstrap()
|
|
17
|
+
one_instrument.bootstrap(self.service_config, self.application)
|
|
14
18
|
|
|
15
19
|
def teardown(self) -> None:
|
|
16
20
|
for one_instrument in self.instruments:
|
|
17
21
|
if one_instrument.is_ready():
|
|
18
|
-
one_instrument.teardown()
|
|
22
|
+
one_instrument.teardown(self.application)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import fastapi
|
|
5
|
+
|
|
6
|
+
from lite_bootstrap.bootstraps.base import BaseBootstrap
|
|
7
|
+
from lite_bootstrap.bootstraps.fastapi_bootstrap.healthchecks_instrument import FastAPIHealthChecksInstrument
|
|
8
|
+
from lite_bootstrap.bootstraps.fastapi_bootstrap.opentelemetry_instrument import FastAPIOpenTelemetryInstrument
|
|
9
|
+
from lite_bootstrap.bootstraps.fastapi_bootstrap.sentry_instrument import FastAPISentryInstrument
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"FastAPIBootstrap",
|
|
14
|
+
"FastAPIHealthChecksInstrument",
|
|
15
|
+
"FastAPIOpenTelemetryInstrument",
|
|
16
|
+
"FastAPISentryInstrument",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
|
|
23
|
+
class FastAPIBootstrap(BaseBootstrap[fastapi.FastAPI]):
|
|
24
|
+
application: fastapi.FastAPI
|
|
25
|
+
instruments: typing.Sequence[
|
|
26
|
+
FastAPIOpenTelemetryInstrument | FastAPISentryInstrument | FastAPIHealthChecksInstrument
|
|
27
|
+
]
|
|
28
|
+
service_config: ServiceConfig
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import fastapi
|
|
5
|
+
|
|
6
|
+
from lite_bootstrap.instruments.healthchecks_instrument import HealthChecksInstrument, HealthCheckTypedDict
|
|
7
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
|
|
11
|
+
class FastAPIHealthChecksInstrument(HealthChecksInstrument):
|
|
12
|
+
enabled: bool = True
|
|
13
|
+
path: str = "/health/"
|
|
14
|
+
include_in_schema: bool = False
|
|
15
|
+
|
|
16
|
+
def build_fastapi_health_check_router(self, service_config: ServiceConfig) -> fastapi.APIRouter:
|
|
17
|
+
fastapi_router: typing.Final = fastapi.APIRouter(
|
|
18
|
+
tags=["probes"],
|
|
19
|
+
include_in_schema=self.include_in_schema,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
@fastapi_router.get(self.path)
|
|
23
|
+
async def health_check_handler() -> HealthCheckTypedDict:
|
|
24
|
+
return self.render_health_check_data(service_config)
|
|
25
|
+
|
|
26
|
+
return fastapi_router
|
|
27
|
+
|
|
28
|
+
def bootstrap(self, service_config: ServiceConfig, application: fastapi.FastAPI | None = None) -> None:
|
|
29
|
+
if application:
|
|
30
|
+
application.include_router(self.build_fastapi_health_check_router(service_config))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import dataclasses
|
|
3
|
+
|
|
4
|
+
import fastapi
|
|
5
|
+
|
|
6
|
+
from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument
|
|
7
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
with contextlib.suppress(ImportError):
|
|
11
|
+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclasses.dataclass(kw_only=True)
|
|
15
|
+
class FastAPIOpenTelemetryInstrument(OpenTelemetryInstrument):
|
|
16
|
+
excluded_urls: list[str] = dataclasses.field(default_factory=list)
|
|
17
|
+
|
|
18
|
+
def bootstrap(self, service_config: ServiceConfig, application: fastapi.FastAPI | None = None) -> None:
|
|
19
|
+
super().bootstrap(service_config, application)
|
|
20
|
+
FastAPIInstrumentor.instrument_app(
|
|
21
|
+
app=application,
|
|
22
|
+
tracer_provider=self.tracer_provider,
|
|
23
|
+
excluded_urls=",".join(self.excluded_urls),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def teardown(self, application: fastapi.FastAPI | None = None) -> None:
|
|
27
|
+
if application:
|
|
28
|
+
FastAPIInstrumentor.uninstrument_app(application)
|
|
29
|
+
super().teardown()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
4
|
+
from lite_bootstrap.types import ApplicationT
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BaseInstrument(abc.ABC):
|
|
8
|
+
def bootstrap(self, service_config: ServiceConfig, application: ApplicationT | None = None) -> None: ... # noqa: B027
|
|
9
|
+
|
|
10
|
+
def teardown(self, application: ApplicationT | None = None) -> None: ... # noqa: B027
|
|
11
|
+
|
|
12
|
+
@abc.abstractmethod
|
|
13
|
+
def is_ready(self) -> bool: ...
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
import typing_extensions
|
|
4
|
+
|
|
5
|
+
from lite_bootstrap.instruments.base import BaseInstrument
|
|
6
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HealthCheckTypedDict(typing_extensions.TypedDict, total=False):
|
|
10
|
+
service_version: str | None
|
|
11
|
+
service_name: str | None
|
|
12
|
+
health_status: bool
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
|
|
16
|
+
class HealthChecksInstrument(BaseInstrument):
|
|
17
|
+
enabled: bool = True
|
|
18
|
+
path: str = "/health/"
|
|
19
|
+
include_in_schema: bool = False
|
|
20
|
+
|
|
21
|
+
def is_ready(self) -> bool:
|
|
22
|
+
return self.enabled
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def render_health_check_data(service_config: ServiceConfig) -> HealthCheckTypedDict:
|
|
26
|
+
return {
|
|
27
|
+
"service_version": service_config.service_version,
|
|
28
|
+
"service_name": service_config.service_name,
|
|
29
|
+
"health_status": True,
|
|
30
|
+
}
|
{lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/instruments/opentelemetry_instrument.py
RENAMED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import dataclasses
|
|
2
3
|
import typing
|
|
3
4
|
|
|
4
|
-
import pydantic
|
|
5
|
-
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
6
|
-
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor # type: ignore[attr-defined]
|
|
7
|
-
from opentelemetry.sdk import resources
|
|
8
|
-
from opentelemetry.sdk.trace import TracerProvider
|
|
9
|
-
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
10
|
-
|
|
11
5
|
from lite_bootstrap.instruments.base import BaseInstrument
|
|
6
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
7
|
+
from lite_bootstrap.types import ApplicationT
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
with contextlib.suppress(ImportError):
|
|
11
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
|
|
12
|
+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor # type: ignore[attr-defined]
|
|
13
|
+
from opentelemetry.sdk import resources
|
|
14
|
+
from opentelemetry.sdk.trace import TracerProvider
|
|
15
|
+
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
|
|
@@ -19,37 +23,24 @@ class InstrumentorWithParams:
|
|
|
19
23
|
|
|
20
24
|
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
21
25
|
class OpenTelemetryInstrument(BaseInstrument):
|
|
22
|
-
service_version: str = "1.0.0"
|
|
23
|
-
service_name: str | None = None
|
|
24
26
|
container_name: str | None = None
|
|
25
27
|
endpoint: str | None = None
|
|
26
28
|
namespace: str | None = None
|
|
27
|
-
insecure: bool =
|
|
29
|
+
insecure: bool = True
|
|
28
30
|
instrumentors: list[InstrumentorWithParams | BaseInstrumentor] = dataclasses.field(default_factory=list)
|
|
31
|
+
span_exporter: SpanExporter | None = None
|
|
29
32
|
|
|
30
33
|
tracer_provider: TracerProvider = dataclasses.field(init=False)
|
|
31
34
|
|
|
32
35
|
def is_ready(self) -> bool:
|
|
33
|
-
return
|
|
34
|
-
(
|
|
35
|
-
self.endpoint,
|
|
36
|
-
self.service_name,
|
|
37
|
-
),
|
|
38
|
-
)
|
|
36
|
+
return bool(self.endpoint)
|
|
39
37
|
|
|
40
|
-
def
|
|
41
|
-
for one_instrumentor in self.instrumentors:
|
|
42
|
-
if isinstance(one_instrumentor, InstrumentorWithParams):
|
|
43
|
-
one_instrumentor.instrumentor.uninstrument(**one_instrumentor.additional_params)
|
|
44
|
-
else:
|
|
45
|
-
one_instrumentor.uninstrument()
|
|
46
|
-
|
|
47
|
-
def bootstrap(self) -> None:
|
|
38
|
+
def bootstrap(self, service_config: ServiceConfig, _: ApplicationT | None = None) -> None:
|
|
48
39
|
attributes = {
|
|
49
|
-
resources.SERVICE_NAME:
|
|
40
|
+
resources.SERVICE_NAME: service_config.service_name,
|
|
50
41
|
resources.TELEMETRY_SDK_LANGUAGE: "python",
|
|
51
42
|
resources.SERVICE_NAMESPACE: self.namespace,
|
|
52
|
-
resources.SERVICE_VERSION:
|
|
43
|
+
resources.SERVICE_VERSION: service_config.service_version,
|
|
53
44
|
resources.CONTAINER_NAME: self.container_name,
|
|
54
45
|
}
|
|
55
46
|
resource: typing.Final = resources.Resource.create(
|
|
@@ -58,7 +49,8 @@ class OpenTelemetryInstrument(BaseInstrument):
|
|
|
58
49
|
self.tracer_provider = TracerProvider(resource=resource)
|
|
59
50
|
self.tracer_provider.add_span_processor(
|
|
60
51
|
BatchSpanProcessor(
|
|
61
|
-
|
|
52
|
+
self.span_exporter
|
|
53
|
+
or OTLPSpanExporter(
|
|
62
54
|
endpoint=self.endpoint,
|
|
63
55
|
insecure=self.insecure,
|
|
64
56
|
),
|
|
@@ -72,3 +64,10 @@ class OpenTelemetryInstrument(BaseInstrument):
|
|
|
72
64
|
)
|
|
73
65
|
else:
|
|
74
66
|
one_instrumentor.instrument(tracer_provider=self.tracer_provider)
|
|
67
|
+
|
|
68
|
+
def teardown(self, _: ApplicationT | None = None) -> None:
|
|
69
|
+
for one_instrumentor in self.instrumentors:
|
|
70
|
+
if isinstance(one_instrumentor, InstrumentorWithParams):
|
|
71
|
+
one_instrumentor.instrumentor.uninstrument(**one_instrumentor.additional_params)
|
|
72
|
+
else:
|
|
73
|
+
one_instrumentor.uninstrument()
|
{lite_bootstrap-0.2.0 → lite_bootstrap-0.3.0}/lite_bootstrap/instruments/sentry_instrument.py
RENAMED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import dataclasses
|
|
2
3
|
import typing
|
|
3
4
|
|
|
4
|
-
import pydantic
|
|
5
|
-
import sentry_sdk
|
|
6
|
-
from sentry_sdk.integrations import Integration
|
|
7
|
-
|
|
8
5
|
from lite_bootstrap.instruments.base import BaseInstrument
|
|
6
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
7
|
+
from lite_bootstrap.types import ApplicationT
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
with contextlib.suppress(ImportError):
|
|
11
|
+
import sentry_sdk
|
|
12
|
+
from sentry_sdk.integrations import Integration
|
|
9
13
|
|
|
10
14
|
|
|
11
|
-
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
15
|
+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
|
|
12
16
|
class SentryInstrument(BaseInstrument):
|
|
13
17
|
dsn: str | None = None
|
|
14
|
-
sample_rate: float =
|
|
18
|
+
sample_rate: float = dataclasses.field(default=1.0)
|
|
15
19
|
traces_sample_rate: float | None = None
|
|
16
|
-
environment: str | None = None
|
|
17
20
|
max_breadcrumbs: int = 15
|
|
18
21
|
max_value_length: int = 16384
|
|
19
22
|
attach_stacktrace: bool = True
|
|
@@ -24,12 +27,12 @@ class SentryInstrument(BaseInstrument):
|
|
|
24
27
|
def is_ready(self) -> bool:
|
|
25
28
|
return bool(self.dsn)
|
|
26
29
|
|
|
27
|
-
def bootstrap(self) -> None:
|
|
30
|
+
def bootstrap(self, service_config: ServiceConfig, _: ApplicationT | None = None) -> None:
|
|
28
31
|
sentry_sdk.init(
|
|
29
32
|
dsn=self.dsn,
|
|
30
33
|
sample_rate=self.sample_rate,
|
|
31
34
|
traces_sample_rate=self.traces_sample_rate,
|
|
32
|
-
environment=
|
|
35
|
+
environment=service_config.service_environment,
|
|
33
36
|
max_breadcrumbs=self.max_breadcrumbs,
|
|
34
37
|
max_value_length=self.max_value_length,
|
|
35
38
|
attach_stacktrace=self.attach_stacktrace,
|
|
@@ -39,4 +42,4 @@ class SentryInstrument(BaseInstrument):
|
|
|
39
42
|
tags: dict[str, str] = self.tags or {}
|
|
40
43
|
sentry_sdk.set_tags(tags)
|
|
41
44
|
|
|
42
|
-
def teardown(self) -> None: ...
|
|
45
|
+
def teardown(self, application: ApplicationT | None = None) -> None: ...
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
-
from fastapi import
|
|
5
|
-
from fastapi.responses import JSONResponse
|
|
4
|
+
from fastapi import FastAPI
|
|
6
5
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor # type: ignore[attr-defined]
|
|
7
6
|
|
|
7
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
8
|
+
|
|
8
9
|
|
|
9
10
|
class CustomInstrumentor(BaseInstrumentor): # type: ignore[misc]
|
|
10
11
|
def instrumentation_dependencies(self) -> typing.Collection[str]:
|
|
@@ -16,12 +17,13 @@ class CustomInstrumentor(BaseInstrumentor): # type: ignore[misc]
|
|
|
16
17
|
|
|
17
18
|
@pytest.fixture
|
|
18
19
|
def fastapi_app() -> FastAPI:
|
|
19
|
-
|
|
20
|
-
router: typing.Final = APIRouter()
|
|
20
|
+
return FastAPI()
|
|
21
21
|
|
|
22
|
-
@router.get("/test")
|
|
23
|
-
async def for_test_endpoint() -> JSONResponse:
|
|
24
|
-
return JSONResponse(content={"key": "value"})
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def service_config() -> ServiceConfig:
|
|
25
|
+
return ServiceConfig(
|
|
26
|
+
service_name="microservice",
|
|
27
|
+
service_version="2.0.0",
|
|
28
|
+
service_environment="test",
|
|
29
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
|
|
2
|
+
|
|
3
|
+
from lite_bootstrap.instruments.opentelemetry_instrument import InstrumentorWithParams, OpenTelemetryInstrument
|
|
4
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
5
|
+
from tests.conftest import CustomInstrumentor
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_opentelemetry_instrument(service_config: ServiceConfig) -> None:
|
|
9
|
+
opentelemetry = OpenTelemetryInstrument(
|
|
10
|
+
endpoint="otl",
|
|
11
|
+
instrumentors=[
|
|
12
|
+
InstrumentorWithParams(instrumentor=CustomInstrumentor(), additional_params={"key": "value"}),
|
|
13
|
+
CustomInstrumentor(),
|
|
14
|
+
],
|
|
15
|
+
span_exporter=ConsoleSpanExporter(),
|
|
16
|
+
)
|
|
17
|
+
try:
|
|
18
|
+
opentelemetry.bootstrap(service_config)
|
|
19
|
+
finally:
|
|
20
|
+
opentelemetry.teardown()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_opentelemetry_instrument_empty_instruments(service_config: ServiceConfig) -> None:
|
|
24
|
+
opentelemetry = OpenTelemetryInstrument(
|
|
25
|
+
endpoint="otl",
|
|
26
|
+
span_exporter=ConsoleSpanExporter(),
|
|
27
|
+
)
|
|
28
|
+
try:
|
|
29
|
+
opentelemetry.bootstrap(service_config)
|
|
30
|
+
finally:
|
|
31
|
+
opentelemetry.teardown()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from lite_bootstrap.instruments.sentry_instrument import SentryInstrument
|
|
2
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_sentry_instrument(service_config: ServiceConfig) -> None:
|
|
6
|
+
SentryInstrument(dsn="https://testdsn@localhost/1", tags={"tag": "value"}).bootstrap(service_config)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_sentry_instrument_empty_dsn(service_config: ServiceConfig) -> None:
|
|
10
|
+
SentryInstrument(dsn="").bootstrap(service_config)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from fastapi import FastAPI
|
|
2
|
+
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
|
|
3
|
+
from starlette import status
|
|
4
|
+
from starlette.testclient import TestClient
|
|
5
|
+
|
|
6
|
+
from lite_bootstrap.bootstraps.fastapi_bootstrap import (
|
|
7
|
+
FastAPIBootstrap,
|
|
8
|
+
FastAPIHealthChecksInstrument,
|
|
9
|
+
FastAPIOpenTelemetryInstrument,
|
|
10
|
+
FastAPISentryInstrument,
|
|
11
|
+
)
|
|
12
|
+
from lite_bootstrap.service_config import ServiceConfig
|
|
13
|
+
from tests.conftest import CustomInstrumentor
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_fastapi_bootstrap(fastapi_app: FastAPI, service_config: ServiceConfig) -> None:
|
|
17
|
+
fastapi_bootstrap = FastAPIBootstrap(
|
|
18
|
+
application=fastapi_app,
|
|
19
|
+
service_config=service_config,
|
|
20
|
+
instruments=[
|
|
21
|
+
FastAPIOpenTelemetryInstrument(
|
|
22
|
+
endpoint="otl",
|
|
23
|
+
instrumentors=[CustomInstrumentor()],
|
|
24
|
+
span_exporter=ConsoleSpanExporter(),
|
|
25
|
+
),
|
|
26
|
+
FastAPISentryInstrument(
|
|
27
|
+
dsn="https://testdsn@localhost/1",
|
|
28
|
+
),
|
|
29
|
+
FastAPIHealthChecksInstrument(
|
|
30
|
+
path="/health/",
|
|
31
|
+
),
|
|
32
|
+
],
|
|
33
|
+
)
|
|
34
|
+
fastapi_bootstrap.bootstrap()
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
response = TestClient(fastapi_app).get("/health/")
|
|
38
|
+
assert response.status_code == status.HTTP_200_OK
|
|
39
|
+
assert response.json() == {"health_status": True, "service_name": "microservice", "service_version": "2.0.0"}
|
|
40
|
+
finally:
|
|
41
|
+
fastapi_bootstrap.teardown()
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import dataclasses
|
|
2
|
-
import typing
|
|
3
|
-
|
|
4
|
-
import fastapi
|
|
5
|
-
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
|
|
6
|
-
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
|
|
7
|
-
|
|
8
|
-
from lite_bootstrap.bootstraps.base import BaseBootstrap
|
|
9
|
-
from lite_bootstrap.instruments.opentelemetry_instrument import OpenTelemetryInstrument
|
|
10
|
-
from lite_bootstrap.instruments.sentry_instrument import SentryInstrument
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@dataclasses.dataclass(kw_only=True)
|
|
14
|
-
class FastAPIOpenTelemetryInstrument(OpenTelemetryInstrument):
|
|
15
|
-
excluded_urls: list[str] = dataclasses.field(default_factory=list)
|
|
16
|
-
app: fastapi.FastAPI = dataclasses.field(init=False)
|
|
17
|
-
|
|
18
|
-
def bootstrap(self) -> None:
|
|
19
|
-
super().bootstrap()
|
|
20
|
-
FastAPIInstrumentor.instrument_app(
|
|
21
|
-
app=self.app,
|
|
22
|
-
tracer_provider=self.tracer_provider,
|
|
23
|
-
excluded_urls=",".join(self.excluded_urls),
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
def teardown(self) -> None:
|
|
27
|
-
FastAPIInstrumentor.uninstrument_app(self.app)
|
|
28
|
-
super().teardown()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@dataclasses.dataclass(kw_only=True)
|
|
32
|
-
class FastAPISentryInstrument(SentryInstrument):
|
|
33
|
-
app: fastapi.FastAPI = dataclasses.field(init=False)
|
|
34
|
-
|
|
35
|
-
def bootstrap(self) -> None:
|
|
36
|
-
super().bootstrap()
|
|
37
|
-
self.app.add_middleware(SentryAsgiMiddleware) # type: ignore[arg-type]
|
|
38
|
-
|
|
39
|
-
def teardown(self) -> None:
|
|
40
|
-
FastAPIInstrumentor.uninstrument_app(self.app)
|
|
41
|
-
super().teardown()
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
|
|
45
|
-
class FastAPIBootstrap(BaseBootstrap):
|
|
46
|
-
app: fastapi.FastAPI
|
|
47
|
-
instruments: typing.Sequence[FastAPIOpenTelemetryInstrument | FastAPISentryInstrument]
|
|
48
|
-
|
|
49
|
-
def __post_init__(self) -> None:
|
|
50
|
-
for one_instrument in self.instruments:
|
|
51
|
-
one_instrument.app = self.app
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
from fastapi import FastAPI
|
|
2
|
-
from starlette import status
|
|
3
|
-
from starlette.testclient import TestClient
|
|
4
|
-
|
|
5
|
-
from lite_bootstrap.bootstraps.fastapi_bootstrap import (
|
|
6
|
-
FastAPIBootstrap,
|
|
7
|
-
FastAPIOpenTelemetryInstrument,
|
|
8
|
-
FastAPISentryInstrument,
|
|
9
|
-
)
|
|
10
|
-
from tests.conftest import CustomInstrumentor
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def test_fastapi_bootstrap(fastapi_app: FastAPI) -> None:
|
|
14
|
-
fastapi_bootstrap = FastAPIBootstrap(
|
|
15
|
-
app=fastapi_app,
|
|
16
|
-
instruments=[
|
|
17
|
-
FastAPIOpenTelemetryInstrument(
|
|
18
|
-
endpoint="localhost",
|
|
19
|
-
service_name="test_service",
|
|
20
|
-
instrumentors=[CustomInstrumentor()],
|
|
21
|
-
),
|
|
22
|
-
FastAPISentryInstrument(
|
|
23
|
-
dsn="https://testdsn@test.sentry.com/1",
|
|
24
|
-
),
|
|
25
|
-
],
|
|
26
|
-
)
|
|
27
|
-
fastapi_bootstrap.bootstrap()
|
|
28
|
-
fastapi_bootstrap.teardown()
|
|
29
|
-
|
|
30
|
-
response = TestClient(fastapi_app).get("/test")
|
|
31
|
-
assert response.status_code == status.HTTP_200_OK
|
|
32
|
-
|
|
33
|
-
fastapi_bootstrap.teardown()
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
from lite_bootstrap.instruments.opentelemetry_instrument import InstrumentorWithParams, OpenTelemetryInstrument
|
|
2
|
-
from tests.conftest import CustomInstrumentor
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def test_bootstrap_opentelemetry() -> None:
|
|
6
|
-
opentelemetry = OpenTelemetryInstrument(
|
|
7
|
-
endpoint="localhost",
|
|
8
|
-
service_name="test_service",
|
|
9
|
-
instrumentors=[
|
|
10
|
-
InstrumentorWithParams(instrumentor=CustomInstrumentor(), additional_params={"key": "value"}),
|
|
11
|
-
CustomInstrumentor(),
|
|
12
|
-
],
|
|
13
|
-
)
|
|
14
|
-
opentelemetry.bootstrap()
|
|
15
|
-
opentelemetry.teardown()
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_bootstrap_opentelemetry_empty_instruments() -> None:
|
|
19
|
-
opentelemetry = OpenTelemetryInstrument(
|
|
20
|
-
endpoint="localhost",
|
|
21
|
-
service_name="test_service",
|
|
22
|
-
)
|
|
23
|
-
opentelemetry.bootstrap()
|
|
24
|
-
opentelemetry.teardown()
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
from lite_bootstrap.instruments.sentry_instrument import SentryInstrument
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def test_sentry_bootstrap() -> None:
|
|
5
|
-
SentryInstrument(dsn="https://testdsn@test.sentry.com/1", tags={"tag": "value"}).bootstrap()
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def test_sentry_bootstrap_empty_dsn() -> None:
|
|
9
|
-
SentryInstrument(dsn="").bootstrap()
|
|
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
|