3636 R = TypeVar ("R" )
3737
3838 import sentry_sdk .profiler
39+ from sentry_sdk .scope import Scope
3940 from sentry_sdk ._types import (
4041 Event ,
4142 MeasurementUnit ,
@@ -1262,6 +1263,9 @@ def __init__(
12621263 active = True , # type: bool
12631264 op = None , # type: Optional[str]
12641265 description = None , # type: Optional[str]
1266+ status = None , # type: Optional[str]
1267+ scope = None , # type: Optional[Scope]
1268+ start_timestamp = None , # type: Optional[Union[datetime, float]]
12651269 origin = "manual" , # type: str
12661270 ** _ , # type: dict[str, object]
12671271 ):
@@ -1272,15 +1276,41 @@ def __init__(
12721276 listed in the signature. These additional arguments are ignored.
12731277 """
12741278 from sentry_sdk .integrations .opentelemetry .consts import SentrySpanAttribute
1279+ from sentry_sdk .integrations .opentelemetry .utils import (
1280+ convert_to_otel_timestamp ,
1281+ )
1282+
1283+ if start_timestamp is not None :
1284+ # OTel timestamps have nanosecond precision
1285+ start_timestamp = convert_to_otel_timestamp (start_timestamp )
12751286
1276- self ._otel_span = tracer .start_span (description or op or "" ) # XXX
1287+ # XXX deal with _otel_span being a NonRecordingSpan
1288+ self ._otel_span = tracer .start_span (
1289+ description or op or "" , start_time = start_timestamp
1290+ ) # XXX
12771291 self ._active = active
12781292
12791293 self ._otel_span .set_attribute (SentrySpanAttribute .ORIGIN , origin )
1280- if op is not None :
1281- self ._otel_span .set_attribute (SentrySpanAttribute .OP , op )
1282- if description is not None :
1283- self ._otel_span .set_attribute (SentrySpanAttribute .DESCRIPTION , description )
1294+ self .op = op
1295+ self .description = description
1296+ if status is not None :
1297+ self .set_status (status )
1298+
1299+ def __repr__ (self ):
1300+ # type: () -> str
1301+ return (
1302+ "<%s(op=%r, description:%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r, origin=%r)>"
1303+ % (
1304+ self .__class__ .__name__ ,
1305+ self .op ,
1306+ self .description ,
1307+ self .trace_id ,
1308+ self .span_id ,
1309+ self .parent_span_id ,
1310+ self .sampled ,
1311+ self .origin ,
1312+ )
1313+ )
12841314
12851315 def __enter__ (self ):
12861316 # type: () -> POTelSpan
@@ -1301,9 +1331,142 @@ def __exit__(self, ty, value, tb):
13011331 if self ._active :
13021332 context .detach (self ._ctx_token )
13031333
1334+ @property
1335+ def description (self ):
1336+ # type: () -> Optional[str]
1337+ from sentry_sdk .integrations .opentelemetry .consts import SentrySpanAttribute
1338+
1339+ return self ._otel_span .attributes .get (SentrySpanAttribute .DESCRIPTION )
1340+
1341+ @description .setter
1342+ def description (self , value ):
1343+ # type: (Optional[str]) -> None
1344+ from sentry_sdk .integrations .opentelemetry .consts import SentrySpanAttribute
1345+
1346+ if value is not None :
1347+ self ._otel_span .set_attribute (SentrySpanAttribute .DESCRIPTION , value )
1348+
1349+ @property
1350+ def origin (self ):
1351+ # type: () -> Optional[str]
1352+ from sentry_sdk .integrations .opentelemetry .consts import SentrySpanAttribute
1353+
1354+ return self ._otel_span .attributes .get (SentrySpanAttribute .ORIGIN )
1355+
1356+ @origin .setter
1357+ def origin (self , value ):
1358+ # type: (Optional[str]) -> None
1359+ from sentry_sdk .integrations .opentelemetry .consts import SentrySpanAttribute
1360+
1361+ if value is not None :
1362+ self ._otel_span .set_attribute (SentrySpanAttribute .ORIGIN , value )
1363+
13041364 @property
13051365 def containing_transaction (self ):
13061366 # type: () -> Optional[Transaction]
1367+ """
1368+ Get the transaction this span is a child of.
1369+
1370+ .. deprecated:: 3.0.0
1371+ This will be removed in the future. Use :func:`root_span` instead.
1372+ """
1373+ logger .warning ("Deprecated: This will be removed in the future." )
1374+ return self .root_span
1375+
1376+ @containing_transaction .setter
1377+ def containing_transaction (self , value ):
1378+ # type: (Span) -> None
1379+ """
1380+ Set this span's transaction.
1381+ .. deprecated:: 3.0.0
1382+ Use :func:`root_span` instead.
1383+ """
1384+ pass
1385+
1386+ @property
1387+ def root_span (self ):
1388+ if isinstance (self ._otel_span , otel_trace .NonRecordingSpan ):
1389+ return None
1390+
1391+ parent = None
1392+ while True :
1393+ # XXX test if this actually works
1394+ if self ._otel_span .parent :
1395+ parent = self ._otel_span .parent
1396+ else :
1397+ break
1398+
1399+ return parent
1400+
1401+ @root_span .setter
1402+ def root_span (self , value ):
1403+ pass
1404+
1405+ @property
1406+ def is_root_span (self ):
1407+ if isinstance (self ._otel_span , otel_trace .NonRecordingSpan ):
1408+ return False
1409+
1410+ return self ._otel_span .parent is None
1411+
1412+ @property
1413+ def parent_span_id (self ):
1414+ # type: () -> Optional[str]
1415+ return self ._otel_span .parent if hasattr (self ._otel_span , "parent" ) else None
1416+
1417+ @property
1418+ def trace_id (self ):
1419+ # type: () -> Optional[str]
1420+ return self ._otel_span .get_span_context ().trace_id
1421+
1422+ @property
1423+ def span_id (self ):
1424+ # type: () -> Optional[str]
1425+ return self ._otel_span .get_span_context ().span_id
1426+
1427+ @property
1428+ def sampled (self ):
1429+ # type: () -> Optional[bool]
1430+ return self ._otel_span .get_span_context ().trace_flags .sampled
1431+
1432+ @sampled .setter
1433+ def sampled (self , value ):
1434+ # type: () -> Optional[bool]
1435+ pass
1436+
1437+ @property
1438+ def op (self ):
1439+ # type: () -> Optional[str]
1440+ from sentry_sdk .integrations .opentelemetry .consts import SentrySpanAttribute
1441+
1442+ self ._otel_span .attributes .get (SentrySpanAttribute .OP )
1443+
1444+ @op .setter
1445+ def op (self , value ):
1446+ # type: (Optional[str]) -> None
1447+ from sentry_sdk .integrations .opentelemetry .consts import SentrySpanAttribute
1448+
1449+ if value is not None :
1450+ self ._otel_span .set_attribute (SentrySpanAttribute .OP , value )
1451+
1452+ @property
1453+ def name (self ):
1454+ # type: () -> str
1455+ pass
1456+
1457+ @name .setter
1458+ def name (self , value ):
1459+ # type: (str) -> None
1460+ pass
1461+
1462+ @property
1463+ def source (self ):
1464+ # type: () -> str
1465+ pass
1466+
1467+ @source .setter
1468+ def source (self , value ):
1469+ # type: (str) -> None
13071470 pass
13081471
13091472 def start_child (self , ** kwargs ):
@@ -1352,7 +1515,18 @@ def from_traceparent(
13521515
13531516 def to_traceparent (self ):
13541517 # type: () -> str
1355- pass
1518+ if self .sampled is True :
1519+ sampled = "1"
1520+ elif self .sampled is False :
1521+ sampled = "0"
1522+ else :
1523+ sampled = None
1524+
1525+ traceparent = "%s-%s" % (self .trace_id , self .span_id )
1526+ if sampled is not None :
1527+ traceparent += "-%s" % (sampled ,)
1528+
1529+ return traceparent
13561530
13571531 def to_baggage (self ):
13581532 # type: () -> Optional[Baggage]
@@ -1368,23 +1542,39 @@ def set_data(self, key, value):
13681542
13691543 def set_status (self , status ):
13701544 # type: (str) -> None
1371- pass
1545+ if status == SPANSTATUS .OK :
1546+ otel_status = StatusCode .OK
1547+ otel_description = None
1548+ else :
1549+ otel_status = StatusCode .ERROR
1550+ otel_description = status .value
1551+
1552+ self ._otel_span .set_status (otel_status , otel_description )
13721553
13731554 def set_measurement (self , name , value , unit = "" ):
13741555 # type: (str, float, MeasurementUnit) -> None
1375- pass
1556+ # XXX own namespace, e.g. sentry.measurement.xxx, so that we can group
1557+ # these back together in the processor?
1558+ # XXX otel throws a warning about value, unit being different types
1559+ self ._otel_span .set_attribute (name , (value , unit ))
13761560
13771561 def set_thread (self , thread_id , thread_name ):
13781562 # type: (Optional[int], Optional[str]) -> None
1379- pass
1563+ if thread_id is not None :
1564+ self .set_data (SPANDATA .THREAD_ID , str (thread_id ))
1565+
1566+ if thread_name is not None :
1567+ self .set_data (SPANDATA .THREAD_NAME , thread_name )
13801568
13811569 def set_profiler_id (self , profiler_id ):
13821570 # type: (Optional[str]) -> None
1383- pass
1571+ if profiler_id is not None :
1572+ self .set_data (SPANDATA .PROFILER_ID , profiler_id )
13841573
13851574 def set_http_status (self , http_status ):
13861575 # type: (int) -> None
1387- pass
1576+ self .set_data (SPANDATA .HTTP_STATUS_CODE , http_status )
1577+ self .set_status (get_span_status_from_http_code (http_status ))
13881578
13891579 def is_success (self ):
13901580 # type: () -> bool
0 commit comments