Skip to content

Commit 9aa7ea7

Browse files
authored
refactor(telemetry): simplify setup (#51)
overhead/perf metrics are replaced by traces to be more accurate depends on CS-SI/opentelemetry-instrumentation-eodag#18 --------- Signed-off-by: Aubin Lambaré <aubin.lambare@cs-soprasteria.com>
1 parent 4606e49 commit 9aa7ea7

7 files changed

Lines changed: 68 additions & 98 deletions

File tree

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ tests/logging.ini
4646
.Python
4747
env/
4848
venv/
49+
*venv*
4950
build/
5051
develop-eggs/
5152
dist/

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
ARG PYTHON_VERSION=3.12
22

3-
FROM python:${PYTHON_VERSION}-slim as base
3+
FROM python:${PYTHON_VERSION}-slim AS base
44

55
# Any python libraries that require system libraries to be installed will likely
66
# need the following packages in order to build
@@ -12,7 +12,7 @@ RUN apt-get update && \
1212

1313
ENV CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt
1414

15-
FROM base as builder
15+
FROM base AS builder
1616

1717
WORKDIR /app
1818

config/otelcol_config.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,19 @@ exporters:
3131
endpoint: 0.0.0.0:8000
3232
namespace: eodag-otelcol-exporter
3333

34+
otlphttp:
35+
endpoint: "http://jaeger:4318"
36+
tls:
37+
insecure: true
38+
3439
service:
3540

3641
pipelines:
3742

3843
traces:
3944
receivers: [otlp]
4045
processors: [batch]
41-
exporters: [debug]
46+
exporters: [debug,otlphttp]
4247

4348
metrics:
4449
# receivers: [otlp, prometheus]

docker-compose-otel.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
services:
2+
otel-collector:
3+
image: otel/opentelemetry-collector-contrib:latest
4+
command: ["--config=/etc/otelcol-config.yml"]
5+
volumes:
6+
- ./config/otelcol_config.yml:/etc/otelcol-config.yml
7+
ports:
8+
- "4318:4318" # OTLP HTTP
9+
- "8000:8000" # Prometheus metrics
10+
jaeger:
11+
image: jaegertracing/all-in-one:latest
12+
ports:
13+
- "16686:16686" # Jaeger UI
14+
- "14318:4318" # OTLP HTTP

stac_fastapi/eodag/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
164164
if os.getenv("OTEL_EXPORTER_OTLP_ENDPOINT", ""):
165165
from stac_fastapi.eodag.telemetry import instrument_eodag
166166

167-
instrument_eodag(app.state.dag)
168-
# init_cache(app)
167+
instrument_eodag(app)
168+
169169
app.state.stac_metadata_model = stac_metadata_model
170170
yield
171171

stac_fastapi/eodag/telemetry.py

Lines changed: 26 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -20,134 +20,76 @@
2020
from __future__ import annotations
2121

2222
import logging
23-
from typing import TYPE_CHECKING, Union
23+
from typing import Union
2424

25+
from fastapi import FastAPI
2526
from opentelemetry import metrics, trace
2627
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
2728
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
2829
from opentelemetry.instrumentation.eodag import EODAGInstrumentor
2930
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
3031
from opentelemetry.sdk.metrics import MeterProvider
31-
from opentelemetry.sdk.metrics._internal.aggregation import (
32-
ExplicitBucketHistogramAggregation,
33-
)
3432
from opentelemetry.sdk.metrics._internal.export import PeriodicExportingMetricReader
35-
from opentelemetry.sdk.metrics._internal.view import View
36-
37-
# See https://github.com/open-telemetry/opentelemetry-python/issues/4615 for the type ignore
38-
from opentelemetry.sdk.resources import Resource # type: ignore[attr-defined]
33+
from opentelemetry.sdk.resources import Resource
3934
from opentelemetry.sdk.trace import TracerProvider
4035
from opentelemetry.sdk.trace.export import BatchSpanProcessor
41-
from opentelemetry.semconv.resource import ResourceAttributes
4236

43-
if TYPE_CHECKING:
44-
from fastapi import FastAPI
37+
logger = logging.getLogger(__name__)
4538

46-
from eodag import EODataAccessGateway
4739

40+
def get_resource(app: FastAPI) -> Resource:
41+
"""create opentelemetry resource"""
42+
if not getattr(app.state, "resource", None):
43+
app.state.otel_resource = Resource.create().merge(Resource.create({"service.name": "stac-fastapi-eodag"}))
4844

49-
logger = logging.getLogger(__name__)
45+
return app.state.otel_resource
5046

5147

52-
def create_tracer_provider(resource: Resource) -> Union[TracerProvider, trace.TracerProvider]:
48+
def get_tracer_provider(resource: Resource) -> Union[TracerProvider, trace.TracerProvider]:
5349
"""create opentelemetry tracer provider"""
5450
tracer_provider = trace.get_tracer_provider()
5551
if tracer_provider and not isinstance(tracer_provider, trace.ProxyTracerProvider):
56-
logger.debug("Tracer provider already set, skipping creation.")
5752
return tracer_provider
5853

5954
tracer_provider = TracerProvider(resource=resource)
6055
processor = BatchSpanProcessor(OTLPSpanExporter())
6156
tracer_provider.add_span_processor(processor)
6257
trace.set_tracer_provider(tracer_provider)
58+
6359
return tracer_provider
6460

6561

66-
def create_meter_provider(resource: Resource) -> Union[MeterProvider, metrics.MeterProvider]:
62+
def get_meter_provider(resource: Resource) -> Union[MeterProvider, metrics.MeterProvider]:
6763
"""create opentelemetry meter provider"""
6864
meter_provider = metrics.get_meter_provider()
6965
if meter_provider and not isinstance(meter_provider, metrics._internal._ProxyMeterProvider):
70-
logger.debug("Meter provider already set, skipping creation.")
7166
return meter_provider
7267

7368
reader = PeriodicExportingMetricReader(OTLPMetricExporter())
74-
view_histograms: View = View(
75-
instrument_type=metrics.Histogram,
76-
aggregation=ExplicitBucketHistogramAggregation(
77-
boundaries=(
78-
0.25,
79-
0.50,
80-
0.75,
81-
1.0,
82-
1.5,
83-
2.0,
84-
3.0,
85-
4.0,
86-
5.0,
87-
6.0,
88-
7.0,
89-
8.0,
90-
9.0,
91-
10.0,
92-
)
93-
),
94-
)
95-
view_overhead_histograms: View = View(
96-
instrument_type=metrics.Histogram,
97-
instrument_name="*overhead*",
98-
aggregation=ExplicitBucketHistogramAggregation(
99-
boundaries=(
100-
0.030,
101-
0.040,
102-
0.050,
103-
0.060,
104-
0.070,
105-
0.080,
106-
0.090,
107-
0.100,
108-
0.125,
109-
0.150,
110-
0.175,
111-
0.200,
112-
0.250,
113-
0.500,
114-
)
115-
),
116-
)
117-
meter_provider = MeterProvider(
118-
resource=resource,
119-
metric_readers=[reader],
120-
views=(
121-
view_histograms,
122-
view_overhead_histograms,
123-
),
124-
)
69+
meter_provider = MeterProvider(resource=resource, metric_readers=[reader])
12570
metrics.set_meter_provider(meter_provider)
71+
12672
return meter_provider
12773

12874

129-
def instrument_fastapi(
130-
fastapi_app: FastAPI,
131-
) -> None:
75+
def instrument_fastapi(app: FastAPI):
13276
"""Instrument FastAPI app."""
13377
logger.info("Instrument FastAPI app")
134-
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "stac-fastapi-eodag"})
135-
tracer_provider = create_tracer_provider(resource)
136-
meter_provider = create_meter_provider(resource)
78+
resource = get_resource(app)
79+
13780
FastAPIInstrumentor.instrument_app(
138-
app=fastapi_app,
139-
tracer_provider=tracer_provider,
140-
meter_provider=meter_provider,
81+
app=app,
82+
tracer_provider=get_tracer_provider(resource),
83+
meter_provider=get_meter_provider(resource),
14184
)
14285

