|
1 | 1 | from decimal import Decimal |
2 | | -import functools |
3 | | -import inspect |
4 | 2 | import uuid |
5 | 3 | import warnings |
6 | 4 | from datetime import datetime, timedelta, timezone |
7 | 5 | from enum import Enum |
8 | 6 |
|
9 | 7 | import sentry_sdk |
10 | | -from sentry_sdk.consts import INSTRUMENTER, OP, SPANSTATUS, SPANDATA |
| 8 | +from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS, SPANDATA |
11 | 9 | from sentry_sdk.profiler.continuous_profiler import get_profiler_id |
| 10 | +from sentry_sdk.tracing_utils import create_span_decorator |
12 | 11 | from sentry_sdk.utils import ( |
13 | 12 | get_current_thread_meta, |
14 | 13 | is_valid_sample_rate, |
15 | 14 | logger, |
16 | 15 | nanosecond_time, |
17 | 16 | should_be_treated_as_error, |
18 | | - qualname_from_function, |
19 | 17 | ) |
20 | 18 |
|
21 | 19 | from typing import TYPE_CHECKING |
@@ -281,7 +279,6 @@ class Span: |
281 | 279 | "_local_aggregator", |
282 | 280 | "scope", |
283 | 281 | "origin", |
284 | | - "name", |
285 | 282 | "_flags", |
286 | 283 | "_flags_capacity", |
287 | 284 | ) |
@@ -352,6 +349,11 @@ def __init__( |
352 | 349 | self.update_active_thread() |
353 | 350 | self.set_profiler_id(get_profiler_id()) |
354 | 351 |
|
| 352 | + @property |
| 353 | + def name(self): |
| 354 | + # type: () -> str |
| 355 | + return self.description |
| 356 | + |
355 | 357 | # TODO this should really live on the Transaction class rather than the Span |
356 | 358 | # class |
357 | 359 | def init_span_recorder(self, maxlen): |
@@ -1374,144 +1376,20 @@ async def my_async_function(): |
1374 | 1376 | return start_child_span_decorator |
1375 | 1377 |
|
1376 | 1378 |
|
1377 | | -def _set_span_attributes(span, attributes): |
1378 | | - # type: (Span, dict[str, Any]) -> None |
1379 | | - """ |
1380 | | - Set the given attributes on the given span. |
1381 | | -
|
1382 | | - :param span: The span to set attributes on. |
1383 | | - :param attributes: The attributes to set on the span. |
1384 | | - """ |
1385 | | - for key, value in attributes.items(): |
1386 | | - span.set_data(key, value) |
1387 | | - |
1388 | | - |
1389 | | -def _set_input_attributes(span, template, args, kwargs): |
1390 | | - """ |
1391 | | - Set LLM input attributes based on given information to the given span. |
1392 | | -
|
1393 | | - :param span: The span to set attributes on. |
1394 | | - :param template: The template to use to set attributes on the span. |
1395 | | - :param args: The arguments to the LLM call. |
1396 | | - :param kwargs: The keyword arguments to the LLM call. |
1397 | | - """ |
1398 | | - pass |
1399 | | - |
1400 | | - |
1401 | | -def _set_output_attributes(span, template, result): |
1402 | | - """ |
1403 | | - Set LLM output attributes based on given information to the given span. |
1404 | | -
|
1405 | | - :param span: The span to set attributes on. |
1406 | | - :param template: The template to use to set attributes on the span. |
1407 | | - :param result: The result of the LLM call. |
1408 | | - """ |
1409 | | - pass |
1410 | | - |
1411 | | - |
1412 | | -def new_trace(func=None, *, as_type="span", name=None): |
| 1379 | +def new_trace(func=None, *, as_type="span", name=None, attributes=None): |
1413 | 1380 | """ |
1414 | 1381 | Decorator to start a child span. |
1415 | 1382 |
|
1416 | 1383 | :param func: The function to trace. |
1417 | 1384 | :param as_type: The type of span to create. |
1418 | | - :param name: The name of the span. |
| 1385 | + :param name: The name of the span. (defaults to the function name) |
| 1386 | + :param attributes: Additional attributes to set on the span. |
1419 | 1387 | """ |
1420 | | - |
1421 | | - def span_decorator(f, *a, **kw): |
1422 | | - @functools.wraps(f) |
1423 | | - async def async_wrapper(*args, **kwargs): |
1424 | | - # type: (*Any, **Any) -> Any |
1425 | | - op = kw.get("op", OP.FUNCTION) |
1426 | | - span_name = kw.get("name", qualname_from_function(f)) |
1427 | | - attributes = kw.get("attributes", {}) |
1428 | | - |
1429 | | - with sentry_sdk.start_span( |
1430 | | - op=op, |
1431 | | - name=span_name, |
1432 | | - ) as span: |
1433 | | - _set_span_attributes(span, attributes) |
1434 | | - _set_input_attributes(span, as_type, args, kwargs) |
1435 | | - result = await f(*args, **kwargs) |
1436 | | - _set_output_attributes(span, as_type, result) |
1437 | | - |
1438 | | - return result |
1439 | | - |
1440 | | - @functools.wraps(f) |
1441 | | - def sync_wrapper(*args, **kwargs): |
1442 | | - # type: (*Any, **Any) -> Any |
1443 | | - op = kw.get("op", OP.FUNCTION) |
1444 | | - span_name = kw.get("name", qualname_from_function(f)) |
1445 | | - attributes = kw.get("attributes", {}) |
1446 | | - |
1447 | | - with sentry_sdk.start_span( |
1448 | | - op=op, |
1449 | | - name=span_name, |
1450 | | - ) as span: |
1451 | | - _set_span_attributes(span, attributes) |
1452 | | - _set_input_attributes(span, as_type, args, kwargs) |
1453 | | - result = f(*args, **kwargs) |
1454 | | - _set_output_attributes(span, as_type, result) |
1455 | | - |
1456 | | - return result |
1457 | | - |
1458 | | - if inspect.iscoroutinefunction(f): |
1459 | | - wrapper = async_wrapper |
1460 | | - else: |
1461 | | - wrapper = sync_wrapper |
1462 | | - |
1463 | | - return wrapper |
1464 | | - |
1465 | | - def ai_chat_decorator(f): |
1466 | | - # type: (Optional[Callable[P, R]]) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]] |
1467 | | - attributes = { |
1468 | | - "gen_ai.operation.name": "chat", |
1469 | | - } |
1470 | | - |
1471 | | - return span_decorator( |
1472 | | - f, |
1473 | | - op=OP.GEN_AI_CHAT, |
1474 | | - name="chat [MODEL]", |
1475 | | - attributes=attributes, |
1476 | | - ) |
1477 | | - |
1478 | | - def ai_agent_decorator(f): |
1479 | | - # type: (Optional[Callable[P, R]]) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]] |
1480 | | - agent_name = name or qualname_from_function(f) |
1481 | | - attributes = { |
1482 | | - "gen_ai.operation.name": "invoke_agent", |
1483 | | - "gen_ai.agent.name": agent_name, |
1484 | | - } |
1485 | | - |
1486 | | - return span_decorator( |
1487 | | - f, |
1488 | | - op=OP.GEN_AI_INVOKE_AGENT, |
1489 | | - name=f"invoke_agent {agent_name}", |
1490 | | - attributes=attributes, |
1491 | | - ) |
1492 | | - |
1493 | | - def ai_tool_decorator(f): |
1494 | | - # type: (Optional[Callable[P, R]]) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]] |
1495 | | - tool_name = name or qualname_from_function(f) |
1496 | | - attributes = { |
1497 | | - "gen_ai.operation.name": "execute_tool", |
1498 | | - "gen_ai.tool.name": tool_name, |
1499 | | - } |
1500 | | - |
1501 | | - return span_decorator( |
1502 | | - f, |
1503 | | - op=OP.GEN_AI_EXECUTE_TOOL, |
1504 | | - name=f"execute_tool {tool_name}", |
1505 | | - attributes=attributes, |
1506 | | - ) |
1507 | | - |
1508 | | - # Select a type based decorator (with default fallback to span_decorator) |
1509 | | - decorator_for_type = { |
1510 | | - "ai_chat": ai_chat_decorator, |
1511 | | - "ai_agent": ai_agent_decorator, |
1512 | | - "ai_tool": ai_tool_decorator, |
1513 | | - } |
1514 | | - decorator = decorator_for_type.get(as_type, span_decorator) |
| 1388 | + decorator = create_span_decorator( |
| 1389 | + template=as_type, |
| 1390 | + name=name, |
| 1391 | + attributes=attributes, |
| 1392 | + ) |
1515 | 1393 |
|
1516 | 1394 | if func: |
1517 | 1395 | return decorator(func) |
|
0 commit comments