sentry-sdk 3.0.0a5__py2.py3-none-any.whl → 3.0.0a6__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sentry-sdk might be problematic. Click here for more details.
- sentry_sdk/ai/utils.py +7 -8
- sentry_sdk/api.py +13 -2
- sentry_sdk/client.py +93 -17
- sentry_sdk/consts.py +14 -6
- sentry_sdk/crons/api.py +5 -0
- sentry_sdk/integrations/anthropic.py +133 -73
- sentry_sdk/integrations/asgi.py +10 -9
- sentry_sdk/integrations/asyncio.py +85 -20
- sentry_sdk/integrations/clickhouse_driver.py +55 -28
- sentry_sdk/integrations/fastapi.py +1 -7
- sentry_sdk/integrations/gnu_backtrace.py +6 -3
- sentry_sdk/integrations/langchain.py +462 -218
- sentry_sdk/integrations/litestar.py +1 -1
- sentry_sdk/integrations/openai_agents/patches/agent_run.py +0 -2
- sentry_sdk/integrations/openai_agents/patches/runner.py +18 -15
- sentry_sdk/integrations/quart.py +1 -1
- sentry_sdk/integrations/starlette.py +1 -5
- sentry_sdk/integrations/starlite.py +2 -2
- sentry_sdk/scope.py +11 -11
- sentry_sdk/tracing.py +94 -17
- sentry_sdk/tracing_utils.py +330 -33
- sentry_sdk/transport.py +357 -62
- sentry_sdk/utils.py +23 -5
- sentry_sdk/worker.py +197 -3
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a6.dist-info}/METADATA +3 -1
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a6.dist-info}/RECORD +30 -30
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a6.dist-info}/WHEEL +0 -0
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a6.dist-info}/entry_points.txt +0 -0
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a6.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-3.0.0a5.dist-info → sentry_sdk-3.0.0a6.dist-info}/top_level.txt +0 -0
sentry_sdk/tracing_utils.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import contextlib
|
|
3
|
+
import functools
|
|
3
4
|
import inspect
|
|
4
5
|
import os
|
|
5
6
|
import re
|
|
@@ -8,7 +9,6 @@ import uuid
|
|
|
8
9
|
from collections.abc import Mapping
|
|
9
10
|
from datetime import datetime, timedelta, timezone
|
|
10
11
|
from decimal import ROUND_DOWN, Decimal, DefaultContext, localcontext
|
|
11
|
-
from functools import wraps
|
|
12
12
|
from random import Random
|
|
13
13
|
from urllib.parse import quote, unquote
|
|
14
14
|
|
|
@@ -17,6 +17,7 @@ from sentry_sdk.consts import (
|
|
|
17
17
|
OP,
|
|
18
18
|
SPANDATA,
|
|
19
19
|
SPANSTATUS,
|
|
20
|
+
SPANTEMPLATE,
|
|
20
21
|
BAGGAGE_HEADER_NAME,
|
|
21
22
|
SENTRY_TRACE_HEADER_NAME,
|
|
22
23
|
)
|
|
@@ -27,6 +28,7 @@ from sentry_sdk.utils import (
|
|
|
27
28
|
logger,
|
|
28
29
|
match_regex_list,
|
|
29
30
|
qualname_from_function,
|
|
31
|
+
safe_repr,
|
|
30
32
|
to_string,
|
|
31
33
|
is_sentry_url,
|
|
32
34
|
_is_external_source,
|
|
@@ -686,71 +688,117 @@ def normalize_incoming_data(incoming_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
686
688
|
return data
|
|
687
689
|
|
|
688
690
|
|
|
689
|
-
def
|
|
691
|
+
def create_span_decorator(
|
|
692
|
+
op: Optional[Union[str, OP]] = None,
|
|
693
|
+
name: Optional[str] = None,
|
|
694
|
+
attributes: Optional[dict[str, Any]] = None,
|
|
695
|
+
template: SPANTEMPLATE = SPANTEMPLATE.DEFAULT,
|
|
696
|
+
) -> Any:
|
|
690
697
|
"""
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
698
|
+
Create a span decorator that can wrap both sync and async functions.
|
|
699
|
+
|
|
700
|
+
:param op: The operation type for the span.
|
|
701
|
+
:type op: str or :py:class:`sentry_sdk.consts.OP` or None
|
|
702
|
+
:param name: The name of the span.
|
|
703
|
+
:type name: str or None
|
|
704
|
+
:param attributes: Additional attributes to set on the span.
|
|
705
|
+
:type attributes: dict or None
|
|
706
|
+
:param template: The type of span to create. This determines what kind of
|
|
707
|
+
span instrumentation and data collection will be applied. Use predefined
|
|
708
|
+
constants from :py:class:`sentry_sdk.consts.SPANTEMPLATE`.
|
|
709
|
+
The default is `SPANTEMPLATE.DEFAULT` which is the right choice for most
|
|
710
|
+
use cases.
|
|
711
|
+
:type template: :py:class:`sentry_sdk.consts.SPANTEMPLATE`
|
|
694
712
|
"""
|
|
695
|
-
|
|
696
|
-
|
|
713
|
+
from sentry_sdk.scope import should_send_default_pii
|
|
714
|
+
|
|
715
|
+
def span_decorator(f: Any) -> Any:
|
|
716
|
+
"""
|
|
717
|
+
Decorator to create a span for the given function.
|
|
718
|
+
"""
|
|
697
719
|
|
|
698
|
-
@wraps(
|
|
699
|
-
async def
|
|
720
|
+
@functools.wraps(f)
|
|
721
|
+
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
700
722
|
span = get_current_span()
|
|
701
723
|
|
|
702
724
|
if span is None:
|
|
703
725
|
logger.debug(
|
|
704
726
|
"Cannot create a child span for %s. "
|
|
705
727
|
"Please start a Sentry transaction before calling this function.",
|
|
706
|
-
qualname_from_function(
|
|
728
|
+
qualname_from_function(f),
|
|
707
729
|
)
|
|
708
|
-
return await
|
|
730
|
+
return await f(*args, **kwargs)
|
|
731
|
+
|
|
732
|
+
span_op = op or _get_span_op(template)
|
|
733
|
+
function_name = name or qualname_from_function(f) or ""
|
|
734
|
+
span_name = _get_span_name(template, function_name, kwargs)
|
|
735
|
+
send_pii = should_send_default_pii()
|
|
709
736
|
|
|
710
737
|
with sentry_sdk.start_span(
|
|
711
|
-
op=
|
|
712
|
-
name=
|
|
738
|
+
op=span_op,
|
|
739
|
+
name=span_name,
|
|
713
740
|
only_as_child_span=True,
|
|
714
|
-
):
|
|
715
|
-
|
|
741
|
+
) as span:
|
|
742
|
+
span.set_attributes(attributes or {})
|
|
743
|
+
_set_input_attributes(
|
|
744
|
+
span, template, send_pii, function_name, f, args, kwargs
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
result = await f(*args, **kwargs)
|
|
748
|
+
|
|
749
|
+
_set_output_attributes(span, template, send_pii, result)
|
|
750
|
+
|
|
751
|
+
return result
|
|
716
752
|
|
|
717
753
|
try:
|
|
718
|
-
|
|
719
|
-
func
|
|
720
|
-
)
|
|
754
|
+
async_wrapper.__signature__ = inspect.signature(f) # type: ignore[attr-defined]
|
|
721
755
|
except Exception:
|
|
722
756
|
pass
|
|
723
757
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
@wraps(func)
|
|
728
|
-
def func_with_tracing(*args: Any, **kwargs: Any) -> Any:
|
|
758
|
+
@functools.wraps(f)
|
|
759
|
+
def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
729
760
|
span = get_current_span()
|
|
730
761
|
|
|
731
762
|
if span is None:
|
|
732
763
|
logger.debug(
|
|
733
764
|
"Cannot create a child span for %s. "
|
|
734
765
|
"Please start a Sentry transaction before calling this function.",
|
|
735
|
-
qualname_from_function(
|
|
766
|
+
qualname_from_function(f),
|
|
736
767
|
)
|
|
737
|
-
return
|
|
768
|
+
return f(*args, **kwargs)
|
|
769
|
+
|
|
770
|
+
span_op = op or _get_span_op(template)
|
|
771
|
+
function_name = name or qualname_from_function(f) or ""
|
|
772
|
+
span_name = _get_span_name(template, function_name, kwargs)
|
|
773
|
+
send_pii = should_send_default_pii()
|
|
738
774
|
|
|
739
775
|
with sentry_sdk.start_span(
|
|
740
|
-
op=
|
|
741
|
-
name=
|
|
776
|
+
op=span_op,
|
|
777
|
+
name=span_name,
|
|
742
778
|
only_as_child_span=True,
|
|
743
|
-
):
|
|
744
|
-
|
|
779
|
+
) as span:
|
|
780
|
+
span.set_attributes(attributes or {})
|
|
781
|
+
_set_input_attributes(
|
|
782
|
+
span, template, send_pii, function_name, f, args, kwargs
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
result = f(*args, **kwargs)
|
|
786
|
+
|
|
787
|
+
_set_output_attributes(span, template, send_pii, result)
|
|
788
|
+
|
|
789
|
+
return result
|
|
745
790
|
|
|
746
791
|
try:
|
|
747
|
-
|
|
748
|
-
func
|
|
749
|
-
)
|
|
792
|
+
sync_wrapper.__signature__ = inspect.signature(f) # type: ignore[attr-defined]
|
|
750
793
|
except Exception:
|
|
751
794
|
pass
|
|
752
795
|
|
|
753
|
-
|
|
796
|
+
if inspect.iscoroutinefunction(f):
|
|
797
|
+
return async_wrapper
|
|
798
|
+
else:
|
|
799
|
+
return sync_wrapper
|
|
800
|
+
|
|
801
|
+
return span_decorator
|
|
754
802
|
|
|
755
803
|
|
|
756
804
|
def get_current_span(
|
|
@@ -813,6 +861,255 @@ def _sample_rand_range(
|
|
|
813
861
|
return sample_rate, 1.0
|
|
814
862
|
|
|
815
863
|
|
|
864
|
+
def _get_value(source: Any, key: str) -> Optional[Any]:
|
|
865
|
+
"""
|
|
866
|
+
Gets a value from a source object. The source can be a dict or an object.
|
|
867
|
+
It is checked for dictionary keys and object attributes.
|
|
868
|
+
"""
|
|
869
|
+
value = None
|
|
870
|
+
if isinstance(source, dict):
|
|
871
|
+
value = source.get(key)
|
|
872
|
+
else:
|
|
873
|
+
if hasattr(source, key):
|
|
874
|
+
try:
|
|
875
|
+
value = getattr(source, key)
|
|
876
|
+
except Exception:
|
|
877
|
+
value = None
|
|
878
|
+
return value
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
def _get_span_name(
|
|
882
|
+
template: Union[str, SPANTEMPLATE],
|
|
883
|
+
name: str,
|
|
884
|
+
kwargs: Optional[dict[str, Any]] = None,
|
|
885
|
+
) -> str:
|
|
886
|
+
"""
|
|
887
|
+
Get the name of the span based on the template and the name.
|
|
888
|
+
"""
|
|
889
|
+
span_name = name
|
|
890
|
+
|
|
891
|
+
if template == SPANTEMPLATE.AI_CHAT:
|
|
892
|
+
model = None
|
|
893
|
+
if kwargs:
|
|
894
|
+
for key in ("model", "model_name"):
|
|
895
|
+
if kwargs.get(key) and isinstance(kwargs[key], str):
|
|
896
|
+
model = kwargs[key]
|
|
897
|
+
break
|
|
898
|
+
|
|
899
|
+
span_name = f"chat {model}" if model else "chat"
|
|
900
|
+
|
|
901
|
+
elif template == SPANTEMPLATE.AI_AGENT:
|
|
902
|
+
span_name = f"invoke_agent {name}"
|
|
903
|
+
|
|
904
|
+
elif template == SPANTEMPLATE.AI_TOOL:
|
|
905
|
+
span_name = f"execute_tool {name}"
|
|
906
|
+
|
|
907
|
+
return span_name
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
def _get_span_op(template: Union[str, SPANTEMPLATE]) -> str:
|
|
911
|
+
"""
|
|
912
|
+
Get the operation of the span based on the template.
|
|
913
|
+
"""
|
|
914
|
+
mapping = {
|
|
915
|
+
SPANTEMPLATE.AI_CHAT: OP.GEN_AI_CHAT,
|
|
916
|
+
SPANTEMPLATE.AI_AGENT: OP.GEN_AI_INVOKE_AGENT,
|
|
917
|
+
SPANTEMPLATE.AI_TOOL: OP.GEN_AI_EXECUTE_TOOL,
|
|
918
|
+
} # type: dict[Union[str, SPANTEMPLATE], Union[str, OP]]
|
|
919
|
+
op = mapping.get(template, OP.FUNCTION)
|
|
920
|
+
|
|
921
|
+
return str(op)
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def _get_input_attributes(
|
|
925
|
+
template: Union[str, SPANTEMPLATE],
|
|
926
|
+
send_pii: bool,
|
|
927
|
+
args: tuple[Any, ...],
|
|
928
|
+
kwargs: dict[str, Any],
|
|
929
|
+
) -> dict[str, Any]:
|
|
930
|
+
"""
|
|
931
|
+
Get input attributes for the given span template.
|
|
932
|
+
"""
|
|
933
|
+
attributes: dict[str, Any] = {}
|
|
934
|
+
|
|
935
|
+
if template in [SPANTEMPLATE.AI_AGENT, SPANTEMPLATE.AI_TOOL, SPANTEMPLATE.AI_CHAT]:
|
|
936
|
+
mapping: dict[str, tuple[str, type]] = {
|
|
937
|
+
"model": (SPANDATA.GEN_AI_REQUEST_MODEL, str),
|
|
938
|
+
"model_name": (SPANDATA.GEN_AI_REQUEST_MODEL, str),
|
|
939
|
+
"agent": (SPANDATA.GEN_AI_AGENT_NAME, str),
|
|
940
|
+
"agent_name": (SPANDATA.GEN_AI_AGENT_NAME, str),
|
|
941
|
+
"max_tokens": (SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, int),
|
|
942
|
+
"frequency_penalty": (SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY, float),
|
|
943
|
+
"presence_penalty": (SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY, float),
|
|
944
|
+
"temperature": (SPANDATA.GEN_AI_REQUEST_TEMPERATURE, float),
|
|
945
|
+
"top_p": (SPANDATA.GEN_AI_REQUEST_TOP_P, float),
|
|
946
|
+
"top_k": (SPANDATA.GEN_AI_REQUEST_TOP_K, int),
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
def _set_from_key(key: str, value: Any) -> None:
|
|
950
|
+
if key in mapping:
|
|
951
|
+
(attribute, data_type) = mapping[key]
|
|
952
|
+
if value is not None and isinstance(value, data_type):
|
|
953
|
+
attributes[attribute] = value
|
|
954
|
+
|
|
955
|
+
for key, value in list(kwargs.items()):
|
|
956
|
+
if key == "prompt" and isinstance(value, str):
|
|
957
|
+
attributes.setdefault(SPANDATA.GEN_AI_REQUEST_MESSAGES, []).append(
|
|
958
|
+
{"role": "user", "content": value}
|
|
959
|
+
)
|
|
960
|
+
continue
|
|
961
|
+
|
|
962
|
+
if key == "system_prompt" and isinstance(value, str):
|
|
963
|
+
attributes.setdefault(SPANDATA.GEN_AI_REQUEST_MESSAGES, []).append(
|
|
964
|
+
{"role": "system", "content": value}
|
|
965
|
+
)
|
|
966
|
+
continue
|
|
967
|
+
|
|
968
|
+
_set_from_key(key, value)
|
|
969
|
+
|
|
970
|
+
if template == SPANTEMPLATE.AI_TOOL and send_pii:
|
|
971
|
+
attributes[SPANDATA.GEN_AI_TOOL_INPUT] = safe_repr(
|
|
972
|
+
{"args": args, "kwargs": kwargs}
|
|
973
|
+
)
|
|
974
|
+
|
|
975
|
+
# Coerce to string
|
|
976
|
+
if SPANDATA.GEN_AI_REQUEST_MESSAGES in attributes:
|
|
977
|
+
attributes[SPANDATA.GEN_AI_REQUEST_MESSAGES] = safe_repr(
|
|
978
|
+
attributes[SPANDATA.GEN_AI_REQUEST_MESSAGES]
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
return attributes
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
def _get_usage_attributes(usage: Any) -> dict[str, Any]:
|
|
985
|
+
"""
|
|
986
|
+
Get usage attributes.
|
|
987
|
+
"""
|
|
988
|
+
attributes = {}
|
|
989
|
+
|
|
990
|
+
def _set_from_keys(attribute: str, keys: tuple[str, ...]) -> None:
|
|
991
|
+
for key in keys:
|
|
992
|
+
value = _get_value(usage, key)
|
|
993
|
+
if value is not None and isinstance(value, int):
|
|
994
|
+
attributes[attribute] = value
|
|
995
|
+
|
|
996
|
+
_set_from_keys(
|
|
997
|
+
SPANDATA.GEN_AI_USAGE_INPUT_TOKENS,
|
|
998
|
+
("prompt_tokens", "input_tokens"),
|
|
999
|
+
)
|
|
1000
|
+
_set_from_keys(
|
|
1001
|
+
SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS,
|
|
1002
|
+
("completion_tokens", "output_tokens"),
|
|
1003
|
+
)
|
|
1004
|
+
_set_from_keys(
|
|
1005
|
+
SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS,
|
|
1006
|
+
("total_tokens",),
|
|
1007
|
+
)
|
|
1008
|
+
|
|
1009
|
+
return attributes
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
def _get_output_attributes(
|
|
1013
|
+
template: Union[str, SPANTEMPLATE], send_pii: bool, result: Any
|
|
1014
|
+
) -> dict[str, Any]:
|
|
1015
|
+
"""
|
|
1016
|
+
Get output attributes for the given span template.
|
|
1017
|
+
"""
|
|
1018
|
+
attributes = {} # type: dict[str, Any]
|
|
1019
|
+
|
|
1020
|
+
if template in [SPANTEMPLATE.AI_AGENT, SPANTEMPLATE.AI_TOOL, SPANTEMPLATE.AI_CHAT]:
|
|
1021
|
+
with capture_internal_exceptions():
|
|
1022
|
+
# Usage from result, result.usage, and result.metadata.usage
|
|
1023
|
+
usage_candidates = [result]
|
|
1024
|
+
|
|
1025
|
+
usage = _get_value(result, "usage")
|
|
1026
|
+
usage_candidates.append(usage)
|
|
1027
|
+
|
|
1028
|
+
meta = _get_value(result, "metadata")
|
|
1029
|
+
usage = _get_value(meta, "usage")
|
|
1030
|
+
usage_candidates.append(usage)
|
|
1031
|
+
|
|
1032
|
+
for usage_candidate in usage_candidates:
|
|
1033
|
+
if usage_candidate is not None:
|
|
1034
|
+
attributes.update(_get_usage_attributes(usage_candidate))
|
|
1035
|
+
|
|
1036
|
+
# Response model
|
|
1037
|
+
model_name = _get_value(result, "model")
|
|
1038
|
+
if model_name is not None and isinstance(model_name, str):
|
|
1039
|
+
attributes[SPANDATA.GEN_AI_RESPONSE_MODEL] = model_name
|
|
1040
|
+
|
|
1041
|
+
model_name = _get_value(result, "model_name")
|
|
1042
|
+
if model_name is not None and isinstance(model_name, str):
|
|
1043
|
+
attributes[SPANDATA.GEN_AI_RESPONSE_MODEL] = model_name
|
|
1044
|
+
|
|
1045
|
+
# Tool output
|
|
1046
|
+
if template == SPANTEMPLATE.AI_TOOL and send_pii:
|
|
1047
|
+
attributes[SPANDATA.GEN_AI_TOOL_OUTPUT] = safe_repr(result)
|
|
1048
|
+
|
|
1049
|
+
return attributes
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
def _set_input_attributes(
|
|
1053
|
+
span: sentry_sdk.tracing.Span,
|
|
1054
|
+
template: Union[str, SPANTEMPLATE],
|
|
1055
|
+
send_pii: bool,
|
|
1056
|
+
name: str,
|
|
1057
|
+
f: Any,
|
|
1058
|
+
args: tuple[Any, ...],
|
|
1059
|
+
kwargs: dict[str, Any],
|
|
1060
|
+
) -> None:
|
|
1061
|
+
"""
|
|
1062
|
+
Set span input attributes based on the given span template.
|
|
1063
|
+
|
|
1064
|
+
:param span: The span to set attributes on.
|
|
1065
|
+
:param template: The template to use to set attributes on the span.
|
|
1066
|
+
:param send_pii: Whether to send PII data.
|
|
1067
|
+
:param f: The wrapped function.
|
|
1068
|
+
:param args: The arguments to the wrapped function.
|
|
1069
|
+
:param kwargs: The keyword arguments to the wrapped function.
|
|
1070
|
+
"""
|
|
1071
|
+
attributes = {} # type: dict[str, Any]
|
|
1072
|
+
|
|
1073
|
+
if template == SPANTEMPLATE.AI_AGENT:
|
|
1074
|
+
attributes = {
|
|
1075
|
+
SPANDATA.GEN_AI_OPERATION_NAME: "invoke_agent",
|
|
1076
|
+
SPANDATA.GEN_AI_AGENT_NAME: name,
|
|
1077
|
+
}
|
|
1078
|
+
elif template == SPANTEMPLATE.AI_CHAT:
|
|
1079
|
+
attributes = {
|
|
1080
|
+
SPANDATA.GEN_AI_OPERATION_NAME: "chat",
|
|
1081
|
+
}
|
|
1082
|
+
elif template == SPANTEMPLATE.AI_TOOL:
|
|
1083
|
+
attributes = {
|
|
1084
|
+
SPANDATA.GEN_AI_OPERATION_NAME: "execute_tool",
|
|
1085
|
+
SPANDATA.GEN_AI_TOOL_NAME: name,
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
docstring = f.__doc__
|
|
1089
|
+
if docstring is not None:
|
|
1090
|
+
attributes[SPANDATA.GEN_AI_TOOL_DESCRIPTION] = docstring
|
|
1091
|
+
|
|
1092
|
+
attributes.update(_get_input_attributes(template, send_pii, args, kwargs))
|
|
1093
|
+
span.set_attributes(attributes or {})
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
def _set_output_attributes(
|
|
1097
|
+
span: sentry_sdk.tracing.Span,
|
|
1098
|
+
template: Union[str, SPANTEMPLATE],
|
|
1099
|
+
send_pii: bool,
|
|
1100
|
+
result: Any,
|
|
1101
|
+
) -> None:
|
|
1102
|
+
"""
|
|
1103
|
+
Set span output attributes based on the given span template.
|
|
1104
|
+
|
|
1105
|
+
:param span: The span to set attributes on.
|
|
1106
|
+
:param template: The template to use to set attributes on the span.
|
|
1107
|
+
:param send_pii: Whether to send PII data.
|
|
1108
|
+
:param result: The result of the wrapped function.
|
|
1109
|
+
"""
|
|
1110
|
+
span.set_attributes(_get_output_attributes(template, send_pii, result) or {})
|
|
1111
|
+
|
|
1112
|
+
|
|
816
1113
|
def get_span_status_from_http_code(http_status_code: int) -> str:
|
|
817
1114
|
"""
|
|
818
1115
|
Returns the Sentry status corresponding to the given HTTP status code.
|