Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
dbb2722
add aio pika instrumentation dependency
bisgaard-itis May 6, 2025
2458561
upgrade dependency accross entire repo
bisgaard-itis May 6, 2025
8cf4d44
instrument aipika rabbit client
bisgaard-itis May 6, 2025
d85dcb1
add opentelemetry instrumentation celery dependency
bisgaard-itis May 6, 2025
273896e
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 6, 2025
0f685de
autoinstrument aiopika in aihttp case
bisgaard-itis May 6, 2025
c7c8469
remove asyncpg and aiopg instrumentation
bisgaard-itis May 6, 2025
8763e94
remove asyncpg instrumentation
bisgaard-itis May 6, 2025
bd45887
Revert "autoinstrument aiopika in aihttp case"
bisgaard-itis May 7, 2025
3755521
autoinstrument aiopika in aihttp case
bisgaard-itis May 6, 2025
b37c053
Revert "remove asyncpg instrumentation"
bisgaard-itis May 7, 2025
02523a7
Revert "remove asyncpg and aiopg instrumentation"
bisgaard-itis May 7, 2025
2601b2a
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 7, 2025
08397b0
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 12, 2025
d4c643a
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 12, 2025
d762da5
attempt to solve issue by fixing otel fixture
bisgaard-itis May 12, 2025
72fa339
ensure to delete env
bisgaard-itis May 12, 2025
6036749
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 19, 2025
660d00c
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 23, 2025
91a0f99
upgrade aiopika instrumentation lib after merging master
bisgaard-itis May 23, 2025
efa751c
add lifespan to tracing in fastapi case
bisgaard-itis May 23, 2025
2c6afb9
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 23, 2025
3840e07
use lifespan manager approach
bisgaard-itis May 23, 2025
90c14ea
disable pylint bare expose
bisgaard-itis May 23, 2025
c5e4c1b
add getter for tracing instrumentation lifespan
bisgaard-itis May 23, 2025
dabc522
fix pylint
bisgaard-itis May 23, 2025
10689e9
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 23, 2025
e7ae167
remove call to private method
bisgaard-itis May 23, 2025
9b7bd1a
add lifespan management to tracing instrumentation in aiohttp
bisgaard-itis May 23, 2025
bd81753
make pylint happy
bisgaard-itis May 23, 2025
6466494
assert app
bisgaard-itis May 23, 2025
8458404
ensure lifespan manager works for fastapi
bisgaard-itis May 23, 2025
f545694
Merge branch 'master' into 7634-opentelemetry-instrument-rpc-clients
bisgaard-itis May 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/aws-library/requirements/_base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ opentelemetry-api==1.30.0
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
# opentelemetry-instrumentation
# opentelemetry-instrumentation-aio-pika
# opentelemetry-instrumentation-botocore
# opentelemetry-instrumentation-logging
# opentelemetry-instrumentation-redis
Expand All @@ -164,10 +165,13 @@ opentelemetry-exporter-otlp-proto-http==1.30.0
# via opentelemetry-exporter-otlp
opentelemetry-instrumentation==0.51b0
# via
# opentelemetry-instrumentation-aio-pika
# opentelemetry-instrumentation-botocore
# opentelemetry-instrumentation-logging
# opentelemetry-instrumentation-redis
# opentelemetry-instrumentation-requests
opentelemetry-instrumentation-aio-pika==0.51b0
# via -r requirements/../../../packages/service-library/requirements/_base.in
opentelemetry-instrumentation-botocore==0.51b0
# via -r requirements/_base.in
opentelemetry-instrumentation-logging==0.51b0
Expand Down Expand Up @@ -431,6 +435,7 @@ wrapt==1.17.2
# aiobotocore
# deprecated
# opentelemetry-instrumentation
# opentelemetry-instrumentation-aio-pika
# opentelemetry-instrumentation-redis
yarl==1.18.3
# via
Expand Down
1 change: 1 addition & 0 deletions packages/service-library/requirements/_base.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ arrow # date/time
faststream
opentelemetry-api
opentelemetry-exporter-otlp
opentelemetry-instrumentation-aio-pika
opentelemetry-instrumentation-logging
opentelemetry-instrumentation-redis
opentelemetry-instrumentation-requests
Expand Down
5 changes: 5 additions & 0 deletions packages/service-library/requirements/_base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ opentelemetry-api==1.30.0
# opentelemetry-exporter-otlp-proto-grpc
# opentelemetry-exporter-otlp-proto-http
# opentelemetry-instrumentation
# opentelemetry-instrumentation-aio-pika
# opentelemetry-instrumentation-logging
# opentelemetry-instrumentation-redis
# opentelemetry-instrumentation-requests
Expand All @@ -120,9 +121,12 @@ opentelemetry-exporter-otlp-proto-http==1.30.0
# via opentelemetry-exporter-otlp
opentelemetry-instrumentation==0.51b0
# via
# opentelemetry-instrumentation-aio-pika
# opentelemetry-instrumentation-logging
# opentelemetry-instrumentation-redis
# opentelemetry-instrumentation-requests
opentelemetry-instrumentation-aio-pika==0.51b0
# via -r requirements/_base.in
opentelemetry-instrumentation-logging==0.51b0
# via -r requirements/_base.in
opentelemetry-instrumentation-redis==0.51b0
Expand Down Expand Up @@ -297,6 +301,7 @@ wrapt==1.17.2
# via
# deprecated
# opentelemetry-instrumentation
# opentelemetry-instrumentation-aio-pika
# opentelemetry-instrumentation-redis
yarl==1.18.3
# via
Expand Down
67 changes: 62 additions & 5 deletions packages/service-library/src/servicelib/aiohttp/tracing.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
""" Adds aiohttp middleware for tracing using opentelemetry instrumentation.

"""
"""Adds aiohttp middleware for tracing using opentelemetry instrumentation."""