14386

144-
def instrument_eodag(eodag_api: EODataAccessGateway):
87+
def instrument_eodag(app: FastAPI):
14588
"""Instrument EODAG app"""
14689
logger.info("Instrument EODAG app")
147-
resource = Resource(attributes={ResourceAttributes.SERVICE_NAME: "stac-fastapi-eodag"})
148-
tracer_provider = create_tracer_provider(resource)
149-
meter_provider = create_meter_provider(resource)
150-
EODAGInstrumentor(eodag_api).instrument(
151-
tracer_provider=tracer_provider,
152-
meter_provider=meter_provider,
90+
resource = get_resource(app)
91+
92+
EODAGInstrumentor(app.state.dag).instrument(
93+
tracer_provider=get_tracer_provider(resource),
94+
meter_provider=get_meter_provider(resource),
15395
)

tests/test_telemetry.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
3232

3333
from stac_fastapi.eodag.telemetry import (
34-
create_meter_provider,
35-
create_tracer_provider,
34+
get_meter_provider,
35+
get_tracer_provider,
3636
instrument_eodag,
3737
instrument_fastapi,
3838
)
@@ -47,14 +47,16 @@ def resource() -> Resource:
4747
return Resource.create({"service.name": "test-service"})
4848

4949

50-
def test_create_tracer_provider(resource: Resource) -> None:
51-
"""Test that ``create_tracer_provider`` sets up a tracer provider and records spans.
50+
def test_get_tracer_provider(resource: Resource, monkeypatch: pytest.MonkeyPatch) -> None:
51+
"""Test that ``get_tracer_provider`` sets up a tracer provider and records spans.
5252
5353
:param resource: fixture providing a test Resource
5454
"""
5555
# Patch exporter to in-memory
56+
monkeypatch.setattr("stac_fastapi.eodag.telemetry.OTLPSpanExporter", lambda *a, **kw: InMemorySpanExporter())
57+
5658
exporter = InMemorySpanExporter()
57-
tracer_provider = create_tracer_provider(resource)
59+
tracer_provider = get_tracer_provider(resource)
5860
tracer_provider.add_span_processor(SimpleSpanProcessor(exporter))
5961

6062
tracer = trace.get_tracer("test")
@@ -66,8 +68,8 @@ def test_create_tracer_provider(resource: Resource) -> None:
6668
assert spans[0].name == "test-span"
6769

6870

69-
def test_create_meter_provider(resource: Resource, monkeypatch: pytest.MonkeyPatch) -> None:
70-
"""Test that ``create_meter_provider`` sets up a meter provider and records metrics.
71+
def test_get_meter_provider(resource: Resource, monkeypatch: pytest.MonkeyPatch) -> None:
72+
"""Test that ``get_meter_provider`` sets up a meter provider and records metrics.
7173
7274
:param resource: fixture providing a test Resource
7375
:param monkeypatch: pytest fixture to patch dependencies
@@ -81,7 +83,7 @@ def test_create_meter_provider(resource: Resource, monkeypatch: pytest.MonkeyPat
8183
)
8284

8385
# Call the function (will use patched reader)
84-
meter_provider = create_meter_provider(resource)
86+
meter_provider = get_meter_provider(resource)
8587

8688
# Ensure it’s registered
8789
metrics.set_meter_provider(meter_provider)
@@ -116,7 +118,13 @@ def test_instrument_eodag_runs(monkeypatch: pytest.MonkeyPatch) -> None:
116118
"""
117119

118120
class DummyEODAG:
119-
pass
121+
class State:
122+
def __init__(self):
123+
self.otel_resource = {}
124+
self.dag = {}
125+
126+
def __init__(self):
127+
self.state = self.State()
120128

121129
class DummyInstrumentor:
122130
def __init__(self, api):

0 commit comments

Comments
 (0)