Skip to content

Commit 726b769

Browse files
committed
POC OtlpIntegration
1 parent 8965821 commit 726b769

File tree

5 files changed

+93
-2
lines changed

5 files changed

+93
-2
lines changed

requirements-linting.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ types-greenlet
77
types-redis
88
types-setuptools
99
types-webob
10-
opentelemetry-distro
10+
opentelemetry-distro[otlp]
1111
pymongo # There is no separate types module.
1212
loguru # There is no separate types module.
1313
pre-commit # local linting

sentry_sdk/consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class EndpointType(Enum):
2121
"""
2222

2323
ENVELOPE = "envelope"
24+
OTLP_TRACES = "integration/otlp/v1/traces"
2425

2526

2627
class CompressionAlgo(Enum):

sentry_sdk/integrations/otlp.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from sentry_sdk.integrations import Integration, DidNotEnable
2+
from sentry_sdk.scope import register_external_propagation_context
3+
from sentry_sdk.utils import logger, Dsn
4+
from sentry_sdk.consts import VERSION, EndpointType
5+
6+
try:
7+
from opentelemetry import trace
8+
from opentelemetry.propagate import set_global_textmap
9+
from opentelemetry.sdk.trace import TracerProvider
10+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
11+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
12+
13+
from sentry_sdk.integrations.opentelemetry.propagator import SentryPropagator
14+
except ImportError:
15+
raise DidNotEnable("opentelemetry-distro[otlp] is not installed")
16+
17+
from typing import TYPE_CHECKING
18+
19+
if TYPE_CHECKING:
20+
from typing import Optional, Dict, Any, Tuple
21+
from sentry_sdk._types import Event, Hint
22+
23+
24+
def otel_propagation_context():
25+
# type: () -> Optional[Tuple[str, str]]
26+
"""
27+
Get the (trace_id, span_id) from opentelemetry if exists.
28+
"""
29+
ctx = trace.get_current_span().get_span_context()
30+
31+
if ctx.trace_id == trace.INVALID_TRACE_ID or ctx.span_id == trace.INVALID_SPAN_ID:
32+
return None
33+
34+
return (trace.format_trace_id(ctx.trace_id), trace.format_span_id(ctx.span_id))
35+
36+
37+
def setup_otlp_exporter(dsn=None):
38+
# type: (Optional[str]) -> None
39+
tracer_provider = trace.get_tracer_provider()
40+
41+
if not isinstance(tracer_provider, TracerProvider):
42+
logger.debug("[OTLP] No TracerProvider configured by user, creating a new one")
43+
tracer_provider = TracerProvider()
44+
trace.set_tracer_provider(tracer_provider)
45+
46+
endpoint = None
47+
headers = None
48+
if dsn:
49+
auth = Dsn(dsn).to_auth(f"sentry.python/{VERSION}")
50+
endpoint = auth.get_api_url(EndpointType.OTLP_TRACES)
51+
headers = {"X-Sentry-Auth": auth.to_header()}
52+
logger.debug(f"[OTLP] Sending traces to {endpoint}")
53+
54+
otlp_exporter = OTLPSpanExporter(endpoint=endpoint, headers=headers)
55+
span_processor = BatchSpanProcessor(otlp_exporter)
56+
tracer_provider.add_span_processor(span_processor)
57+
58+
59+
class OTLPIntegration(Integration):
60+
identifier = "otlp"
61+
62+
def __init__(self, setup_otlp_exporter=True, setup_propagator=True):
63+
# type: (bool, bool) -> None
64+
self.setup_otlp_exporter = setup_otlp_exporter
65+
self.setup_propagator = setup_propagator
66+
67+
@staticmethod
68+
def setup_once():
69+
# type: () -> None
70+
logger.debug("[OTLP] Setting up trace linking for all events")
71+
register_external_propagation_context(otel_propagation_context)
72+
73+
def setup_once_with_options(self, options=None):
74+
# type: (Optional[Dict[str, Any]]) -> None
75+
if self.setup_otlp_exporter:
76+
logger.debug("[OTLP] Setting up OTLP exporter")
77+
dsn = options.get("dsn") if options else None # type: Optional[str]
78+
setup_otlp_exporter(dsn)
79+
80+
if self.setup_propagator:
81+
logger.debug("[OTLP] Setting up propagator for distributed tracing")
82+
# TODO-neel better propagator support, chain with existing ones if possible instead of replacing
83+
set_global_textmap(SentryPropagator())

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def get_file_text(file_name):
7272
"openai": ["openai>=1.0.0", "tiktoken>=0.3.0"],
7373
"openfeature": ["openfeature-sdk>=0.7.1"],
7474
"opentelemetry": ["opentelemetry-distro>=0.35b0"],
75-
"opentelemetry-experimental": ["opentelemetry-distro"],
75+
"opentelemetry-otlp": ["opentelemetry-distro[otlp]>=0.35b0"],
7676
"pure-eval": ["pure_eval", "executing", "asttokens"],
7777
"pydantic_ai": ["pydantic-ai>=1.0.0"],
7878
"pymongo": ["pymongo>=3.1"],

tox.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ envlist =
4040
# OpenTelemetry (OTel)
4141
{py3.7,py3.9,py3.12,py3.13,py3.14,py3.14t}-opentelemetry
4242

43+
# OpenTelemetry with OTLP
44+
{py3.7,py3.9,py3.12,py3.13,py3.14,py3.14t}-otlp
45+
4346
# OpenTelemetry Experimental (POTel)
4447
{py3.8,py3.9,py3.10,py3.11,py3.12,py3.13}-potel
4548

@@ -342,6 +345,9 @@ deps =
342345
# OpenTelemetry (OTel)
343346
opentelemetry: opentelemetry-distro
344347

348+
# OpenTelemetry with OTLP
349+
otlp: opentelemetry-distro[otlp]
350+
345351
# OpenTelemetry Experimental (POTel)
346352
potel: -e .[opentelemetry-experimental]
347353

@@ -765,6 +771,7 @@ setenv =
765771
cloud_resource_context: TESTPATH=tests/integrations/cloud_resource_context
766772
gcp: TESTPATH=tests/integrations/gcp
767773
opentelemetry: TESTPATH=tests/integrations/opentelemetry
774+
otlp: TESTPATH=tests/integrations/otlp
768775
potel: TESTPATH=tests/integrations/opentelemetry
769776
socket: TESTPATH=tests/integrations/socket
770777

0 commit comments

Comments
 (0)