From df5b1455b48ae98b59b6ad1ef9da70adfb768ae7 Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Fri, 19 Jul 2024 19:30:30 -0400 Subject: [PATCH 1/4] otel: derive event name early for tracing this fixes span names like "Event None dispatched" --- fastapi_events/dispatcher.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fastapi_events/dispatcher.py b/fastapi_events/dispatcher.py index c767ace..2e2dd51 100644 --- a/fastapi_events/dispatcher.py +++ b/fastapi_events/dispatcher.py @@ -264,6 +264,8 @@ class UserCreated(pydantic.BaseModel): # Handle dispatch without event_name specified if not event_name and isinstance(event_name_or_model, (str, Enum)): event_name = event_name_or_model + elif hasattr(event_name_or_model, "__event_name__"): + event_name = event_name_or_model.__event_name__ with create_span_for_dispatch_fn(event_name=event_name): if HAS_PYDANTIC: From 0d7c2c15ef5207bc1da927d4a6ec3d25d1f19530 Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Fri, 19 Jul 2024 19:34:10 -0400 Subject: [PATCH 2/4] otel: serialize pydantic models when creating spans --- fastapi_events/otel/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fastapi_events/otel/utils.py b/fastapi_events/otel/utils.py index 75ecd59..11377ac 100644 --- a/fastapi_events/otel/utils.py +++ b/fastapi_events/otel/utils.py @@ -9,6 +9,7 @@ from fastapi_events.otel import HAS_OTEL_INSTALLED, propagate, trace from fastapi_events.otel.attributes import SpanAttributes from fastapi_events.utils import strtobool +from pydantic import BaseModel logger = logging.getLogger(__name__) @@ -39,6 +40,9 @@ def create_span_for_handle_fn( links, context = [], None + if isinstance(payload, BaseModel): + payload = payload.model_dump() + # Extract span from remote context remote_ctx = propagate.extract(payload) if use_span_linking: From a0fb7fb2e9ad4ee34127123528261a9aaeaae340 Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Fri, 19 Jul 2024 19:34:52 -0400 Subject: [PATCH 3/4] otel: serialize pydantic models when injecting trace parents --- fastapi_events/dispatcher.py | 2 +- fastapi_events/otel/utils.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fastapi_events/dispatcher.py b/fastapi_events/dispatcher.py index 2e2dd51..845178e 100644 --- a/fastapi_events/dispatcher.py +++ b/fastapi_events/dispatcher.py @@ -292,7 +292,7 @@ class UserCreated(pydantic.BaseModel): ) # OTEL - if payload and isinstance(payload, dict): + if payload: logger.debug("Injecting traceparent to event payload...") inject_traceparent(payload=payload) diff --git a/fastapi_events/otel/utils.py b/fastapi_events/otel/utils.py index 11377ac..398ccd6 100644 --- a/fastapi_events/otel/utils.py +++ b/fastapi_events/otel/utils.py @@ -89,6 +89,9 @@ def inject_traceparent(payload: Dict): logger.debug("Unable to inject traceparent. OTEL is not installed.") return + if isinstance(payload, BaseModel): + payload = payload.model_dump() + if not isinstance(payload, dict): logger.debug("Unable to inject traceparent. Payload is not a dict") return From a2f9dff0d13022d831f308a34db881b4955f2074 Mon Sep 17 00:00:00 2001 From: Eugene Brodsky Date: Mon, 29 Jul 2024 16:36:59 -0400 Subject: [PATCH 4/4] fix(otel): ensure backwards compatibility with pydantic-v1 and with no pydantic --- fastapi_events/otel/utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fastapi_events/otel/utils.py b/fastapi_events/otel/utils.py index 398ccd6..fcf88b1 100644 --- a/fastapi_events/otel/utils.py +++ b/fastapi_events/otel/utils.py @@ -9,7 +9,6 @@ from fastapi_events.otel import HAS_OTEL_INSTALLED, propagate, trace from fastapi_events.otel.attributes import SpanAttributes from fastapi_events.utils import strtobool -from pydantic import BaseModel logger = logging.getLogger(__name__) @@ -40,8 +39,10 @@ def create_span_for_handle_fn( links, context = [], None - if isinstance(payload, BaseModel): + if hasattr(payload, "model_dump"): # handle Pydantic v2 payload = payload.model_dump() + elif hasattr(payload, "dict"): # handles Pydantic v1 + payload = payload.dict() # Extract span from remote context remote_ctx = propagate.extract(payload) @@ -89,8 +90,10 @@ def inject_traceparent(payload: Dict): logger.debug("Unable to inject traceparent. OTEL is not installed.") return - if isinstance(payload, BaseModel): + if hasattr(payload, "model_dump"): # handle Pydantic v2 payload = payload.model_dump() + elif hasattr(payload, "dict"): # handles Pydantic v1 + payload = payload.dict() if not isinstance(payload, dict): logger.debug("Unable to inject traceparent. Payload is not a dict")