import logging
from collections.abc import AsyncIterator, Callable

from aiohttp import web
from opentelemetry import trace
Expand Down Expand Up @@ -44,8 +43,15 @@
except ImportError:
HAS_REQUESTS = False

try:
from opentelemetry.instrumentation.aio_pika import AioPikaInstrumentor

HAS_AIO_PIKA = True
except ImportError:
HAS_AIO_PIKA = False


def setup_tracing(
def _startup(
app: web.Application,
tracing_settings: TracingSettings,
service_name: str,
Expand Down Expand Up @@ -74,7 +80,9 @@ def setup_tracing(
trace.set_tracer_provider(TracerProvider(resource=resource))
tracer_provider: trace.TracerProvider = trace.get_tracer_provider()

tracing_destination: str = f"{URL(opentelemetry_collector_endpoint).with_port(opentelemetry_collector_port).with_path('/v1/traces')}"
tracing_destination: str = (
f"{URL(opentelemetry_collector_endpoint).with_port(opentelemetry_collector_port).with_path('/v1/traces')}"
)

_logger.info(
"Trying to connect service %s to tracing collector at %s.",
Expand Down Expand Up @@ -128,3 +136,52 @@ def setup_tracing(
msg="Attempting to add requests opentelemetry autoinstrumentation...",
):
RequestsInstrumentor().instrument()

if HAS_AIO_PIKA:
with log_context(
_logger,
logging.INFO,
msg="Attempting to add aio_pika opentelemetry autoinstrumentation...",
):
AioPikaInstrumentor().instrument()


def _shutdown() -> None:
"""Uninstruments all opentelemetry instrumentors that were instrumented."""
try:
AioHttpClientInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument AioHttpClientInstrumentor")
if HAS_AIOPG:
try:
AiopgInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument AiopgInstrumentor")
if HAS_BOTOCORE:
try:
BotocoreInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument BotocoreInstrumentor")
if HAS_REQUESTS:
try:
RequestsInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument RequestsInstrumentor")
if HAS_AIO_PIKA:
try:
AioPikaInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument AioPikaInstrumentor")


def get_tracing_lifespan(
app: web.Application, tracing_settings: TracingSettings, service_name: str
) -> Callable[[web.Application], AsyncIterator]:
_startup(app=app, tracing_settings=tracing_settings, service_name=service_name)

async def tracing_lifespan(app: web.Application):
assert app # nosec
yield
_shutdown()

return tracing_lifespan
98 changes: 89 additions & 9 deletions packages/service-library/src/servicelib/fastapi/tracing.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
""" Adds fastapi middleware for tracing using opentelemetry instrumentation.

"""
"""Adds fastapi middleware for tracing using opentelemetry instrumentation."""

import logging
from collections.abc import AsyncIterator

from fastapi import FastAPI
from fastapi_lifespan_manager import State
from httpx import AsyncClient, Client
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
Expand Down Expand Up @@ -60,10 +60,17 @@
except ImportError:
HAS_REQUESTS = False

try:
from opentelemetry.instrumentation.aio_pika.aio_pika_instrumentor import (
AioPikaInstrumentor,
)

def initialize_tracing(
app: FastAPI, tracing_settings: TracingSettings, service_name: str
) -> None:
HAS_AIOPIKA_INSTRUMENTOR = True
except ImportError:
HAS_AIOPIKA_INSTRUMENTOR = False


def _startup(tracing_settings: TracingSettings, service_name: str) -> None:
if (
not tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT
and not tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_PORT
Expand All @@ -80,7 +87,9 @@ def initialize_tracing(
f"{tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT}"
)

tracing_destination: str = f"{URL(opentelemetry_collector_endpoint).with_port(tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_PORT).with_path('/v1/traces')}"
tracing_destination: str = (
f"{URL(opentelemetry_collector_endpoint).with_port(tracing_settings.TRACING_OPENTELEMETRY_COLLECTOR_PORT).with_path('/v1/traces')}"
)

_logger.info(
"Trying to connect service %s to opentelemetry tracing collector at %s.",
Expand All @@ -91,16 +100,22 @@ def initialize_tracing(
otlp_exporter = OTLPSpanExporterHTTP(endpoint=tracing_destination)
span_processor = BatchSpanProcessor(otlp_exporter)
global_tracer_provider.add_span_processor(span_processor)
# Instrument FastAPI
FastAPIInstrumentor().instrument_app(app)

FastAPIInstrumentor().instrument()
if HAS_AIOPG:
with log_context(
_logger,
logging.INFO,
msg="Attempting to add asyncpg opentelemetry autoinstrumentation...",
):
AiopgInstrumentor().instrument()
if HAS_AIOPIKA_INSTRUMENTOR:
with log_context(
_logger,
logging.INFO,
msg="Attempting to add aio_pika opentelemetry autoinstrumentation...",
):
AioPikaInstrumentor().instrument()
if HAS_ASYNCPG:
with log_context(
_logger,
Expand Down Expand Up @@ -131,5 +146,70 @@ def initialize_tracing(
RequestsInstrumentor().instrument()


def _shutdown() -> None:
"""Uninstruments all opentelemetry instrumentors that were instrumented."""
FastAPIInstrumentor().uninstrument()
if HAS_AIOPG:
try:
AiopgInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument AiopgInstrumentor")
if HAS_AIOPIKA_INSTRUMENTOR:
try:
AioPikaInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument AioPikaInstrumentor")
if HAS_ASYNCPG:
try:
AsyncPGInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument AsyncPGInstrumentor")
if HAS_REDIS:
try:
RedisInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument RedisInstrumentor")
if HAS_BOTOCORE:
try:
BotocoreInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument BotocoreInstrumentor")
if HAS_REQUESTS:
try:
RequestsInstrumentor().uninstrument()
except Exception: # pylint:disable=broad-exception-caught
_logger.exception("Failed to uninstrument RequestsInstrumentor")


def setup_httpx_client_tracing(client: AsyncClient | Client):
HTTPXClientInstrumentor.instrument_client(client)


def setup_tracing(
app: FastAPI, tracing_settings: TracingSettings, service_name: str
) -> None:

_startup(tracing_settings=tracing_settings, service_name=service_name)

def _on_shutdown() -> None:
_shutdown()

app.add_event_handler("shutdown", _on_shutdown)


def get_tracing_instrumentation_lifespan(
tracing_settings: TracingSettings, service_name: str
):

_startup(tracing_settings=tracing_settings, service_name=service_name)

async def tracing_instrumentation_lifespan(
app: FastAPI,
) -> AsyncIterator[State]:
assert app # nosec

yield {}

_shutdown()

return tracing_instrumentation_lifespan
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,4 @@ async def ping(self) -> bool:
return False

@abstractmethod
async def close(self) -> None:
...
async def close(self) -> None: ...
37 changes: 23 additions & 14 deletions packages/service-library/tests/aiohttp/test_tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from aiohttp import web
from aiohttp.test_utils import TestClient
from pydantic import ValidationError
from servicelib.aiohttp.tracing import setup_tracing
from servicelib.aiohttp.tracing import get_tracing_lifespan
from settings_library.tracing import TracingSettings


Expand All @@ -24,14 +24,23 @@ def tracing_settings_in(request):
def set_and_clean_settings_env_vars(
monkeypatch: pytest.MonkeyPatch, tracing_settings_in
):
endpoint_mocked = False
if tracing_settings_in[0]:
endpoint_mocked = True
monkeypatch.setenv(
"TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT", f"{tracing_settings_in[0]}"
)
port_mocked = False
if tracing_settings_in[1]:
port_mocked = True
monkeypatch.setenv(
"TRACING_OPENTELEMETRY_COLLECTOR_PORT", f"{tracing_settings_in[1]}"
)
yield
if endpoint_mocked:
monkeypatch.delenv("TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT")
if port_mocked:
monkeypatch.delenv("TRACING_OPENTELEMETRY_COLLECTOR_PORT")


@pytest.mark.parametrize(
Expand All @@ -50,11 +59,10 @@ async def test_valid_tracing_settings(
app = web.Application()
service_name = "simcore_service_webserver"
tracing_settings = TracingSettings()
setup_tracing(
app,
service_name=service_name,
tracing_settings=tracing_settings,
)
async for _ in get_tracing_lifespan(
app, service_name=service_name, tracing_settings=tracing_settings
)(app):
pass


@pytest.mark.parametrize(
Expand Down Expand Up @@ -128,14 +136,15 @@ async def test_tracing_setup_package_detection(
app = web.Application()
service_name = "simcore_service_webserver"
tracing_settings = TracingSettings()
setup_tracing(
app,
service_name=service_name,
tracing_settings=tracing_settings,
)
# idempotency
setup_tracing(
async for _ in get_tracing_lifespan(
app,
service_name=service_name,
tracing_settings=tracing_settings,
)
)(app):
# idempotency
async for _ in get_tracing_lifespan(
app,
service_name=service_name,
tracing_settings=tracing_settings,
)(app):
pass
Loading
Loading