Skip to content

Commit 195dcf7

Browse files
Adding tracer to v3
1 parent 1a8818d commit 195dcf7

File tree

6 files changed

+366
-48
lines changed

6 files changed

+366
-48
lines changed

aws_lambda_powertools/tracing/base.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import abc
21
import numbers
32
import traceback
3+
from abc import ABC, abstractmethod
44
from contextlib import contextmanager
55
from typing import Any, Generator, List, Optional, Sequence, Union
66

77

8-
class BaseSegment(abc.ABC):
8+
class BaseSegment(ABC):
99
"""Holds common properties and methods on segment and subsegment."""
1010

11-
@abc.abstractmethod
11+
@abstractmethod
1212
def close(self, end_time: Optional[int] = None):
1313
"""Close the trace entity by setting `end_time`
1414
and flip the in progress flag to False.
@@ -19,15 +19,15 @@ def close(self, end_time: Optional[int] = None):
1919
Time in epoch seconds, by default current time will be used.
2020
"""
2121

22-
@abc.abstractmethod
22+
@abstractmethod
2323
def add_subsegment(self, subsegment: Any):
2424
"""Add input subsegment as a child subsegment."""
2525

26-
@abc.abstractmethod
26+
@abstractmethod
2727
def remove_subsegment(self, subsegment: Any):
2828
"""Remove input subsegment from child subsegments."""
2929

30-
@abc.abstractmethod
30+
@abstractmethod
3131
def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> None:
3232
"""Annotate segment or subsegment with a key-value pair.
3333
@@ -41,7 +41,7 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> N
4141
Annotation value
4242
"""
4343

44-
@abc.abstractmethod
44+
@abstractmethod
4545
def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None:
4646
"""Add metadata to segment or subsegment. Metadata is not indexed
4747
but can be later retrieved by BatchGetTraces API.
@@ -56,7 +56,7 @@ def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None
5656
Metadata namespace, by default 'default'
5757
"""
5858

59-
@abc.abstractmethod
59+
@abstractmethod
6060
def add_exception(self, exception: BaseException, stack: List[traceback.StackSummary], remote: bool = False):
6161
"""Add an exception to trace entities.
6262
@@ -73,8 +73,8 @@ def add_exception(self, exception: BaseException, stack: List[traceback.StackSum
7373
"""
7474

7575

76-
class BaseProvider(abc.ABC):
77-
@abc.abstractmethod
76+
class BaseProvider(ABC):
77+
@abstractmethod
7878
@contextmanager
7979
def in_subsegment(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]:
8080
"""Return a subsegment context manger.
@@ -87,7 +87,7 @@ def in_subsegment(self, name=None, **kwargs) -> Generator[BaseSegment, None, Non
8787
Optional parameters to be propagated to segment
8888
"""
8989

90-
@abc.abstractmethod
90+
@abstractmethod
9191
@contextmanager
9292
def in_subsegment_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]:
9393
"""Return a subsegment async context manger.
@@ -100,7 +100,7 @@ def in_subsegment_async(self, name=None, **kwargs) -> Generator[BaseSegment, Non
100100
Optional parameters to be propagated to segment
101101
"""
102102

103-
@abc.abstractmethod
103+
@abstractmethod
104104
def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> None:
105105
"""Annotate current active trace entity with a key-value pair.
106106
@@ -114,7 +114,7 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> N
114114
Annotation value
115115
"""
116116

117-
@abc.abstractmethod
117+
@abstractmethod
118118
def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None:
119119
"""Add metadata to the current active trace entity.
120120
@@ -130,7 +130,7 @@ def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None
130130
Metadata namespace, by default 'default'
131131
"""
132132

133-
@abc.abstractmethod
133+
@abstractmethod
134134
def patch(self, modules: Sequence[str]) -> None:
135135
"""Instrument a set of supported libraries
136136
@@ -140,6 +140,6 @@ def patch(self, modules: Sequence[str]) -> None:
140140
Set of modules to be patched
141141
"""
142142

143-
@abc.abstractmethod
143+
@abstractmethod
144144
def patch_all(self) -> None:
145145
"""Instrument all supported libraries"""

aws_lambda_powertools/tracing/provider/__init__.py

