1# --------------------------------------------------------------------------
2#
3# Copyright (c) Microsoft Corporation. All rights reserved.
4#
5# The MIT License (MIT)
6#
7# Permission is hereby granted, free of charge, to any person obtaining a copy
8# of this software and associated documentation files (the ""Software""), to deal
9# in the Software without restriction, including without limitation the rights
10# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11# copies of the Software, and to permit persons to whom the Software is
12# furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included in
15# all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23# THE SOFTWARE.
24#
25# --------------------------------------------------------------------------
26"""The decorator to apply if you want the given function traced."""
27
28import functools
29
30from typing import Callable, Any, TypeVar, overload, Optional, Mapping, TYPE_CHECKING
31from typing_extensions import ParamSpec
32from .common import change_context, get_function_and_class_name
33from . import SpanKind as _SpanKind
34from ..settings import settings
35
36if TYPE_CHECKING:
37 from azure.core.tracing import SpanKind
38
39P = ParamSpec("P")
40T = TypeVar("T")
41
42
43@overload
44def distributed_trace(__func: Callable[P, T]) -> Callable[P, T]:
45 pass
46
47
48@overload
49def distributed_trace(
50 *,
51 name_of_span: Optional[str] = None,
52 kind: Optional["SpanKind"] = None,
53 tracing_attributes: Optional[Mapping[str, Any]] = None,
54 **kwargs: Any,
55) -> Callable[[Callable[P, T]], Callable[P, T]]:
56 pass
57
58
59def distributed_trace(
60 __func: Optional[Callable[P, T]] = None, # pylint: disable=unused-argument
61 *,
62 name_of_span: Optional[str] = None,
63 kind: Optional["SpanKind"] = None,
64 tracing_attributes: Optional[Mapping[str, Any]] = None,
65 **kwargs: Any,
66) -> Any:
67 """Decorator to apply to function to get traced automatically.
68
69 Span will use the func name or "name_of_span".
70
71 :param callable __func: A function to decorate
72 :keyword name_of_span: The span name to replace func name if necessary
73 :paramtype name_of_span: str
74 :keyword kind: The kind of the span. INTERNAL by default.
75 :paramtype kind: ~azure.core.tracing.SpanKind
76 :keyword tracing_attributes: Attributes to add to the span.
77 :paramtype tracing_attributes: Mapping[str, Any] or None
78 :return: The decorated function
79 :rtype: Any
80 """
81 if tracing_attributes is None:
82 tracing_attributes = {}
83 if kind is None:
84 kind = _SpanKind.INTERNAL
85
86 def decorator(func: Callable[P, T]) -> Callable[P, T]:
87 @functools.wraps(func)
88 def wrapper_use_tracer(*args: Any, **kwargs: Any) -> T:
89 merge_span = kwargs.pop("merge_span", False)
90 passed_in_parent = kwargs.pop("parent_span", None)
91
92 span_impl_type = settings.tracing_implementation()
93 if span_impl_type is None:
94 return func(*args, **kwargs)
95
96 # Merge span is parameter is set, but only if no explicit parent are passed
97 if merge_span and not passed_in_parent:
98 return func(*args, **kwargs)
99
100 with change_context(passed_in_parent):
101 name = name_of_span or get_function_and_class_name(func, *args)
102 with span_impl_type(name=name, kind=kind) as span:
103 for key, value in tracing_attributes.items():
104 span.add_attribute(key, value)
105 return func(*args, **kwargs)
106
107 return wrapper_use_tracer
108
109 return decorator if __func is None else decorator(__func)