Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 40 additions & 3 deletions sentry_sdk/integrations/opentelemetry/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
are experimental and not suitable for production use. They may be changed or
removed at any time without prior notice.
"""

import sentry_sdk
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations.opentelemetry.propagator import SentryPropagator
from sentry_sdk.integrations.opentelemetry.span_processor import SentrySpanProcessor
Expand All @@ -13,9 +13,16 @@
from opentelemetry import trace
from opentelemetry.propagate import set_global_textmap
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
except ImportError:
raise DidNotEnable("opentelemetry not installed")

try:
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
except ImportError:
OTLPSpanExporter = None


try:
from opentelemetry.instrumentation.django import DjangoInstrumentor # type: ignore[import-not-found]
except ImportError:
Expand All @@ -30,6 +37,20 @@
class OpenTelemetryIntegration(Integration):
identifier = "opentelemetry"

def __init__(self, enable_span_processor=True, enable_otlp_exporter=False, enable_propagator=True):
# type: (bool, bool, bool) -> None
self.enable_span_processor = enable_span_processor
self.enable_otlp_exporter = enable_otlp_exporter
self.enable_propagator = enable_propagator

if self.enable_otlp_exporter and OTLPSpanExporter is None:
logger.warning("[Otel] OTLPSpanExporter not installed.")
self.enable_otlp_exporter = False

if self.enable_span_processor and self.enable_otlp_exporter:
logger.warning("[Otel] Disabling span processor because otlp exporter is set.")
self.enable_span_processor = False

@staticmethod
def setup_once():
# type: () -> None
Expand All @@ -46,10 +67,26 @@ def setup_once():

def _setup_sentry_tracing():
# type: () -> None
integration = sentry_sdk.get_client().get_integration(OpenTelemetryIntegration)
if integration is None:
return

# TODO provider oblivious
provider = TracerProvider()
provider.add_span_processor(SentrySpanProcessor())

if integration.enable_otlp_exporter:
otlp_exporter = OTLPSpanExporter(
# endpoint="___OTLP_TRACES_URL___",
# headers={"x-sentry-auth": "sentry sentry_key=___PUBLIC_KEY___"},
)
otlp_span_processor = BatchSpanProcessor(otlp_exporter)
provider.add_span_processor(otlp_span_processor)
elif integration.enable_span_processor:
provider.add_span_processor(SentrySpanProcessor())
trace.set_tracer_provider(provider)
set_global_textmap(SentryPropagator())

if integration.enable_propagator:
set_global_textmap(SentryPropagator())


def _setup_instrumentors():
Expand Down
55 changes: 55 additions & 0 deletions sentry_sdk/integrations/otlp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import sentry_sdk
from sentry_sdk.integrations import Integration, DidNotEnable
from sentry_sdk.scope import add_global_event_processor

try:
from opentelemetry import trace
except ImportError:
raise DidNotEnable("opentelemetry is not installed")

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Optional

from sentry_sdk._types import Event, Hint


class OtlpIntegration(Integration):
identifier = "otlp"

@staticmethod
def setup_once():
# type: () -> None
@add_global_event_processor
def link_trace_context_to_error_event(event, hint):
# type: (Event, Optional[Hint]) -> Optional[Event]
integration = sentry_sdk.get_client().get_integration(OtlpIntegration)
if integration is None:
return event

if hasattr(event, "type") and event["type"] == "transaction":
return event

otel_span = trace.get_current_span()
if not otel_span:
return event

ctx = otel_span.get_span_context()

if (
ctx.trace_id == trace.INVALID_TRACE_ID
or ctx.span_id == trace.INVALID_SPAN_ID
):
return event

contexts = event.setdefault("contexts", {})
contexts.setdefault("trace", {}).update(
{
"trace_id": trace.format_trace_id(ctx.trace_id),
"span_id": trace.format_span_id(ctx.span_id),
"status": "ok", # TODO
}
)

return event
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def get_file_text(file_name):
"loguru": ["loguru>=0.5"],
"openai": ["openai>=1.0.0", "tiktoken>=0.3.0"],
"openfeature": ["openfeature-sdk>=0.7.1"],
"opentelemetry": ["opentelemetry-distro>=0.35b0"],
"opentelemetry": ["opentelemetry-distro>=0.35b0", "opentelemetry-exporter-otlp-proto-http>=1.0.0"],
"opentelemetry-experimental": ["opentelemetry-distro"],
"pure-eval": ["pure_eval", "executing", "asttokens"],
"pydantic_ai": ["pydantic-ai>=1.0.0"],
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ deps =

# OpenTelemetry (OTel)
opentelemetry: opentelemetry-distro
opentelemetry: opentelemetry-exporter-otlp-proto-http

# OpenTelemetry Experimental (POTel)
potel: -e .[opentelemetry-experimental]
Expand Down
Loading