36
36
R = TypeVar ("R" )
37
37
38
38
import sentry_sdk .profiler
39
+ from sentry_sdk .scope import Scope
39
40
from sentry_sdk ._types import (
40
41
Event ,
41
42
MeasurementUnit ,
@@ -1262,6 +1263,9 @@ def __init__(
1262
1263
active = True , # type: bool
1263
1264
op = None , # type: Optional[str]
1264
1265
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]]
1265
1269
origin = "manual" , # type: str
1266
1270
** _ , # type: dict[str, object]
1267
1271
):
@@ -1272,15 +1276,41 @@ def __init__(
1272
1276
listed in the signature. These additional arguments are ignored.
1273
1277
"""
1274
1278
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 )
1275
1286
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
1277
1291
self ._active = active
1278
1292
1279
1293
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
+ )
1284
1314
1285
1315
def __enter__ (self ):
1286
1316
# type: () -> POTelSpan
@@ -1301,9 +1331,142 @@ def __exit__(self, ty, value, tb):
1301
1331
if self ._active :
1302
1332
context .detach (self ._ctx_token )
1303
1333
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: (Op
A3E2
tional[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
+
1304
1364
@property
1305
1365
def containing_transaction (self ):
1306
1366
# 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
1307
1470
pass
1308
1471
1309
1472
def start_child (self , ** kwargs ):
@@ -1352,7 +1515,18 @@ def from_traceparent(
1352
1515
1353
1516
def to_traceparent (self ):
1354
1517
# 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
1356
1530
1357
1531
def to_baggage (self ):
1358
1532
# type: () -> Optional[Baggage]
@@ -1368,23 +1542,39 @@ def set_data(self, key, value):
1368
1542
1369
1543
def set_status (self , status ):
1370
1544
# 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 )
1372
1553
1373
1554
def set_measurement (self , name , value , unit = "" ):
1374
1555
# 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 ))
1376
1560
1377
1561
def set_thread (self , thread_id , thread_name ):
1378
1562
# 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 )
1380
1568
1381
1569
def set_profiler_id (self , profiler_id ):
1382
1570
# type: (Optional[str]) -> None
1383
- pass
1571
+ if profiler_id is not None :
1572
+ self .set_data (SPANDATA .PROFILER_ID , profiler_id )
1384
1573
1385
1574
def set_http_status (self , http_status ):
1386
1575
# 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 ))
1388
1578
1389
1579
def is_success (self ):
1390
1580
# type: () -> bool
0 commit comments