Whitespace-only changes.
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from __future__ import annotations
2+
3+
from contextlib import asynccontextmanager, contextmanager
4+
from numbers import Number
5+
from typing import Any, AsyncGenerator, Generator, Literal, Sequence, Union
6+
7+
from aws_lambda_powertools.shared import constants
8+
from aws_lambda_powertools.shared.lazy_import import LazyLoader
9+
from aws_lambda_powertools.tracing.provider.base import BaseProvider, BaseSpan
10+
11+
aws_xray_sdk = LazyLoader(constants.XRAY_SDK_MODULE, globals(), constants.XRAY_SDK_MODULE)
12+
13+
14+
class XraySpan(BaseSpan):
15+
def __init__(self, subsegment):
16+
self.subsegment = subsegment
17+
self.add_subsegment = self.subsegment.add_subsegment
18+
self.remove_subsegment = self.subsegment.remove_subsegment
19+
self.put_annotation = self.subsegment.put_annotation
20+
self.put_metadata = self.subsegment.put_metadata
21+
self.add_exception = self.subsegment.add_exception
22+
self.close = self.subsegment.close
23+
24+
def set_attribute(
25+
self,
26+
key: str,
27+
value: Any,
28+
category: Literal["Annotation", "Metadata", "Auto"] = "Auto",
29+
**kwargs,
30+
) -> None:
31+
"""
32+
Set an attribute on this span with a key-value pair.
33+
34+
Parameters
35+
----------
36+
key: str
37+
attribute key
38+
value: Any
39+
Value for attribute
40+
category: Literal["Annotation","Metadata","Auto"] = "Auto"
41+
This parameter specifies the category of attribute to set.
42+
- **"Annotation"**: Sets the attribute as an Annotation.
43+
- **"Metadata"**: Sets the attribute as Metadata.
44+
- **"Auto" (default)**: Automatically determines the attribute
45+
type based on its value.
46+
47+
kwargs: Optional[dict]
48+
Optional parameters to be passed to provider.set_attributes
49+
"""
50+
if category == "Annotation":
51+
self.put_annotation(key=key, value=value)
52+
return
53+
54+
if category == "Metadata":
55+
self.put_metadata(key=key, value=value, namespace=kwargs.get("namespace", "dafault"))
56+
return
57+
58+
# Auto
59+
if isinstance(value, (str, Number, bool)):
60+
self.put_annotation(key=key, value=value)
61+
return
62+
63+
# Auto & not in (str, Number, bool)
64+
self.put_metadata(key=key, value=value, namespace=kwargs.get("namespace", "dafault"))
65+
66+
def record_exception(self, exception: BaseException, **kwargs):
67+
stack = aws_xray_sdk.core.utils.stacktrace.get_stacktrace()
68+
self.add_exception(exception=exception, stack=stack)
69+
70+
71+
class AwsXrayProvider(BaseProvider):
72+
def __init__(self, xray_recorder=None):
73+
if not xray_recorder:
74+
from aws_xray_sdk.core import xray_recorder
75+
76+
self.recorder = xray_recorder
77+
self.in_subsegment = self.recorder.in_subsegment
78+
self.in_subsegment_async = self.recorder.in_subsegment_async
79+
80+
@contextmanager
81+
def trace(self, name: str, **kwargs) -> Generator[XraySpan, None, None]:
82+
with self.in_subsegment(name=name, **kwargs) as sub_segment:
83+
yield XraySpan(subsegment=sub_segment)
84+
85+
@asynccontextmanager
86+
async def trace_async(self, name: str, **kwargs) -> AsyncGenerator[XraySpan, None]:
87+
async with self.in_subsegment_async(name=name, **kwargs) as subsegment:
88+
yield XraySpan(subsegment=subsegment)
89+
90+
def set_attribute(
91+
self,
92+
key: str,
93+
value: Any,
94+
category: Literal["Annotation", "Metadata", "Auto"] = "Auto",
95+
**kwargs,
96+
) -> None:
97+
"""
98+
Set an attribute on the current active span with a key-value pair.
99+
100+
Parameters
101+
----------
102+
key: str
103+
attribute key
104+
value: Any
105+
Value for attribute
106+
category: Literal["Annotation","Metadata","Auto"] = "Auto"
107+
This parameter specifies the type of attribute to set.
108+
- **"Annotation"**: Sets the attribute as an Annotation.
109+
- **"Metadata"**: Sets the attribute as Metadata.
110+
- **"Auto" (default)**: Automatically determines the attribute
111+
type based on its value.
112+
113+
kwargs: Optional[dict]
114+
Optional parameters to be passed to provider.set_attributes
115+
"""
116+
if category == "Annotation":
117+
self.put_annotation(key=key, value=value)
118+
return
119+
120+
if category == "Metadata":
121+
self.put_metadata(key=key, value=value, namespace=kwargs.get("namespace", "dafault"))
122+
return
123+
124+
# Auto
125+
if isinstance(value, (str, Number, bool)):
126+
self.put_annotation(key=key, value=value)
127+
return
128+
129+
# Auto & not in (str, Number, bool)
130+
self.put_metadata(key=key, value=value, namespace=kwargs.get("namespace", "dafault"))
131+
132+
def put_annotation(self, key: str, value: Union[str, Number, bool]) -> None:
133+
return self.recorder.put_annotation(key=key, value=value)
134+
135+
def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None:
136+
return self.recorder.put_metadata(key=key, value=value, namespace=namespace)
137+
138+
def patch(self, modules: Sequence[str]) -> None:
139+
return aws_xray_sdk.core.patch(modules)
140+
141+
def patch_all(self) -> None:
142+
return aws_xray_sdk.core.patch_all()
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
from abc import ABC, abstractmethod
2+
from contextlib import asynccontextmanager, contextmanager
3+
from typing import Any, AsyncGenerator, Generator, Sequence
4+
5+
6+
class BaseSpan(ABC):
7+
"""A span represents a unit of work or operation within a trace.
8+
Spans are the building blocks of Traces."""
9+
10+
@abstractmethod
11+
def set_attribute(self, key: str, value: Any, **kwargs) -> None:
12+
"""Set an attribute for a span with a key-value pair.
13+
14+
Parameters
15+
----------
16+
key: str
17+
Attribute key
18+
value: Any
19+
Attribute value
20+
kwargs: Optional[dict]
21+
Optional parameters
22+
"""
23+
24+
@abstractmethod
25+
def record_exception(self, exception: BaseException, **kwargs):
26+
"""Records an exception to this Span.
27+
28+
Parameters
29+
----------
30+
exception: Exception
31+
Caught exception during the exectution of this Span
32+
kwargs: Optional[dict]
33+
Optional parameters
34+
"""
35+
36+
37+
class BaseProvider(ABC):
38+
"""BaseProvider is an abstract base class that defines the expected behavior for tracing providers
39+
used by Tracer. Inheriting classes must implement this interface to be compatible with Tracer.
40+
"""
41+
42+
@abstractmethod
43+
@contextmanager
44+
def trace(self, name: str, **kwargs) -> Generator[BaseSpan, None, None]:
45+
"""Context manager for creating a new span and set it
46+
as the current span in this tracer's context.
47+
48+
Exiting the context manager will call the span's end method,
49+
as well as return the current span to its previous value by
50+
returning to the previous context.
51+
52+
Parameters
53+
----------
54+
name: str
55+
Span name
56+
kwargs: Optional[dict]
57+
Optional parameters to be propagated to the span
58+
"""
59+
60+
@abstractmethod
61+
@asynccontextmanager
62+
def trace_async(self, name: str, **kwargs) -> AsyncGenerator[BaseSpan, None]:
63+
"""Async Context manager for creating a new span async and set it
64+
as the current span in this tracer's context.
65+
66+
Exiting the context manager will call the span's end method,
67+
as well as return the current span to its previous value by
68+
returning to the previous context.
69+
70+
Parameters
71+
----------
72+
name: str
73+
Span name
74+
kwargs: Optional[dict]
75+
Optional parameters to be propagated to the span
76+
"""
77+
78+
@abstractmethod
79+
def set_attribute(self, key: str, value: Any, **kwargs) -> None:
80+
"""set attribute on current active span with a key-value pair.
81+
82+
Parameters
83+
----------
84+
key: str
85+
attribute key
86+
value: Any
87+
attribute value
88+
kwargs: Optional[dict]
89+
Optional parameters to be propagated to the span
90+
"""
91+
92+
@abstractmethod
93+
def patch(self, modules: Sequence[str]) -> None:
94+
"""Instrument a set of given libraries if supported by provider
95+
See specific provider for more detail
96+
97+
Exmaple
98+
-------
99+
tracer = Tracer(service="payment")
100+
libraries = (['aioboto3',mysql])
101+
# provider.patch will be called by tracer.patch
102+
tracer.patch(libraries)
103+
104+
Parameters
105+
----------
106+
modules: Set[str]
107+
Set of modules to be patched
108+
"""
109+
110+
@abstractmethod
111+
def patch_all(self) -> None:
112+
"""Instrument all supported libraries"""

0 commit comments

Comments
 (0)