From 06c9110ac2ee2f39700b04f8fb8f44fa9eb0f96c Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Sun, 28 May 2023 20:08:40 -0700 Subject: [PATCH 01/30] refactor to standalone tracer class --- aws_lambda_powertools/tracing/base.py | 1 + .../tracing/provider/__init__.py | 0 .../tracing/provider/base.py | 145 ++++++++++++++++++ .../tracing/provider/xray_provider.py | 0 aws_lambda_powertools/tracing/tracer.py | 1 + 5 files changed, 147 insertions(+) create mode 100644 aws_lambda_powertools/tracing/provider/__init__.py create mode 100644 aws_lambda_powertools/tracing/provider/base.py create mode 100644 aws_lambda_powertools/tracing/provider/xray_provider.py diff --git a/aws_lambda_powertools/tracing/base.py b/aws_lambda_powertools/tracing/base.py index 6ea58da6b5a..8a9ae16e225 100644 --- a/aws_lambda_powertools/tracing/base.py +++ b/aws_lambda_powertools/tracing/base.py @@ -5,6 +5,7 @@ from typing import Any, Generator, List, Optional, Sequence, Union +## TO: Discuss how to refactor this one. X-ray exclusive as well? class BaseSegment(abc.ABC): """Holds common properties and methods on segment and subsegment.""" diff --git a/aws_lambda_powertools/tracing/provider/__init__.py b/aws_lambda_powertools/tracing/provider/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/aws_lambda_powertools/tracing/provider/base.py b/aws_lambda_powertools/tracing/provider/base.py new file mode 100644 index 00000000000..6ea58da6b5a --- /dev/null +++ b/aws_lambda_powertools/tracing/provider/base.py @@ -0,0 +1,145 @@ +import abc +import numbers +import traceback +from contextlib import contextmanager +from typing import Any, Generator, List, Optional, Sequence, Union + + +class BaseSegment(abc.ABC): + """Holds common properties and methods on segment and subsegment.""" + + @abc.abstractmethod + def close(self, end_time: Optional[int] = None): + """Close the trace entity by setting `end_time` + and flip the in progress flag to False. + + Parameters + ---------- + end_time: int + Time in epoch seconds, by default current time will be used. + """ + + @abc.abstractmethod + def add_subsegment(self, subsegment: Any): + """Add input subsegment as a child subsegment.""" + + @abc.abstractmethod + def remove_subsegment(self, subsegment: Any): + """Remove input subsegment from child subsegments.""" + + @abc.abstractmethod + def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> None: + """Annotate segment or subsegment with a key-value pair. + + Note: Annotations will be indexed for later search query. + + Parameters + ---------- + key: str + Metadata key + value: Union[str, numbers.Number, bool] + Annotation value + """ + + @abc.abstractmethod + def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: + """Add metadata to segment or subsegment. Metadata is not indexed + but can be later retrieved by BatchGetTraces API. + + Parameters + ---------- + key: str + Metadata key + value: Any + Any object that can be serialized into a JSON string + namespace: Set[str] + Metadata namespace, by default 'default' + """ + + @abc.abstractmethod + def add_exception(self, exception: BaseException, stack: List[traceback.StackSummary], remote: bool = False): + """Add an exception to trace entities. + + Parameters + ---------- + exception: Exception + Caught exception + stack: List[traceback.StackSummary] + List of traceback summaries + + Output from `traceback.extract_stack()`. + remote: bool + Whether it's a client error (False) or downstream service error (True), by default False + """ + + +class BaseProvider(abc.ABC): + @abc.abstractmethod + @contextmanager + def in_subsegment(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: + """Return a subsegment context manger. + + Parameters + ---------- + name: str + Subsegment name + kwargs: Optional[dict] + Optional parameters to be propagated to segment + """ + + @abc.abstractmethod + @contextmanager + def in_subsegment_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: + """Return a subsegment async context manger. + + Parameters + ---------- + name: str + Subsegment name + kwargs: Optional[dict] + Optional parameters to be propagated to segment + """ + + @abc.abstractmethod + def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> None: + """Annotate current active trace entity with a key-value pair. + + Note: Annotations will be indexed for later search query. + + Parameters + ---------- + key: str + Metadata key + value: Union[str, numbers.Number, bool] + Annotation value + """ + + @abc.abstractmethod + def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: + """Add metadata to the current active trace entity. + + Note: Metadata is not indexed but can be later retrieved by BatchGetTraces API. + + Parameters + ---------- + key: str + Metadata key + value: Any + Any object that can be serialized into a JSON string + namespace: Set[str] + Metadata namespace, by default 'default' + """ + + @abc.abstractmethod + def patch(self, modules: Sequence[str]) -> None: + """Instrument a set of supported libraries + + Parameters + ---------- + modules: Set[str] + Set of modules to be patched + """ + + @abc.abstractmethod + def patch_all(self) -> None: + """Instrument all supported libraries""" diff --git a/aws_lambda_powertools/tracing/provider/xray_provider.py b/aws_lambda_powertools/tracing/provider/xray_provider.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index e61f5c0b3d2..76cbac59aa2 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -167,6 +167,7 @@ def __init__( self.patch(modules=patch_modules) if self._is_xray_provider(): + # TO why? self._disable_xray_trace_batching() def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]): From 5eab0940867bed512369404f611fee0b47c1e68b Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Mon, 29 May 2023 01:20:39 -0700 Subject: [PATCH 02/30] refactor tracer provider, add a xray provider --- .../tracing/provider/__init__.py | 3 + .../tracing/provider/base.py | 41 ++++++++++- .../tracing/provider/xray_provider.py | 70 +++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/__init__.py b/aws_lambda_powertools/tracing/provider/__init__.py index e69de29bb2d..71cad469da4 100644 --- a/aws_lambda_powertools/tracing/provider/__init__.py +++ b/aws_lambda_powertools/tracing/provider/__init__.py @@ -0,0 +1,3 @@ +from .xray_provider import Xray_provider + +__all__ = ["Xray_provider"] diff --git a/aws_lambda_powertools/tracing/provider/base.py b/aws_lambda_powertools/tracing/provider/base.py index 6ea58da6b5a..db0a4bb91c9 100644 --- a/aws_lambda_powertools/tracing/provider/base.py +++ b/aws_lambda_powertools/tracing/provider/base.py @@ -2,7 +2,7 @@ import numbers import traceback from contextlib import contextmanager -from typing import Any, Generator, List, Optional, Sequence, Union +from typing import Any, Callable, Generator, List, Optional, Sequence, Union class BaseSegment(abc.ABC): @@ -72,11 +72,30 @@ def add_exception(self, exception: BaseException, stack: List[traceback.StackSum Whether it's a client error (False) or downstream service error (True), by default False """ + # new function below + @abc.abstractmethod + def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None): + """To ignore the endpoints you don't want requests to be traced, + perhaps due to the volume of calls or sensitive URLs.""" + + @abc.abstractmethod + def inject_context(self, context): + """To inject missing context/information like service name""" + + @abc.abstractmethod + def capture_method_async( + self, + method: Callable, + capture_response: Optional[Union[bool, str]] = None, + capture_error: Optional[Union[bool, str]] = None, + ): + """To capture async method""" + class BaseProvider(abc.ABC): @abc.abstractmethod @contextmanager - def in_subsegment(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: + def trace(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: """Return a subsegment context manger. Parameters @@ -89,7 +108,7 @@ def in_subsegment(self, name=None, **kwargs) -> Generator[BaseSegment, None, Non @abc.abstractmethod @contextmanager - def in_subsegment_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: + def trace_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: """Return a subsegment async context manger. Parameters @@ -100,6 +119,18 @@ def in_subsegment_async(self, name=None, **kwargs) -> Generator[BaseSegment, Non Optional parameters to be propagated to segment """ + @abc.abstractmethod + def start_span(self, name=None) -> Generator[BaseSegment, None, None]: + """This method is proposed as a solution as it exists for other providers + This method is responsible for starting the trace. This might involve initializing some data structures, + connecting to an external service, or performing some other setup work""" + + @abc.abstractmethod + def end_span(self, span: BaseSegment): + """This method is proposed as a solution as it exists for other providers. + This method is responsible for ending the tracing of a span. This might involve finalizing data structures, + sending data to an external service, or performing some other cleanup work""" + @abc.abstractmethod def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> None: """Annotate current active trace entity with a key-value pair. @@ -130,6 +161,10 @@ def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None Metadata namespace, by default 'default' """ + @abc.abstractmethod + def put_exception(self, *args, **kwargs) -> None: + """Add exception to the current active trace entity.""" + @abc.abstractmethod def patch(self, modules: Sequence[str]) -> None: """Instrument a set of supported libraries diff --git a/aws_lambda_powertools/tracing/provider/xray_provider.py b/aws_lambda_powertools/tracing/provider/xray_provider.py index e69de29bb2d..3a8e3df1b1d 100644 --- a/aws_lambda_powertools/tracing/provider/xray_provider.py +++ b/aws_lambda_powertools/tracing/provider/xray_provider.py @@ -0,0 +1,70 @@ +from contextlib import contextmanager +from typing import Generator, Optional + +from ...shared import constants +from ...shared.lazy_import import LazyLoader +from .base import BaseProvider, BaseSegment + +aws_xray_sdk = LazyLoader(constants.XRAY_SDK_MODULE, globals(), constants.XRAY_SDK_MODULE) + + +class Xray_provider(BaseProvider): + def __init__(self): + from aws_xray_sdk.core import xray_recorder + + self.recorder = xray_recorder + self.patch = aws_xray_sdk.core.patch + self.patch_all = aws_xray_sdk.core.patch_all + self.put_annotation = self.recorder.put_annotation + self.put_metadata = self.recorder.put_metadata + + @contextmanager + def trace(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: + """Return a subsegment context manger. + + Parameters + ---------- + name: str + Subsegment name + kwargs: Optional[dict] + Optional parameters to be propagated to segment + """ + return self.recorder.in_subsegment(name) + + @contextmanager + def trace_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: + """Return a subsegment async context manger. + + Parameters + ---------- + name: str + Subsegment name + kwargs: Optional[dict] + Optional parameters to be propagated to segment + """ + return self.recorder.in_subsegment_async(name) + + # we don't need to start,end explicitly in xray + def start_span(self, name=None) -> Generator[BaseSegment, None, None]: + """This method is proposed as a solution as it exists for other providers + This method is responsible for starting the trace. This might involve initializing some data structures, + connecting to an external service, or performing some other setup work""" + raise Exception("Not implemented") + + def end_span(self, span: BaseSegment): + """This method is proposed as a solution as it exists for other providers. + This method is responsible for ending the tracing of a span. This might involve finalizing data structures, + sending data to an external service, or performing some other cleanup work""" + raise Exception("Not implemented") + + def put_exception( + self, + method_name: str, + error: Exception, + subsegment: BaseSegment, + capture_error: Optional[bool] = None, + namespace: str = None, + ): + if not capture_error: + return + self.put_metadata(key=f"{method_name} error", value=error, namespace=namespace) From cbd75f9ebe0cb8a3dd5fb13009f5648977420c63 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 30 May 2023 14:47:46 -0700 Subject: [PATCH 03/30] fix static checking error --- aws_lambda_powertools/tracing/provider/xray_provider.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/xray_provider.py b/aws_lambda_powertools/tracing/provider/xray_provider.py index 3a8e3df1b1d..2391477e9c3 100644 --- a/aws_lambda_powertools/tracing/provider/xray_provider.py +++ b/aws_lambda_powertools/tracing/provider/xray_provider.py @@ -1,3 +1,4 @@ +import logging from contextlib import contextmanager from typing import Generator, Optional @@ -5,12 +6,13 @@ from ...shared.lazy_import import LazyLoader from .base import BaseProvider, BaseSegment +logger = logging.getLogger(__name__) aws_xray_sdk = LazyLoader(constants.XRAY_SDK_MODULE, globals(), constants.XRAY_SDK_MODULE) class Xray_provider(BaseProvider): def __init__(self): - from aws_xray_sdk.core import xray_recorder + from aws_xray_sdk.core import xray_recorder # type: ignore self.recorder = xray_recorder self.patch = aws_xray_sdk.core.patch @@ -63,7 +65,7 @@ def put_exception( error: Exception, subsegment: BaseSegment, capture_error: Optional[bool] = None, - namespace: str = None, + namespace: str = "default", ): if not capture_error: return From 927710ce0bf5e6b934ad7b00bafa1e841d3902dd Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 6 Feb 2024 01:47:51 +0000 Subject: [PATCH 04/30] add_dd_poc --- aws_lambda_powertools/tracing/base.py | 2 +- aws_lambda_powertools/tracing/dd_tracer.py | 73 +++++++++++ poetry.lock | 134 ++++++++++----------- poetry.toml | 2 + pyproject.toml | 1 + 5 files changed, 144 insertions(+), 68 deletions(-) create mode 100644 aws_lambda_powertools/tracing/dd_tracer.py create mode 100644 poetry.toml diff --git a/aws_lambda_powertools/tracing/base.py b/aws_lambda_powertools/tracing/base.py index 8a9ae16e225..f37f617e6bc 100644 --- a/aws_lambda_powertools/tracing/base.py +++ b/aws_lambda_powertools/tracing/base.py @@ -5,7 +5,7 @@ from typing import Any, Generator, List, Optional, Sequence, Union -## TO: Discuss how to refactor this one. X-ray exclusive as well? +## TO-Discuss how to refactor this one. Segment seems X-ray exclusive concept class BaseSegment(abc.ABC): """Holds common properties and methods on segment and subsegment.""" diff --git a/aws_lambda_powertools/tracing/dd_tracer.py b/aws_lambda_powertools/tracing/dd_tracer.py new file mode 100644 index 00000000000..f41f1ae2430 --- /dev/null +++ b/aws_lambda_powertools/tracing/dd_tracer.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from numbers import Number +from traceback import StackSummary +from typing import Any, Generator, List, Optional, Sequence + +import ddtrace + +from .base import BaseProvider, BaseSegment + + +class DDSpan(BaseSegment): + def __init__(self, dd_span=ddtrace.Span): + self.dd_span = dd_span + + def close(self, end_time: int | None = None): + self.dd_span.finish(finish_time=float(end_time)) + + def add_subsegment(self, subsegment: Any): + raise NotImplementedError + + def remove_subsegment(self, subsegment: Any): + raise NotImplementedError + + def put_annotation(self, key: str, value: str | Number | bool) -> None: + self.dd_span.set_tag(key=key, value=value) + + def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: + self.dd_span.set_tag(key=f"{namespace}.{key}", value=value) + + def add_exception(self, exception: BaseException, stack: List[StackSummary], remote: bool = False): + self.dd_span.set_exc_info(exc_type=exception, exc_val=exception, exc_tb=stack) + + +class DDTraceProvider(BaseProvider): + def __init__(self, dd_tracer=ddtrace.Tracer): + self.dd_tracer = dd_tracer + + def in_subsegment( + self, + name=None, + service: Optional[str] = None, + resource: Optional[str] = None, + span_type: Optional[str] = None, + span_api: str = ddtrace.SPAN_API_DATADOG, + **kwargs, + ) -> Generator[BaseSegment, None, None]: + return self.dd_tracer.start_span( + name, + child_of=self.dd_tracer.context_provider.active(), + service=service, + resource=resource, + span_type=span_type, + activate=True, + span_api=span_api, + ) + + in_subsegment_async = in_subsegment + + def put_annotation(self, key: str, value: str | Number | bool) -> None: + self.dd_tracer.context_provider.active().set_tag(key=key, value=value) + + def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: + self.dd_tracer.context_provider.active().set_tag(key=f"{namespace},{key}", value=value) + + def patch(self, modules: Sequence[str]) -> None: + module_to_patch = {} + for m in modules: + module_to_patch[m] = True + ddtrace.patch(**module_to_patch) + + def patch_all(self) -> None: + ddtrace.patch_all() diff --git a/poetry.lock b/poetry.lock index b6bf62d37bb..ae2a32a3219 100644 --- a/poetry.lock +++ b/poetry.lock @@ -867,71 +867,71 @@ six = "*" [[package]] name = "ddtrace" -version = "2.4.0" +version = "2.5.2" description = "Datadog APM client library" optional = false python-versions = ">=3.7" files = [ - {file = "ddtrace-2.4.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:90641de597d3424573aa96263509800bb64018727bf74e29e250e6d21200a4be"}, - {file = "ddtrace-2.4.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:75b7d01af5fb8d279a2edb56d48af0dc221ed43f4e5049387e4a9be529217033"}, - {file = "ddtrace-2.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f104933ffbae735887e10e3e0d9a5d28dd7d42d1fd86141c4fa171c07598b561"}, - {file = "ddtrace-2.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d675545d2fd7c5be10fe704a3f151add0ce8b101c976ca0ab452699aac0d8489"}, - {file = "ddtrace-2.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b380dabf377a318ebd909423293b02beaa43ffda03ad129a5a93c4a1a4b5c6"}, - {file = "ddtrace-2.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d2f93337c1546404967525388a45174481daa72ecf7d3a1e4c21349e1a2d572c"}, - {file = "ddtrace-2.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0e345e034e8962d76642ab2763f5bdb1bc4424c2ea17d9ca5f82e093160d6ca1"}, - {file = "ddtrace-2.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa5e1a7121d08d50795e3f6218f3959cfa55363a3896210410ef354a7573de9"}, - {file = "ddtrace-2.4.0-cp310-cp310-win32.whl", hash = "sha256:d9c69a42919a27cff8d42461b301014d79683c40f60d0cb5f3000e4ff7cb907f"}, - {file = "ddtrace-2.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:962de6a60f42e2cde1823c47a3383bb0d6beaa954d57b12687688935d0ddd3d3"}, - {file = "ddtrace-2.4.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ed91c32353c8288fb95de67faa341c5ab9a089c0161ad51fc739f0db2b46866e"}, - {file = "ddtrace-2.4.0-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:410c9b9241ed2514dc9413887d852140cc7ff396b40ffc412835a14668b9b1a3"}, - {file = "ddtrace-2.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639b11f780d0ed1a372a2a6b92cc1b9c586a0fea27439557e768d5ebedabbc34"}, - {file = "ddtrace-2.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08861e4acd61198428f0d994db1bc5d2893ec816b9cd78c0c6d1fc963f0dc771"}, - {file = "ddtrace-2.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad627a4611bff8f527e2c0c0fc51be9d74a563328269f53b871901570ee4ff3"}, - {file = "ddtrace-2.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6ae2f75f2edc068d6c104ceb0e882a6dfad8f702b27384b3dac5290aebbc248"}, - {file = "ddtrace-2.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:82a0832000fedcb95856477bab95c6f151fa28ede3aceafaabe7c08beffaa577"}, - {file = "ddtrace-2.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8b1baac10f9cc3803854f802062e02ae5de0d5546f19165c3b6e8840e9b09f4"}, - {file = "ddtrace-2.4.0-cp311-cp311-win32.whl", hash = "sha256:c687fe20b17e2d24de222913dc2383e6b1462641d8ff18d27678dcb72ced82a3"}, - {file = "ddtrace-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:47296b116a97e01fe6bf48a4eea4e825212ee23288ee064964ab87ba608fc038"}, - {file = "ddtrace-2.4.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:6e2b2b4160ea53dd3e4f8bb35af7124a5e8954c8badffa81468c8a62d12acc51"}, - {file = "ddtrace-2.4.0-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:49ac0d69f98a4ff2175db39481598300fd94f038a027b537d0a66d9dbeca1ed7"}, - {file = "ddtrace-2.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2604e1c91b44d3b6fb15d0337cda1ac2c15aec215f6a44e1bb39d25b47c2633c"}, - {file = "ddtrace-2.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb7d2c846e3d7e8156199855d4db014a71d62daedba84a213416e2a488e834b3"}, - {file = "ddtrace-2.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85774e12d5d92152cd1c64f3a8a2f4dbe7f3d39201f8a8ff5e914b9639fe6e17"}, - {file = "ddtrace-2.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:418c0c990c505accc8665bfc056f4297938a54176157bf1f0765f2fae584efec"}, - {file = "ddtrace-2.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:183f7c3ddd9a2891bd1b6f5ea3af6d16517775268b3940259820ca3c83292d16"}, - {file = "ddtrace-2.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:eb90e71b70e3ea6c24711cfb5c48c711a2175c315daf07f4f28903aa773a48b7"}, - {file = "ddtrace-2.4.0-cp312-cp312-win32.whl", hash = "sha256:5eab75f1d4170c41de1f9c32e7e39714b2dd11a59d9ff7e94a199b88fa813ecd"}, - {file = "ddtrace-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:d892e0b71f3b6bcf31920b5e7fd699c86aea734bc02eec3c1b22acd8f63057e4"}, - {file = "ddtrace-2.4.0-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:c07ea7a17a2897d891ee5e95de3b0e4f57184c471e87ffcc7208b3ccd68b9fcc"}, - {file = "ddtrace-2.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05b28815e65d6361cd056c877ab051e132a6929b0d353313a499122e6522ea3"}, - {file = "ddtrace-2.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:63719bfc8fe5e8510022a3275145d6b2b1c4f955c395698fb792d99d4cda698d"}, - {file = "ddtrace-2.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:190f96eccdd8107cc93db6e79af4b8fc9403418c823d895af898cf635f5cada6"}, - {file = "ddtrace-2.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b0fdb6a2fe0eadd122df4ea3a11690cb88f4f642bd19b1a21d01e9dcfd6eb20c"}, - {file = "ddtrace-2.4.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1b2bf18ee10ea8fe668096a6c70db4161e228edee161b04719506947d7117937"}, - {file = "ddtrace-2.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ca5fa396b8df0d7b55ad9e8d5b19be09c5dedefa388bf7590340ace5ce392e14"}, - {file = "ddtrace-2.4.0-cp37-cp37m-win32.whl", hash = "sha256:c67a4d8767aa269f8dfab79ae39b8170b95de6813bd1cba17dc951f0a1ee462b"}, - {file = "ddtrace-2.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1db7931541052622a91c8c6594b274d96efe956d5dbbe09c57a50c0f74640b52"}, - {file = "ddtrace-2.4.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:a8b6ab9f26d2ea50dfa69a282d727c865461f0c1b535f973922072f700cde031"}, - {file = "ddtrace-2.4.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:9ad7aa89988b77b893c3e9898fc48e3cef9471bc2648d6a83cc800b49cad1f1f"}, - {file = "ddtrace-2.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38b95920bcc17289a0e3871830ef19030df763039021a796a1debb7fd4ea347b"}, - {file = "ddtrace-2.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9152dcc4b8a98392ce5853b8e160f8d215ddd148337d42861ab3c12635b32b75"}, - {file = "ddtrace-2.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c335be0ab8f4f376f51111219a9d85bcdbd6d75c18a8d5471817645bed1430c0"}, - {file = "ddtrace-2.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0c95339694034d4fbf9e1b2a0918f99b3936336e8deb4d513e9cf7a6ae1532f3"}, - {file = "ddtrace-2.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f8bddc5e84e50663b64fbad2e2c61203484dea06de7759a47f096514d99f5c8f"}, - {file = "ddtrace-2.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af7c4c94959481bc4060c7dfb5f7e70b1929b18089c7ea0329fc3f28707fd8a"}, - {file = "ddtrace-2.4.0-cp38-cp38-win32.whl", hash = "sha256:de3fcca4747340c835e7816009dd363d4e02dc5fc25365b2418dc3d986a6550a"}, - {file = "ddtrace-2.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:2f3dbcff2b305d34ecc63db05d0efeb923846ba07871be6f0a3509a33290fb69"}, - {file = "ddtrace-2.4.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7b43e2e890e868a133afc25f57774bb6bc8ae8841094cba4e8f2b3ee50f9c7ee"}, - {file = "ddtrace-2.4.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:de66ea03ca5b3f02d0f878fc9d486d4d4f654cf66b38d3fdf73bf314fc0e3f5b"}, - {file = "ddtrace-2.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01cba8d20d4754135411e0e3398af02bc29b3c5f3dc85b1ee8cdfb9a0532f793"}, - {file = "ddtrace-2.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb324809582b65baa682f045cb2873d686de3aa93cac75718462d0a23f980836"}, - {file = "ddtrace-2.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f475ea4e2086e6a16a48568688918b21043ba391a6f968cb9bc17ec70d51de75"}, - {file = "ddtrace-2.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1d4a5d9c89db2cc0e4a6eaf10b6d1af449d1ef14060000b23eceee19497705e"}, - {file = "ddtrace-2.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a057db38d52271b6206bac2ab23f2a36cbe547397cba1ce586021df711570559"}, - {file = "ddtrace-2.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:45ee78018276764f7fdaf1cf3b945660cf1ab39e1a03e0c61bf1984a71562204"}, - {file = "ddtrace-2.4.0-cp39-cp39-win32.whl", hash = "sha256:4f63dea207c90bb2c2d52ff9de0ee71b27aedb5d8540745e4e0b38a896737de0"}, - {file = "ddtrace-2.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:e3523c71d37fb3135d0817e92b486bcee7829c41e5465ed41b080286d7e2739d"}, - {file = "ddtrace-2.4.0.tar.gz", hash = "sha256:fb1bab23debb3a1fb71e3d6a1ce9818bc5e6ad9b885b901f78f3f28639393ecb"}, + {file = "ddtrace-2.5.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:f918538a6adb33696be653d343ee318b16ea977376d9b7214d14fe97c42e9bd9"}, + {file = "ddtrace-2.5.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f56735eb636d3ab2f7224f261d3a6bd43f884e9901d68407d485ea65f3dc0f46"}, + {file = "ddtrace-2.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72d21fe6842a8d80c8765dd699153a2475ae2d49e82e10f9668eadb08b454040"}, + {file = "ddtrace-2.5.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6e48caf63506d7ac3df7caa955b6258de91c1a1f55149506ab8ac36143770b9"}, + {file = "ddtrace-2.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc3f26e04ba7521f6885d871fd6266fedc0a7ccf2637b85579c058927404bad7"}, + {file = "ddtrace-2.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:15d78b0cd5d2090c063031d76e933b8b24e043d524a6091a751cf57b0fab025f"}, + {file = "ddtrace-2.5.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ee76beaf87695f2204b0c2c2a3664b39f3483b7a8447b28e5e2bcc899861b3eb"}, + {file = "ddtrace-2.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8840f0e82d6dca3888bd06e7ab0ca6d39009f3cd3475028d8bc03c939127afc2"}, + {file = "ddtrace-2.5.2-cp310-cp310-win32.whl", hash = "sha256:a34ccab0c8991c5fc5252d5cd6e88852cd7f77c8bf838de84e70b4a3bfacaad4"}, + {file = "ddtrace-2.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:ffa4f5779c7000fe5960156bd15339184355b30a661b0955799cae50da5d03a7"}, + {file = "ddtrace-2.5.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ea2740a3d61876cb07b271af444e98cdc8b730497cfcddbc3794c7a7441b8d15"}, + {file = "ddtrace-2.5.2-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:62e775ba9d2a2b5f952a6609029e965057bdd852ccd6e53b55c0f82ae83aa542"}, + {file = "ddtrace-2.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30186112f156a564efda5e2018240b55baee7664897ca5fc35c452d032a77185"}, + {file = "ddtrace-2.5.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9dccdc69de364cffc2b892280724c78cb54db151452a0b6d1b4a89b6f060c44"}, + {file = "ddtrace-2.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa2543c2303ab325af7794f2a8a420133cd9222e70bfbce3869da146fc5e2ba"}, + {file = "ddtrace-2.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aa2e64f79ada9f2fd5307cd0eba726d8585e47b0282fb9463aaa4b267265e94a"}, + {file = "ddtrace-2.5.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:37b4d55a5be59530e6e5761a36d727aee812be69c81b00ee0182eb62be6f3b75"}, + {file = "ddtrace-2.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6d97f990d2322a23e82203cc5a2aa694fb0d42541a44bb120390e6598a63e5f5"}, + {file = "ddtrace-2.5.2-cp311-cp311-win32.whl", hash = "sha256:5d3f1bc3ce87fbcf2256197178179ef681df720ebbc39b0559bda00247744533"}, + {file = "ddtrace-2.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:a50057085b0972e695bb1ef3042f6cd6a1a3b12111fac4985942f2dbbcf8ac2f"}, + {file = "ddtrace-2.5.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b923b099b9a1e50f01ce8bcd4d11e3255a48c71f3e6314dd9a482baed0a87ed6"}, + {file = "ddtrace-2.5.2-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:512d3975b1657c706ca9c84373e5fce323f6fc94bfac33c30876ad8d55e0ea71"}, + {file = "ddtrace-2.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c54bc474c70151d5a141061b6c20a1efabdf458e4239c790d45fa12a13b8e7d"}, + {file = "ddtrace-2.5.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5fb2bbd38dc46ba6a7ea1031c4751b1ca888be5fac8a42049ebc2517707c00d"}, + {file = "ddtrace-2.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa6fb6bcfb3810d8f0882e489e7d2ef4dd3a92b452cfdd8d1fd4703dc496b17"}, + {file = "ddtrace-2.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3f4eed40d978352c7371804ecb68bbe9e55967bb904bd03b0568554e0b6b92cf"}, + {file = "ddtrace-2.5.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:57606af5380888e2e7cc67b7c4fa5e1bc51d29c48f004b4be0cbe1b319fddc75"}, + {file = "ddtrace-2.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ee8d0259a004964a8eddb394aa84a5754435d4270cd2041e6559c9e68fa49141"}, + {file = "ddtrace-2.5.2-cp312-cp312-win32.whl", hash = "sha256:4df564e620ec7e657fcdb0d5bf1231aa1357bf49b736f0d9e9f6df17a23fc569"}, + {file = "ddtrace-2.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:637f16af1c84566bde044798312c67bc5676df949632ab02e740440558f2a598"}, + {file = "ddtrace-2.5.2-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:d24841a9390f3e169edcaf1ca5ac80599062e66dee43a510decb25e779b6f7b4"}, + {file = "ddtrace-2.5.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49aa4e0210862e829e09569de2e2f34ac17c5e246567c5b6662ec21e2a06d938"}, + {file = "ddtrace-2.5.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:985738fe875b11f05dfa2b1f21a619d499344eb740f63e01d6eae1fb29eb949b"}, + {file = "ddtrace-2.5.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8814321822e4afc95ac86fbc476dc20d78dd4b1d510c02606459df4580093d18"}, + {file = "ddtrace-2.5.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ad6c0ae7baff9d00c689834aec0627274d681ed1d2a8ae627348a6191e8d32ec"}, + {file = "ddtrace-2.5.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:aa596f2e80c525a2310e605bfa3fa6ba6790b2ae90c02e47ceee0e62ceae17a6"}, + {file = "ddtrace-2.5.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6bdfae9fa03af334820678196a4895450d0b6bd9f1b5119d42ddbd327a55fcce"}, + {file = "ddtrace-2.5.2-cp37-cp37m-win32.whl", hash = "sha256:227bb0391d310e0d5a54505c7ab59f9692a5db91dc492373489bc45726980e1d"}, + {file = "ddtrace-2.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6e55c4738b58b4452933204305243e19000f6f283af93bf51b63382100cb8f21"}, + {file = "ddtrace-2.5.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:4d9e7a9e26c38ae1e368f5d820e78459ff2d39689f40d4a3db185ddb3686c383"}, + {file = "ddtrace-2.5.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:c361ea11b442b04d8e011528205ed65b926d71d18f38d372270204eabf49b068"}, + {file = "ddtrace-2.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aafd86eeea622cd0e8cf6b63632efc67a52a32317d2a376382ef6170d383c9f"}, + {file = "ddtrace-2.5.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ff039635470ba483ed448baaf6337d85a731b17af62fef06dfa811f761f374f"}, + {file = "ddtrace-2.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20f1cb3bea1170410d603f9d557918c24d4d8783659c03817daea6352d9f37f9"}, + {file = "ddtrace-2.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7351500241eb24c7d789b371a6860ca2b0e2db1ff9d317089153b562a3a461e1"}, + {file = "ddtrace-2.5.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a2cfc6ee800890556e404b94d13680c83952efa5d3dafa72ef8cb08a8782f874"}, + {file = "ddtrace-2.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:96a791f03b62ebdb9f3e635a0e93711149123a8fc1c1c152be0d1cdb5d8e6359"}, + {file = "ddtrace-2.5.2-cp38-cp38-win32.whl", hash = "sha256:6c61e72abec3f2f6b46e53712a32a971de1b6a9be657d5ebeff1334f6146babc"}, + {file = "ddtrace-2.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:b93d8b536f5fc45a72bb2785051dc729f4d581ef2d69ed10bccae6a7487477b2"}, + {file = "ddtrace-2.5.2-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:38cbcb7b4ff1371480b29228d2b8e570e7d7b386a7632b96f9600135ec3eb9db"}, + {file = "ddtrace-2.5.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a270d128c6067f52a76ecbb658fae3f4d3bd4888baa9e6159ff82b6de14c53be"}, + {file = "ddtrace-2.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e59f3958016fcec5eb16abd7979a9ec4d850733e2a03b878b096277fc092784"}, + {file = "ddtrace-2.5.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:066403f0e00a8de09c8187037befe7463d1fab5d8178b62a07c2542792710d14"}, + {file = "ddtrace-2.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbbcbf24bca8497f1412ec438fbdc94847aef9e86092ffd4f8626bbe6d278d33"}, + {file = "ddtrace-2.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d34f8da809e2783770a6c88396b3653fb12a4196e9b5f16b8c10f37bbf2b7b31"}, + {file = "ddtrace-2.5.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9eaca41664dd0c2bd7257fe2e91c7e46718b20591bfaa0b5c01c39b599115f88"}, + {file = "ddtrace-2.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f4b67e02ba5c316711719dcfc15e94f47684e7af1785289d016a29a2c664827"}, + {file = "ddtrace-2.5.2-cp39-cp39-win32.whl", hash = "sha256:9bbd675d73aae6516e02a86cb830778771dafb0e182d5a122270ccd82ee77eed"}, + {file = "ddtrace-2.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:e93f3f5d3d57beb492b04286c758be65495908bd313df6f56865ad7af222e49e"}, + {file = "ddtrace-2.5.2.tar.gz", hash = "sha256:5addeb19eea5ebdc23c493e5635f4c8737795b48ba637117a1895f31b900985f"}, ] [package.dependencies] @@ -943,7 +943,7 @@ bytecode = [ cattrs = "*" ddsketch = ">=2.0.1" envier = "*" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-metadata = {version = "<=6.5.0", markers = "python_version < \"3.8\""} opentelemetry-api = ">=1" protobuf = ">=3" setuptools = {version = "*", markers = "python_version >= \"3.12\""} @@ -1321,13 +1321,13 @@ files = [ [[package]] name = "importlib-metadata" -version = "6.7.0" +version = "6.5.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, - {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, + {file = "importlib_metadata-6.5.0-py3-none-any.whl", hash = "sha256:03ba783c3a2c69d751b109fc0c94a62c51f581b3d6acf8ed1331b6d5729321ff"}, + {file = "importlib_metadata-6.5.0.tar.gz", hash = "sha256:7a8bdf1bc3a726297f5cfbc999e6e7ff6b4fa41b26bba4afc580448624460045"}, ] [package.dependencies] @@ -1337,7 +1337,7 @@ zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "importlib-resources" @@ -3402,4 +3402,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = "^3.7.4" -content-hash = "f4c66a8fa656902aba0c04cc8b5dc236d7f0ed6f7c3e22507cc89e711b0b62b2" +content-hash = "a9db62332c148ebb92c77e667cc75da0062ff2626af1255c0bfa2642bd587240" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 00000000000..ab1033bd372 --- /dev/null +++ b/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/pyproject.toml b/pyproject.toml index 0e576d412df..c9bff3c27d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,7 @@ redis = {version = ">=4.4,<6.0", optional = true} typing-extensions = "^4.6.2" datadog-lambda = { version = ">=4.77,<6.0", optional = true } aws-encryption-sdk = { version = "^3.1.1", optional = true } +ddtrace = "^2.5.2" [tool.poetry.dev-dependencies] coverage = {extras = ["toml"], version = "^7.2"} From e6b3a7c1700b147f29f95f77df3d2bcb9ca822e6 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Thu, 15 Feb 2024 19:07:09 +0000 Subject: [PATCH 05/30] first working POC for dd --- aws_lambda_powertools/tracing/dd_tracer.py | 26 +++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/aws_lambda_powertools/tracing/dd_tracer.py b/aws_lambda_powertools/tracing/dd_tracer.py index f41f1ae2430..1b4b7a2c8fb 100644 --- a/aws_lambda_powertools/tracing/dd_tracer.py +++ b/aws_lambda_powertools/tracing/dd_tracer.py @@ -1,5 +1,6 @@ from __future__ import annotations +import logging from numbers import Number from traceback import StackSummary from typing import Any, Generator, List, Optional, Sequence @@ -8,12 +9,15 @@ from .base import BaseProvider, BaseSegment +logger = logging.getLogger(__name__) + class DDSpan(BaseSegment): def __init__(self, dd_span=ddtrace.Span): self.dd_span = dd_span def close(self, end_time: int | None = None): + print("close is called") self.dd_span.finish(finish_time=float(end_time)) def add_subsegment(self, subsegment: Any): @@ -31,9 +35,22 @@ def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None def add_exception(self, exception: BaseException, stack: List[StackSummary], remote: bool = False): self.dd_span.set_exc_info(exc_type=exception, exc_val=exception, exc_tb=stack) + def __enter__(self): + print("entered") + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + try: + if exc_type: + self.dd_span.set_exc_info(exc_type, exc_val, exc_tb) + print("exited") + self.close() + except Exception: + logger.exception("error closing trace") + class DDTraceProvider(BaseProvider): - def __init__(self, dd_tracer=ddtrace.Tracer): + def __init__(self, dd_tracer: ddtrace.Tracer): self.dd_tracer = dd_tracer def in_subsegment( @@ -42,18 +59,15 @@ def in_subsegment( service: Optional[str] = None, resource: Optional[str] = None, span_type: Optional[str] = None, - span_api: str = ddtrace.SPAN_API_DATADOG, **kwargs, ) -> Generator[BaseSegment, None, None]: - return self.dd_tracer.start_span( + dd_span = self.dd_tracer.trace( name, - child_of=self.dd_tracer.context_provider.active(), service=service, resource=resource, span_type=span_type, - activate=True, - span_api=span_api, ) + return DDSpan(dd_span=dd_span) in_subsegment_async = in_subsegment From dde7b7f973720b449b3be249ba44e5617a889c2e Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Thu, 15 Feb 2024 19:08:07 +0000 Subject: [PATCH 06/30] fix close --- aws_lambda_powertools/tracing/dd_tracer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/tracing/dd_tracer.py b/aws_lambda_powertools/tracing/dd_tracer.py index 1b4b7a2c8fb..562896a15f4 100644 --- a/aws_lambda_powertools/tracing/dd_tracer.py +++ b/aws_lambda_powertools/tracing/dd_tracer.py @@ -18,7 +18,9 @@ def __init__(self, dd_span=ddtrace.Span): def close(self, end_time: int | None = None): print("close is called") - self.dd_span.finish(finish_time=float(end_time)) + if end_time: + end_time = float(end_time) + self.dd_span.finish(finish_time=end_time) def add_subsegment(self, subsegment: Any): raise NotImplementedError From da8effc03dc04822c2f8ba94e4072a88fe15b6a5 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 20 Feb 2024 01:46:55 +0000 Subject: [PATCH 07/30] add OPTL POC --- aws_lambda_powertools/tracing/base.py | 1 + .../tracing/provider/__init__.py | 5 +- .../tracing/provider/base.py | 180 ------------------ .../tracing/{ => provider}/dd_tracer.py | 8 +- .../tracing/provider/otel_tracer.py | 74 +++++++ .../tracing/provider/xray_provider.py | 72 ------- poetry.lock | 29 ++- pyproject.toml | 2 + 8 files changed, 112 insertions(+), 259 deletions(-) delete mode 100644 aws_lambda_powertools/tracing/provider/base.py rename aws_lambda_powertools/tracing/{ => provider}/dd_tracer.py (94%) create mode 100644 aws_lambda_powertools/tracing/provider/otel_tracer.py delete mode 100644 aws_lambda_powertools/tracing/provider/xray_provider.py diff --git a/aws_lambda_powertools/tracing/base.py b/aws_lambda_powertools/tracing/base.py index f37f617e6bc..3b71b6d5fbd 100644 --- a/aws_lambda_powertools/tracing/base.py +++ b/aws_lambda_powertools/tracing/base.py @@ -17,6 +17,7 @@ def close(self, end_time: Optional[int] = None): Parameters ---------- end_time: int + # TO-discuss: Providers typically use ns(time_ns -> nanosecond) as start or close time Time in epoch seconds, by default current time will be used. """ diff --git a/aws_lambda_powertools/tracing/provider/__init__.py b/aws_lambda_powertools/tracing/provider/__init__.py index 71cad469da4..926f15f50c2 100644 --- a/aws_lambda_powertools/tracing/provider/__init__.py +++ b/aws_lambda_powertools/tracing/provider/__init__.py @@ -1,3 +1,4 @@ -from .xray_provider import Xray_provider +from .dd_tracer import DDTraceProvider +from .otel_tracer import OtelProvider -__all__ = ["Xray_provider"] +__all__ = ["DDTraceProvider", "OtelProvider"] diff --git a/aws_lambda_powertools/tracing/provider/base.py b/aws_lambda_powertools/tracing/provider/base.py deleted file mode 100644 index db0a4bb91c9..00000000000 --- a/aws_lambda_powertools/tracing/provider/base.py +++ /dev/null @@ -1,180 +0,0 @@ -import abc -import numbers -import traceback -from contextlib import contextmanager -from typing import Any, Callable, Generator, List, Optional, Sequence, Union - - -class BaseSegment(abc.ABC): - """Holds common properties and methods on segment and subsegment.""" - - @abc.abstractmethod - def close(self, end_time: Optional[int] = None): - """Close the trace entity by setting `end_time` - and flip the in progress flag to False. - - Parameters - ---------- - end_time: int - Time in epoch seconds, by default current time will be used. - """ - - @abc.abstractmethod - def add_subsegment(self, subsegment: Any): - """Add input subsegment as a child subsegment.""" - - @abc.abstractmethod - def remove_subsegment(self, subsegment: Any): - """Remove input subsegment from child subsegments.""" - - @abc.abstractmethod - def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> None: - """Annotate segment or subsegment with a key-value pair. - - Note: Annotations will be indexed for later search query. - - Parameters - ---------- - key: str - Metadata key - value: Union[str, numbers.Number, bool] - Annotation value - """ - - @abc.abstractmethod - def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: - """Add metadata to segment or subsegment. Metadata is not indexed - but can be later retrieved by BatchGetTraces API. - - Parameters - ---------- - key: str - Metadata key - value: Any - Any object that can be serialized into a JSON string - namespace: Set[str] - Metadata namespace, by default 'default' - """ - - @abc.abstractmethod - def add_exception(self, exception: BaseException, stack: List[traceback.StackSummary], remote: bool = False): - """Add an exception to trace entities. - - Parameters - ---------- - exception: Exception - Caught exception - stack: List[traceback.StackSummary] - List of traceback summaries - - Output from `traceback.extract_stack()`. - remote: bool - Whether it's a client error (False) or downstream service error (True), by default False - """ - - # new function below - @abc.abstractmethod - def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None): - """To ignore the endpoints you don't want requests to be traced, - perhaps due to the volume of calls or sensitive URLs.""" - - @abc.abstractmethod - def inject_context(self, context): - """To inject missing context/information like service name""" - - @abc.abstractmethod - def capture_method_async( - self, - method: Callable, - capture_response: Optional[Union[bool, str]] = None, - capture_error: Optional[Union[bool, str]] = None, - ): - """To capture async method""" - - -class BaseProvider(abc.ABC): - @abc.abstractmethod - @contextmanager - def trace(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: - """Return a subsegment context manger. - - Parameters - ---------- - name: str - Subsegment name - kwargs: Optional[dict] - Optional parameters to be propagated to segment - """ - - @abc.abstractmethod - @contextmanager - def trace_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: - """Return a subsegment async context manger. - - Parameters - ---------- - name: str - Subsegment name - kwargs: Optional[dict] - Optional parameters to be propagated to segment - """ - - @abc.abstractmethod - def start_span(self, name=None) -> Generator[BaseSegment, None, None]: - """This method is proposed as a solution as it exists for other providers - This method is responsible for starting the trace. This might involve initializing some data structures, - connecting to an external service, or performing some other setup work""" - - @abc.abstractmethod - def end_span(self, span: BaseSegment): - """This method is proposed as a solution as it exists for other providers. - This method is responsible for ending the tracing of a span. This might involve finalizing data structures, - sending data to an external service, or performing some other cleanup work""" - - @abc.abstractmethod - def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]) -> None: - """Annotate current active trace entity with a key-value pair. - - Note: Annotations will be indexed for later search query. - - Parameters - ---------- - key: str - Metadata key - value: Union[str, numbers.Number, bool] - Annotation value - """ - - @abc.abstractmethod - def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: - """Add metadata to the current active trace entity. - - Note: Metadata is not indexed but can be later retrieved by BatchGetTraces API. - - Parameters - ---------- - key: str - Metadata key - value: Any - Any object that can be serialized into a JSON string - namespace: Set[str] - Metadata namespace, by default 'default' - """ - - @abc.abstractmethod - def put_exception(self, *args, **kwargs) -> None: - """Add exception to the current active trace entity.""" - - @abc.abstractmethod - def patch(self, modules: Sequence[str]) -> None: - """Instrument a set of supported libraries - - Parameters - ---------- - modules: Set[str] - Set of modules to be patched - """ - - @abc.abstractmethod - def patch_all(self) -> None: - """Instrument all supported libraries""" diff --git a/aws_lambda_powertools/tracing/dd_tracer.py b/aws_lambda_powertools/tracing/provider/dd_tracer.py similarity index 94% rename from aws_lambda_powertools/tracing/dd_tracer.py rename to aws_lambda_powertools/tracing/provider/dd_tracer.py index 562896a15f4..a0e4732a9f8 100644 --- a/aws_lambda_powertools/tracing/dd_tracer.py +++ b/aws_lambda_powertools/tracing/provider/dd_tracer.py @@ -7,7 +7,7 @@ import ddtrace -from .base import BaseProvider, BaseSegment +from ..base import BaseProvider, BaseSegment logger = logging.getLogger(__name__) @@ -47,8 +47,8 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.dd_span.set_exc_info(exc_type, exc_val, exc_tb) print("exited") self.close() - except Exception: - logger.exception("error closing trace") + except Exception as e: + logger.exception(f"error closing trace {e}") class DDTraceProvider(BaseProvider): @@ -77,7 +77,7 @@ def put_annotation(self, key: str, value: str | Number | bool) -> None: self.dd_tracer.context_provider.active().set_tag(key=key, value=value) def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: - self.dd_tracer.context_provider.active().set_tag(key=f"{namespace},{key}", value=value) + self.dd_tracer.context_provider.active().set_tag(key=f"{namespace}.{key}", value=value) def patch(self, modules: Sequence[str]) -> None: module_to_patch = {} diff --git a/aws_lambda_powertools/tracing/provider/otel_tracer.py b/aws_lambda_powertools/tracing/provider/otel_tracer.py new file mode 100644 index 00000000000..d910a2f7142 --- /dev/null +++ b/aws_lambda_powertools/tracing/provider/otel_tracer.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import logging +from contextlib import contextmanager +from numbers import Number +from traceback import StackSummary +from typing import Any, Generator, List, Sequence + +from opentelemetry import trace as otel_trace + +from ..base import BaseProvider, BaseSegment + +logger = logging.getLogger(__name__) + + +class OtelSpan(BaseSegment): + def __init__(self, otel_span=otel_trace.Span): + self.otel_span = otel_span + + def close(self, end_time: int | None = None): + print("close is called") + self.otel_span.end(end_time=end_time) + + def add_subsegment(self, subsegment: Any): + raise NotImplementedError + + def remove_subsegment(self, subsegment: Any): + raise NotImplementedError + + def put_annotation(self, key: str, value: str | Number | bool) -> None: + self.otel_span.set_attribute(key=key, value=value) + + def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: + self.otel_span.set_attribute(key=f"{namespace}.{key}", value=str(value)) + + def add_exception(self, exception: BaseException, stack: List[StackSummary], remote: bool = False): + self.otel_span.record_exception(exception=exception, attributes={"traceback": stack, "remote": remote}) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + try: + self.otel_span.__exit__(exc_type, exc_val, exc_tb) + except Exception as e: + logger.exception(f"error closing trace {e}") + + +class OtelProvider(BaseProvider): + def __init__(self, otel_tracer: otel_trace.Tracer): + self.otel_tracer = otel_tracer + + @contextmanager + def in_subsegment(self, name: str, **kwargs) -> Generator[BaseSegment, None, None]: + with self.otel_tracer.start_as_current_span(name=name, **kwargs) as otel_span: + yield OtelSpan(otel_span=otel_span) + + start_as_current_span = in_subsegment + in_subsegment_async = in_subsegment + + def put_annotation(self, key: str, value: str | Number | bool) -> None: + active_span = otel_trace.get_current_span() + active_span.set_attribute(key=key, value=value) + + def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: + active_span = otel_trace.get_current_span() + active_span.set_attribute(key=f"{namespace}.{key}", value=value) + + def patch(self, modules: Sequence[str]) -> None: + # OTEL sdk doesn't have patch + pass + + def patch_all(self) -> None: + pass diff --git a/aws_lambda_powertools/tracing/provider/xray_provider.py b/aws_lambda_powertools/tracing/provider/xray_provider.py deleted file mode 100644 index 2391477e9c3..00000000000 --- a/aws_lambda_powertools/tracing/provider/xray_provider.py +++ /dev/null @@ -1,72 +0,0 @@ -import logging -from contextlib import contextmanager -from typing import Generator, Optional - -from ...shared import constants -from ...shared.lazy_import import LazyLoader -from .base import BaseProvider, BaseSegment - -logger = logging.getLogger(__name__) -aws_xray_sdk = LazyLoader(constants.XRAY_SDK_MODULE, globals(), constants.XRAY_SDK_MODULE) - - -class Xray_provider(BaseProvider): - def __init__(self): - from aws_xray_sdk.core import xray_recorder # type: ignore - - self.recorder = xray_recorder - self.patch = aws_xray_sdk.core.patch - self.patch_all = aws_xray_sdk.core.patch_all - self.put_annotation = self.recorder.put_annotation - self.put_metadata = self.recorder.put_metadata - - @contextmanager - def trace(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: - """Return a subsegment context manger. - - Parameters - ---------- - name: str - Subsegment name - kwargs: Optional[dict] - Optional parameters to be propagated to segment - """ - return self.recorder.in_subsegment(name) - - @contextmanager - def trace_async(self, name=None, **kwargs) -> Generator[BaseSegment, None, None]: - """Return a subsegment async context manger. - - Parameters - ---------- - name: str - Subsegment name - kwargs: Optional[dict] - Optional parameters to be propagated to segment - """ - return self.recorder.in_subsegment_async(name) - - # we don't need to start,end explicitly in xray - def start_span(self, name=None) -> Generator[BaseSegment, None, None]: - """This method is proposed as a solution as it exists for other providers - This method is responsible for starting the trace. This might involve initializing some data structures, - connecting to an external service, or performing some other setup work""" - raise Exception("Not implemented") - - def end_span(self, span: BaseSegment): - """This method is proposed as a solution as it exists for other providers. - This method is responsible for ending the tracing of a span. This might involve finalizing data structures, - sending data to an external service, or performing some other cleanup work""" - raise Exception("Not implemented") - - def put_exception( - self, - method_name: str, - error: Exception, - subsegment: BaseSegment, - capture_error: Optional[bool] = None, - namespace: str = "default", - ): - if not capture_error: - return - self.put_metadata(key=f"{method_name} error", value=error, namespace=namespace) diff --git a/poetry.lock b/poetry.lock index ae2a32a3219..39fa10ef011 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2066,6 +2066,33 @@ files = [ deprecated = ">=1.2.6" importlib-metadata = ">=6.0,<7.0" +[[package]] +name = "opentelemetry-sdk" +version = "1.22.0" +description = "OpenTelemetry Python SDK" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_sdk-1.22.0-py3-none-any.whl", hash = "sha256:a730555713d7c8931657612a88a141e3a4fe6eb5523d9e2d5a8b1e673d76efa6"}, + {file = "opentelemetry_sdk-1.22.0.tar.gz", hash = "sha256:45267ac1f38a431fc2eb5d6e0c0d83afc0b78de57ac345488aa58c28c17991d0"}, +] + +[package.dependencies] +opentelemetry-api = "1.22.0" +opentelemetry-semantic-conventions = "0.43b0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.43b0" +description = "OpenTelemetry Semantic Conventions" +optional = false +python-versions = ">=3.7" +files = [ + {file = "opentelemetry_semantic_conventions-0.43b0-py3-none-any.whl", hash = "sha256:291284d7c1bf15fdaddf309b3bd6d3b7ce12a253cec6d27144439819a15d8445"}, + {file = "opentelemetry_semantic_conventions-0.43b0.tar.gz", hash = "sha256:b9576fb890df479626fa624e88dde42d3d60b8b6c8ae1152ad157a8b97358635"}, +] + [[package]] name = "packaging" version = "23.2" @@ -3402,4 +3429,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = "^3.7.4" -content-hash = "a9db62332c148ebb92c77e667cc75da0062ff2626af1255c0bfa2642bd587240" +content-hash = "cb1f8062892aeece0e0331a70a80f99f895b76af9eae104c80ab76b3ef40251d" diff --git a/pyproject.toml b/pyproject.toml index c9bff3c27d9..c3febcb8a51 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,8 @@ typing-extensions = "^4.6.2" datadog-lambda = { version = ">=4.77,<6.0", optional = true } aws-encryption-sdk = { version = "^3.1.1", optional = true } ddtrace = "^2.5.2" +opentelemetry-api = "^1.22.0" +opentelemetry-sdk = "^1.22.0" [tool.poetry.dev-dependencies] coverage = {extras = ["toml"], version = "^7.2"} From 61f5757190e109c2b4b5c8ae208e5d0d4a1dc801 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Thu, 29 Feb 2024 10:48:50 +0000 Subject: [PATCH 08/30] add new base and xray provider --- .../tracing/provider/base.py | 90 +++++++++++++++++++ .../tracing/provider/otel_tracer.py | 5 ++ .../tracing/provider/xray_tracer.py | 61 +++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 aws_lambda_powertools/tracing/provider/base.py create mode 100644 aws_lambda_powertools/tracing/provider/xray_tracer.py diff --git a/aws_lambda_powertools/tracing/provider/base.py b/aws_lambda_powertools/tracing/provider/base.py new file mode 100644 index 00000000000..aecca308374 --- /dev/null +++ b/aws_lambda_powertools/tracing/provider/base.py @@ -0,0 +1,90 @@ +import abc +import numbers +from contextlib import contextmanager +from typing import Generator, Sequence, Union + + +## TO-Discuss how to refactor this one. Segment seems X-ray exclusive concept +class BaseSpan(abc.ABC): + """Holds common properties and methods on segment and subsegment.""" + + @abc.abstractmethod + def set_attribute(self, key: str, value: Union[str, numbers.Number, bool], **kwargs) -> None: + """Annotate segment or subsegment with a key-value pair. + + Note: Annotations will be indexed for later search query. + + Parameters + ---------- + key: str + Metadata key + value: Union[str, numbers.Number, bool] + Annotation value + """ + + @abc.abstractmethod + def record_exception(self, exception: BaseException, **kwargs): + """Add an exception to trace entities. + + Parameters + ---------- + exception: Exception + Caught exception + Output from `traceback.extract_stack()`. + """ + + +class BaseProvider(abc.ABC): + @abc.abstractmethod + @contextmanager + def trace(self, name=None, **kwargs) -> Generator[BaseSpan, None, None]: + """Return a span context manger. + + Parameters + ---------- + name: str + Span name + kwargs: Optional[dict] + Optional parameters to be propagated to span + """ + + @abc.abstractmethod + @contextmanager + def trace_async(self, name=None, **kwargs) -> Generator[BaseSpan, None, None]: + """Return a async span context manger. + + Parameters + ---------- + name: str + Span name + kwargs: Optional[dict] + Optional parameters to be propagated to span + """ + + @abc.abstractmethod + def set_attribute(self, key: str, value: Union[str, numbers.Number, bool], **kwargs) -> None: + """Annotate current active trace entity with a key-value pair. + + Note: Annotations will be indexed for later search query. + + Parameters + ---------- + key: str + Metadata key + value: Union[str, numbers.Number, bool] + Annotation value + """ + + @abc.abstractmethod + def patch(self, modules: Sequence[str]) -> None: + """Instrument a set of supported libraries + + Parameters + ---------- + modules: Set[str] + Set of modules to be patched + """ + + @abc.abstractmethod + def patch_all(self) -> None: + """Instrument all supported libraries""" diff --git a/aws_lambda_powertools/tracing/provider/otel_tracer.py b/aws_lambda_powertools/tracing/provider/otel_tracer.py index d910a2f7142..474c9e5fc14 100644 --- a/aws_lambda_powertools/tracing/provider/otel_tracer.py +++ b/aws_lambda_powertools/tracing/provider/otel_tracer.py @@ -13,6 +13,11 @@ logger = logging.getLogger(__name__) +# optl terminology first +# 1. Provider based on OTel terminology +# 2. X-Ray provider on top of the new BaseProvider +# 3. Datadog provider on top of the new BaseProvider +# access xray sdk class OtelSpan(BaseSegment): def __init__(self, otel_span=otel_trace.Span): self.otel_span = otel_span diff --git a/aws_lambda_powertools/tracing/provider/xray_tracer.py b/aws_lambda_powertools/tracing/provider/xray_tracer.py new file mode 100644 index 00000000000..bcc0ff497b6 --- /dev/null +++ b/aws_lambda_powertools/tracing/provider/xray_tracer.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from contextlib import contextmanager +from numbers import Number +from typing import Generator + +from ...shared import constants +from ...shared.lazy_import import LazyLoader +from .base import BaseProvider, BaseSpan + +aws_xray_sdk = LazyLoader(constants.XRAY_SDK_MODULE, globals(), constants.XRAY_SDK_MODULE) + + +class XraySpan(BaseSpan): + def __init__(self, subsegment): + self.subsegment = subsegment + self.add_subsegment = self.subsegment.add_subsegment + self.remove_subsegment = self.subsegment.remove_subsegment + self.put_annotation = self.subsegment.put_annotation + self.put_metadata = self.subsegment.put_metadata + self.add_exception = self.subsegment.add_exception + self.close = self.subsegment.close + + def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: + if kwargs.get("namespace", "") != "": + self.put_metadata(key=key, value=value, namespace=kwargs["namespace"]) + else: + self.put_annotation(key=key, value=value) + + def record_exception(self, exception: BaseException, **kwargs): + stack = aws_xray_sdk.core.utils.stacktrace.get_stacktrace(limit=self._max_trace_back) + self.add_exception(exception=exception, stack=stack) + + +class XrayProvider(BaseProvider): + def __init__(self, xray_recorder=None): + if not xray_recorder: + from aws_xray_sdk.core import xray_recorder + self.recorder = xray_recorder + self.patch = aws_xray_sdk.core.patch + self.patch_all = aws_xray_sdk.core.patch_all + self.in_subsegment = self.recorder.in_subsegment + self.in_subsegment_async = self.recorder.in_subsegment_async + self.put_annotation = self.recorder.put_annotation + self.put_metadata = self.recorder.put_metadata + + @contextmanager + def trace(self, name: str) -> Generator[XraySpan, None, None]: + with self.in_subsegment(name=name) as sub_segment: + yield XraySpan(subsegment=sub_segment) + + @contextmanager + def trace_async(self, name: str) -> Generator[XraySpan, None, None]: + with self.in_subsegment_async(name=name) as sub_segment: + yield XraySpan(subsegment=sub_segment) + + def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: + if kwargs.get("namespace", "") != "": + self.put_metadata(key=key, value=value, namespace=kwargs["namespace"]) + else: + self.put_annotation(key=key, value=value) From 0cb8463e67c711df381ba8454165ef0812330809 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Wed, 13 Mar 2024 22:04:24 +0000 Subject: [PATCH 09/30] add functional xray provider --- .../tracing/provider/xray_tracer.py | 12 +++++++++--- aws_lambda_powertools/tracing/tracer.py | 10 +++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/xray_tracer.py b/aws_lambda_powertools/tracing/provider/xray_tracer.py index bcc0ff497b6..03b0e6960b6 100644 --- a/aws_lambda_powertools/tracing/provider/xray_tracer.py +++ b/aws_lambda_powertools/tracing/provider/xray_tracer.py @@ -2,7 +2,7 @@ from contextlib import contextmanager from numbers import Number -from typing import Generator +from typing import Generator, Sequence from ...shared import constants from ...shared.lazy_import import LazyLoader @@ -50,8 +50,8 @@ def trace(self, name: str) -> Generator[XraySpan, None, None]: yield XraySpan(subsegment=sub_segment) @contextmanager - def trace_async(self, name: str) -> Generator[XraySpan, None, None]: - with self.in_subsegment_async(name=name) as sub_segment: + async def trace_async(self, name: str) -> Generator[XraySpan, None, None]: + async with self.in_subsegment_async(name=name) as sub_segment: yield XraySpan(subsegment=sub_segment) def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: @@ -59,3 +59,9 @@ def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: self.put_metadata(key=key, value=value, namespace=kwargs["namespace"]) else: self.put_annotation(key=key, value=value) + + def patch(self, modules: Sequence[str]) -> None: + pass + + def patch_all(self) -> None: + pass diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 71945a3701d..765fffc1322 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -308,7 +308,7 @@ def handler(event, context): @functools.wraps(lambda_handler) def decorate(event, context, **kwargs): - with self.provider.in_subsegment(name=f"## {lambda_handler_name}") as subsegment: + with self.provider.trace(name=f"## {lambda_handler_name}") as subsegment: try: logger.debug("Calling lambda handler") response = lambda_handler(event, context, **kwargs) @@ -573,7 +573,7 @@ def _decorate_async_function( ): @functools.wraps(method) async def decorate(*args, **kwargs): - async with self.provider.in_subsegment_async(name=f"## {method_name}") as subsegment: + async with self.provider.trace_async(name=f"## {method_name}") as subsegment: try: logger.debug(f"Calling method: {method_name}") response = await method(*args, **kwargs) @@ -606,7 +606,7 @@ def _decorate_generator_function( ): @functools.wraps(method) def decorate(*args, **kwargs): - with self.provider.in_subsegment(name=f"## {method_name}") as subsegment: + with self.provider.trace(name=f"## {method_name}") as subsegment: try: logger.debug(f"Calling method: {method_name}") result = yield from method(*args, **kwargs) @@ -640,7 +640,7 @@ def _decorate_generator_function_with_context_manager( @functools.wraps(method) @contextlib.contextmanager def decorate(*args, **kwargs): - with self.provider.in_subsegment(name=f"## {method_name}") as subsegment: + with self.provider.trace(name=f"## {method_name}") as subsegment: try: logger.debug(f"Calling method: {method_name}") with method(*args, **kwargs) as return_val: @@ -673,7 +673,7 @@ def _decorate_sync_function( ) -> AnyCallableT: @functools.wraps(method) def decorate(*args, **kwargs): - with self.provider.in_subsegment(name=f"## {method_name}") as subsegment: + with self.provider.trace(name=f"## {method_name}") as subsegment: try: logger.debug(f"Calling method: {method_name}") response = method(*args, **kwargs) From 64dd749f85bc762de2274c80dbdf91f3fdeae38b Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Thu, 14 Mar 2024 15:29:46 +0000 Subject: [PATCH 10/30] Merging from develop --- aws_lambda_powertools/tracing/provider/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws_lambda_powertools/tracing/provider/base.py b/aws_lambda_powertools/tracing/provider/base.py index aecca308374..b014ea227fd 100644 --- a/aws_lambda_powertools/tracing/provider/base.py +++ b/aws_lambda_powertools/tracing/provider/base.py @@ -8,6 +8,8 @@ class BaseSpan(abc.ABC): """Holds common properties and methods on segment and subsegment.""" + """Triggering""" + @abc.abstractmethod def set_attribute(self, key: str, value: Union[str, numbers.Number, bool], **kwargs) -> None: """Annotate segment or subsegment with a key-value pair. From ef7c5094282235d266ce96e6a1a2ce81dc2fc11a Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 19 Mar 2024 15:25:30 +0000 Subject: [PATCH 11/30] fix to pass test --- aws_lambda_powertools/tracing/provider/xray_tracer.py | 5 +++-- aws_lambda_powertools/tracing/tracer.py | 8 +++----- tests/unit/test_tracing.py | 2 ++ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/xray_tracer.py b/aws_lambda_powertools/tracing/provider/xray_tracer.py index 03b0e6960b6..0febda2ef85 100644 --- a/aws_lambda_powertools/tracing/provider/xray_tracer.py +++ b/aws_lambda_powertools/tracing/provider/xray_tracer.py @@ -1,6 +1,6 @@ from __future__ import annotations -from contextlib import contextmanager +from contextlib import asynccontextmanager, contextmanager from numbers import Number from typing import Generator, Sequence @@ -49,7 +49,7 @@ def trace(self, name: str) -> Generator[XraySpan, None, None]: with self.in_subsegment(name=name) as sub_segment: yield XraySpan(subsegment=sub_segment) - @contextmanager + @asynccontextmanager async def trace_async(self, name: str) -> Generator[XraySpan, None, None]: async with self.in_subsegment_async(name=name) as sub_segment: yield XraySpan(subsegment=sub_segment) @@ -61,6 +61,7 @@ def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: self.put_annotation(key=key, value=value) def patch(self, modules: Sequence[str]) -> None: + # defined in init pass def patch_all(self) -> None: diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 765fffc1322..364891a3fc2 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -12,6 +12,7 @@ from ..shared.lazy_import import LazyLoader from ..shared.types import AnyCallableT from .base import BaseProvider, BaseSegment +from .provider import xray_tracer is_cold_start = True logger = logging.getLogger(__name__) @@ -808,11 +809,8 @@ def _patch_xray_provider(self): # Due to Lazy Import, we need to activate `core` attrib via import # we also need to include `patch`, `patch_all` methods # to ensure patch calls are done via the provider - from aws_xray_sdk.core import xray_recorder # type: ignore - provider = xray_recorder - provider.patch = aws_xray_sdk.core.patch - provider.patch_all = aws_xray_sdk.core.patch_all + provider = xray_tracer.XrayProvider() return provider @@ -827,7 +825,7 @@ def _disable_xray_trace_batching(self): aws_xray_sdk.core.xray_recorder.configure(streaming_threshold=0) def _is_xray_provider(self): - return "aws_xray_sdk" in self.provider.__module__ + return isinstance(self.provider, xray_tracer.XrayProvider) def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None): """If you want to ignore certain httplib requests you can do so based on the hostname or URL that is being diff --git a/tests/unit/test_tracing.py b/tests/unit/test_tracing.py index ae140eb2bee..5c977f288bc 100644 --- a/tests/unit/test_tracing.py +++ b/tests/unit/test_tracing.py @@ -33,9 +33,11 @@ def __init__( self.put_metadata_mock = put_metadata_mock or mocker.MagicMock() self.put_annotation_mock = put_annotation_mock or mocker.MagicMock() self.in_subsegment = in_subsegment or mocker.MagicMock() + self.trace = self.in_subsegment self.patch_mock = patch_mock or mocker.MagicMock() self.disable_tracing_provider_mock = disable_tracing_provider_mock or mocker.MagicMock() self.in_subsegment_async = in_subsegment_async or mocker.MagicMock(spec=True) + self.trace_async = self.in_subsegment_async def put_metadata(self, *args, **kwargs): return self.put_metadata_mock(*args, **kwargs) From 78051f67856192a87ea01159139267f69ab05eb3 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Wed, 20 Mar 2024 00:14:32 +0000 Subject: [PATCH 12/30] fix mypy, move into folder --- .../tracing/provider/__init__.py | 7 +- .../tracing/provider/base.py | 12 +-- .../tracing/provider/datadog/dd_tracer.py | 96 +++++++++++++++++++ .../tracing/provider/dd_tracer.py | 89 ----------------- .../tracing/provider/otel/otel_tracer.py | 83 ++++++++++++++++ .../tracing/provider/otel_tracer.py | 79 --------------- .../provider/{ => xray}/xray_tracer.py | 18 ++-- aws_lambda_powertools/tracing/tracer.py | 6 +- poetry.lock | 13 ++- pyproject.toml | 1 + 10 files changed, 213 insertions(+), 191 deletions(-) create mode 100644 aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py delete mode 100644 aws_lambda_powertools/tracing/provider/dd_tracer.py create mode 100644 aws_lambda_powertools/tracing/provider/otel/otel_tracer.py delete mode 100644 aws_lambda_powertools/tracing/provider/otel_tracer.py rename aws_lambda_powertools/tracing/provider/{ => xray}/xray_tracer.py (82%) diff --git a/aws_lambda_powertools/tracing/provider/__init__.py b/aws_lambda_powertools/tracing/provider/__init__.py index 926f15f50c2..fbcd106d742 100644 --- a/aws_lambda_powertools/tracing/provider/__init__.py +++ b/aws_lambda_powertools/tracing/provider/__init__.py @@ -1,4 +1,5 @@ -from .dd_tracer import DDTraceProvider -from .otel_tracer import OtelProvider +from .datadog.dd_tracer import DDTraceProvider +from .otel.otel_tracer import OtelProvider +from .xray.xray_tracer import XrayProvider -__all__ = ["DDTraceProvider", "OtelProvider"] +__all__ = ["DDTraceProvider", "OtelProvider", "XrayProvider"] diff --git a/aws_lambda_powertools/tracing/provider/base.py b/aws_lambda_powertools/tracing/provider/base.py index b014ea227fd..e5953459cf6 100644 --- a/aws_lambda_powertools/tracing/provider/base.py +++ b/aws_lambda_powertools/tracing/provider/base.py @@ -1,15 +1,13 @@ import abc import numbers -from contextlib import contextmanager -from typing import Generator, Sequence, Union +from contextlib import asynccontextmanager, contextmanager +from typing import AsyncGenerator, Generator, Sequence, Union ## TO-Discuss how to refactor this one. Segment seems X-ray exclusive concept class BaseSpan(abc.ABC): """Holds common properties and methods on segment and subsegment.""" - """Triggering""" - @abc.abstractmethod def set_attribute(self, key: str, value: Union[str, numbers.Number, bool], **kwargs) -> None: """Annotate segment or subsegment with a key-value pair. @@ -39,7 +37,7 @@ def record_exception(self, exception: BaseException, **kwargs): class BaseProvider(abc.ABC): @abc.abstractmethod @contextmanager - def trace(self, name=None, **kwargs) -> Generator[BaseSpan, None, None]: + def trace(self, name: str, **kwargs) -> Generator[BaseSpan, None, None]: """Return a span context manger. Parameters @@ -51,8 +49,8 @@ def trace(self, name=None, **kwargs) -> Generator[BaseSpan, None, None]: """ @abc.abstractmethod - @contextmanager - def trace_async(self, name=None, **kwargs) -> Generator[BaseSpan, None, None]: + @asynccontextmanager + def trace_async(self, name: str, **kwargs) -> AsyncGenerator[BaseSpan, None]: """Return a async span context manger. Parameters diff --git a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py new file mode 100644 index 00000000000..6773c5febf3 --- /dev/null +++ b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +import logging +from contextlib import asynccontextmanager, contextmanager +from typing import Any, AsyncGenerator, Dict, Generator, Optional, Sequence + +import ddtrace + +from ..base import BaseProvider, BaseSpan + +logger = logging.getLogger(__name__) + + +class DDSpan(BaseSpan): + def __init__(self, dd_span=ddtrace.Span): + self.dd_span = dd_span + + def set_attribute(self, key: str | bytes, value: Any, **kwargs) -> None: + self.dd_span.set_tag(key=key, value=value) + + def record_exception(self, exception: BaseException, attributes: Optional[Dict] = None, **kwargs): + self.dd_span.set_traceback() + _attributes = attributes or {} + if kwargs: + kwargs.update(_attributes) + # attribute should overwrite kwargs + _attributes = kwargs + self.dd_span.set_tags(tags=attributes) + + def __enter__(self): + print("entered") + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + try: + if exc_type: + self.dd_span.set_exc_info(exc_type, exc_val, exc_tb) + self.dd_span.finish() + except Exception as e: + logger.exception(f"error closing trace {e}") + + +class DDTraceProvider(BaseProvider): + def __init__(self, dd_tracer: ddtrace.Tracer): + self.dd_tracer = dd_tracer + + @contextmanager + def trace( + self, + name: str, + service: Optional[str] = None, + resource: Optional[str] = None, + span_type: Optional[str] = None, + **kwargs, + ) -> Generator[DDSpan, None, None]: + dd_span = self.dd_tracer.trace( + name=name, + service=service, + resource=resource, + span_type=span_type, + ) + yield DDSpan(dd_span=dd_span) + + in_subsegment = trace + + @asynccontextmanager + async def trace_async( + self, + name: str, + service: Optional[str] = None, + resource: Optional[str] = None, + span_type: Optional[str] = None, + **kwargs, + ) -> AsyncGenerator[DDSpan, None]: + dd_span = self.dd_tracer.trace( + name=name, + service=service, + resource=resource, + span_type=span_type, + ) + yield DDSpan(dd_span=dd_span) + + def set_attribute(self, key: str | bytes, value: Any, **kwargs: Any) -> None: + span = self.dd_tracer.context_provider.active() + if isinstance(span, ddtrace.Span): + span.set_tag(key=key, value=value) + # ignore if no active span + + def patch(self, modules: Sequence[str]) -> None: + module_to_patch = {} + for m in modules: + module_to_patch[m] = True + ddtrace.patch(**module_to_patch) # type:ignore + + def patch_all(self) -> None: + ddtrace.patch_all() diff --git a/aws_lambda_powertools/tracing/provider/dd_tracer.py b/aws_lambda_powertools/tracing/provider/dd_tracer.py deleted file mode 100644 index a0e4732a9f8..00000000000 --- a/aws_lambda_powertools/tracing/provider/dd_tracer.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import annotations - -import logging -from numbers import Number -from traceback import StackSummary -from typing import Any, Generator, List, Optional, Sequence - -import ddtrace - -from ..base import BaseProvider, BaseSegment - -logger = logging.getLogger(__name__) - - -class DDSpan(BaseSegment): - def __init__(self, dd_span=ddtrace.Span): - self.dd_span = dd_span - - def close(self, end_time: int | None = None): - print("close is called") - if end_time: - end_time = float(end_time) - self.dd_span.finish(finish_time=end_time) - - def add_subsegment(self, subsegment: Any): - raise NotImplementedError - - def remove_subsegment(self, subsegment: Any): - raise NotImplementedError - - def put_annotation(self, key: str, value: str | Number | bool) -> None: - self.dd_span.set_tag(key=key, value=value) - - def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: - self.dd_span.set_tag(key=f"{namespace}.{key}", value=value) - - def add_exception(self, exception: BaseException, stack: List[StackSummary], remote: bool = False): - self.dd_span.set_exc_info(exc_type=exception, exc_val=exception, exc_tb=stack) - - def __enter__(self): - print("entered") - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - try: - if exc_type: - self.dd_span.set_exc_info(exc_type, exc_val, exc_tb) - print("exited") - self.close() - except Exception as e: - logger.exception(f"error closing trace {e}") - - -class DDTraceProvider(BaseProvider): - def __init__(self, dd_tracer: ddtrace.Tracer): - self.dd_tracer = dd_tracer - - def in_subsegment( - self, - name=None, - service: Optional[str] = None, - resource: Optional[str] = None, - span_type: Optional[str] = None, - **kwargs, - ) -> Generator[BaseSegment, None, None]: - dd_span = self.dd_tracer.trace( - name, - service=service, - resource=resource, - span_type=span_type, - ) - return DDSpan(dd_span=dd_span) - - in_subsegment_async = in_subsegment - - def put_annotation(self, key: str, value: str | Number | bool) -> None: - self.dd_tracer.context_provider.active().set_tag(key=key, value=value) - - def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: - self.dd_tracer.context_provider.active().set_tag(key=f"{namespace}.{key}", value=value) - - def patch(self, modules: Sequence[str]) -> None: - module_to_patch = {} - for m in modules: - module_to_patch[m] = True - ddtrace.patch(**module_to_patch) - - def patch_all(self) -> None: - ddtrace.patch_all() diff --git a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py new file mode 100644 index 00000000000..7280af4fe53 --- /dev/null +++ b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py @@ -0,0 +1,83 @@ +from __future__ import annotations + +import logging +from contextlib import asynccontextmanager, contextmanager +from typing import AsyncGenerator, Dict, Generator, Optional, Sequence + +from opentelemetry import trace as otel_trace + +from ..base import BaseProvider, BaseSpan + +logger = logging.getLogger(__name__) + + +# optl terminology first +# 1. Provider based on OTel terminology +# 2. X-Ray provider on top of the new BaseProvider +# 3. Datadog provider on top of the new BaseProvider +# access xray sdk +class OtelSpan(BaseSpan): + def __init__(self, otel_span=otel_trace.Span): + self.otel_span = otel_span + + def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: # type: ignore[override] + self.otel_span.set_attribute(key=key, value=value) + + def record_exception( + self, + exception: BaseException, + attributes: Optional[Dict] = None, + timestamp: Optional[int] = None, + escaped: bool = False, + **kwargs, + ): + _attributes = attributes or {} + if kwargs: + kwargs.update(_attributes) + # attribute should overwrite kwargs + _attributes = kwargs + self.otel_span.record_exception( + exception=exception, + attributes=_attributes, + timestamp=timestamp, + escaped=escaped, + ) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + try: + self.otel_span.__exit__(exc_type, exc_val, exc_tb) + except Exception as e: + logger.exception(f"error closing trace {e}") + + +class OtelProvider(BaseProvider): + def __init__(self, otel_tracer: otel_trace.Tracer): + self.otel_tracer = otel_tracer + + @contextmanager + def trace(self, name: str, **kwargs) -> Generator[OtelSpan, None, None]: + with self.otel_tracer.start_as_current_span(name=name, **kwargs) as otel_span: + yield OtelSpan(otel_span=otel_span) + + in_subsegment = trace + + @asynccontextmanager + async def trace_async(self, name: str, **kwargs) -> AsyncGenerator[OtelSpan, None]: + with self.otel_tracer.start_as_current_span(name=name, **kwargs) as otel_span: + yield OtelSpan(otel_span=otel_span) + + in_subsegment_async = trace_async + + def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: # type: ignore[override] + active_span = otel_trace.get_current_span() + active_span.set_attribute(key=key, value=value) + + def patch(self, modules: Sequence[str]) -> None: + # OTEL sdk doesn't have patch + pass + + def patch_all(self) -> None: + pass diff --git a/aws_lambda_powertools/tracing/provider/otel_tracer.py b/aws_lambda_powertools/tracing/provider/otel_tracer.py deleted file mode 100644 index 474c9e5fc14..00000000000 --- a/aws_lambda_powertools/tracing/provider/otel_tracer.py +++ /dev/null @@ -1,79 +0,0 @@ -from __future__ import annotations - -import logging -from contextlib import contextmanager -from numbers import Number -from traceback import StackSummary -from typing import Any, Generator, List, Sequence - -from opentelemetry import trace as otel_trace - -from ..base import BaseProvider, BaseSegment - -logger = logging.getLogger(__name__) - - -# optl terminology first -# 1. Provider based on OTel terminology -# 2. X-Ray provider on top of the new BaseProvider -# 3. Datadog provider on top of the new BaseProvider -# access xray sdk -class OtelSpan(BaseSegment): - def __init__(self, otel_span=otel_trace.Span): - self.otel_span = otel_span - - def close(self, end_time: int | None = None): - print("close is called") - self.otel_span.end(end_time=end_time) - - def add_subsegment(self, subsegment: Any): - raise NotImplementedError - - def remove_subsegment(self, subsegment: Any): - raise NotImplementedError - - def put_annotation(self, key: str, value: str | Number | bool) -> None: - self.otel_span.set_attribute(key=key, value=value) - - def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: - self.otel_span.set_attribute(key=f"{namespace}.{key}", value=str(value)) - - def add_exception(self, exception: BaseException, stack: List[StackSummary], remote: bool = False): - self.otel_span.record_exception(exception=exception, attributes={"traceback": stack, "remote": remote}) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - try: - self.otel_span.__exit__(exc_type, exc_val, exc_tb) - except Exception as e: - logger.exception(f"error closing trace {e}") - - -class OtelProvider(BaseProvider): - def __init__(self, otel_tracer: otel_trace.Tracer): - self.otel_tracer = otel_tracer - - @contextmanager - def in_subsegment(self, name: str, **kwargs) -> Generator[BaseSegment, None, None]: - with self.otel_tracer.start_as_current_span(name=name, **kwargs) as otel_span: - yield OtelSpan(otel_span=otel_span) - - start_as_current_span = in_subsegment - in_subsegment_async = in_subsegment - - def put_annotation(self, key: str, value: str | Number | bool) -> None: - active_span = otel_trace.get_current_span() - active_span.set_attribute(key=key, value=value) - - def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: - active_span = otel_trace.get_current_span() - active_span.set_attribute(key=f"{namespace}.{key}", value=value) - - def patch(self, modules: Sequence[str]) -> None: - # OTEL sdk doesn't have patch - pass - - def patch_all(self) -> None: - pass diff --git a/aws_lambda_powertools/tracing/provider/xray_tracer.py b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py similarity index 82% rename from aws_lambda_powertools/tracing/provider/xray_tracer.py rename to aws_lambda_powertools/tracing/provider/xray/xray_tracer.py index 0febda2ef85..54afe897f0e 100644 --- a/aws_lambda_powertools/tracing/provider/xray_tracer.py +++ b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py @@ -2,11 +2,11 @@ from contextlib import asynccontextmanager, contextmanager from numbers import Number -from typing import Generator, Sequence +from typing import AsyncGenerator, Generator, Sequence -from ...shared import constants -from ...shared.lazy_import import LazyLoader -from .base import BaseProvider, BaseSpan +from ....shared import constants +from ....shared.lazy_import import LazyLoader +from ..base import BaseProvider, BaseSpan aws_xray_sdk = LazyLoader(constants.XRAY_SDK_MODULE, globals(), constants.XRAY_SDK_MODULE) @@ -28,7 +28,7 @@ def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: self.put_annotation(key=key, value=value) def record_exception(self, exception: BaseException, **kwargs): - stack = aws_xray_sdk.core.utils.stacktrace.get_stacktrace(limit=self._max_trace_back) + stack = aws_xray_sdk.core.utils.stacktrace.get_stacktrace() self.add_exception(exception=exception, stack=stack) @@ -45,14 +45,14 @@ def __init__(self, xray_recorder=None): self.put_metadata = self.recorder.put_metadata @contextmanager - def trace(self, name: str) -> Generator[XraySpan, None, None]: + def trace(self, name: str, **kwargs) -> Generator[XraySpan, None, None]: with self.in_subsegment(name=name) as sub_segment: yield XraySpan(subsegment=sub_segment) @asynccontextmanager - async def trace_async(self, name: str) -> Generator[XraySpan, None, None]: - async with self.in_subsegment_async(name=name) as sub_segment: - yield XraySpan(subsegment=sub_segment) + async def trace_async(self, name: str, **kwargs) -> AsyncGenerator[XraySpan, None]: + async with self.in_subsegment_async(name=name) as subsegment: + yield XraySpan(subsegment=subsegment) def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: if kwargs.get("namespace", "") != "": diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 2b67cdd903d..701d5572132 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -12,7 +12,7 @@ from aws_lambda_powertools.shared.lazy_import import LazyLoader from aws_lambda_powertools.shared.types import AnyCallableT from aws_lambda_powertools.tracing.base import BaseProvider, BaseSegment -from aws_lambda_powertools.tracing.provider import xray_tracer +from aws_lambda_powertools.tracing.provider import XrayProvider is_cold_start = True logger = logging.getLogger(__name__) @@ -810,7 +810,7 @@ def _patch_xray_provider(self): # we also need to include `patch`, `patch_all` methods # to ensure patch calls are done via the provider - provider = xray_tracer.XrayProvider() + provider = XrayProvider() return provider @@ -825,7 +825,7 @@ def _disable_xray_trace_batching(self): aws_xray_sdk.core.xray_recorder.configure(streaming_threshold=0) def _is_xray_provider(self): - return isinstance(self.provider, xray_tracer.XrayProvider) + return isinstance(self.provider, XrayProvider) def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None): """If you want to ignore certain httplib requests you can do so based on the hostname or URL that is being diff --git a/poetry.lock b/poetry.lock index 4ccd49fb65e..dd011162359 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3247,6 +3247,17 @@ files = [ doc = ["sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] test = ["mypy", "pytest", "typing-extensions"] +[[package]] +name = "types-aws-xray-sdk" +version = "2.13.0.20240308" +description = "Typing stubs for aws-xray-sdk" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-aws-xray-sdk-2.13.0.20240308.tar.gz", hash = "sha256:034f76378a5f0854f59aa8df17bfd3ee526e9172cee410757e4382b67c8f86da"}, + {file = "types_aws_xray_sdk-2.13.0.20240308-py3-none-any.whl", hash = "sha256:e29e3ef6a3c10d962575ad32cc1d623a079a30c287b980e6372017292a4c9e55"}, +] + [[package]] name = "types-pyopenssl" version = "24.0.0.20240311" @@ -3559,4 +3570,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0.0" -content-hash = "73cacb6abaf392f5c2bf68a6c09121d26eb123c0de7816d4cea41d73e2068a27" +content-hash = "c190d67369d09d1c48d1fd5e8759b2abeb39c8877f6ee4927841301118c33eed" diff --git a/pyproject.toml b/pyproject.toml index 562122e9dd8..509ddfcc41b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ ddtrace = "^2.5.2" opentelemetry-api = "^1.22.0" opentelemetry-sdk = "^1.22.0" jsonpath-ng = { version = "^1.6.0", optional = true } +types-aws-xray-sdk = "^2.13.0.20240308" [tool.poetry.dev-dependencies] coverage = { extras = ["toml"], version = "^7.4" } From 5bc015893709a17560ab0a956306419e37e81dc5 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Wed, 20 Mar 2024 17:08:31 +0000 Subject: [PATCH 13/30] fix poetry --- poetry.lock | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/poetry.lock b/poetry.lock index d2128e72d1a..dc97913ea9b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -349,17 +349,17 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.34.65" +version = "1.34.66" description = "The AWS SDK for Python" optional = false python-versions = ">= 3.8" files = [ - {file = "boto3-1.34.65-py3-none-any.whl", hash = "sha256:b611de58ab28940a36c77d7ef9823427ebf25d5ee8277b802f9979b14e780534"}, - {file = "boto3-1.34.65.tar.gz", hash = "sha256:db97f9c29f1806cf9020679be0dd5ffa2aff2670e28e0e2046f98b979be498a4"}, + {file = "boto3-1.34.66-py3-none-any.whl", hash = "sha256:036989117c0bc4029daaa4cf713c4ff8c227b3eac6ef0e2118eb4098c114080e"}, + {file = "boto3-1.34.66.tar.gz", hash = "sha256:b1d6be3d5833e56198dc635ff4b428b93e5a2a2bd9bc4d94581a572a1ce97cfe"}, ] [package.dependencies] -botocore = ">=1.34.65,<1.35.0" +botocore = ">=1.34.66,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -368,13 +368,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.65" +version = "1.34.66" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">= 3.8" files = [ - {file = "botocore-1.34.65-py3-none-any.whl", hash = "sha256:3b0012d7293880c0a4883883047e93f2888d7317b5e9e8a982a991b90d951f3e"}, - {file = "botocore-1.34.65.tar.gz", hash = "sha256:399a1b1937f7957f0ee2e0df351462b86d44986b795ced980c11eb768b0e61c5"}, + {file = "botocore-1.34.66-py3-none-any.whl", hash = "sha256:92560f8fbdaa9dd221212a3d3a7609219ba0bbf308c13571674c0cda9d8f39e1"}, + {file = "botocore-1.34.66.tar.gz", hash = "sha256:fd7d8742007c220f897cb126b8916ca0cf3724a739d4d716aa5385d7f9d8aeb1"}, ] [package.dependencies] @@ -429,13 +429,13 @@ ujson = ["ujson (>=5.7.0)"] [[package]] name = "cdk-nag" -version = "2.28.68" +version = "2.28.69" description = "Check CDK v2 applications for best practices using a combination on available rule packs." optional = false python-versions = "~=3.8" files = [ - {file = "cdk-nag-2.28.68.tar.gz", hash = "sha256:070f79793230b4e9183fd9ef2dd869ce97c9284b4cbc6ed5b61943075fc8d745"}, - {file = "cdk_nag-2.28.68-py3-none-any.whl", hash = "sha256:43652b2bae579b1cbbff715fffa5f85c163022a33e3c261c950d349b2e34a22f"}, + {file = "cdk-nag-2.28.69.tar.gz", hash = "sha256:7b71070e1f70ba6642a9f03f01dd9d8a603122c41c3e6ddd831cd950bbdfad4a"}, + {file = "cdk_nag-2.28.69-py3-none-any.whl", hash = "sha256:6a4616c0b66a99776a68549ae4f470a6bc7451e6348dd9a25b80f9fe66ff3082"}, ] [package.dependencies] @@ -1377,13 +1377,13 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs [[package]] name = "importlib-resources" -version = "6.3.1" +version = "6.3.2" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.3.1-py3-none-any.whl", hash = "sha256:4811639ca7fa830abdb8e9ca0a104dc6ad13de691d9fe0d3173a71304f068159"}, - {file = "importlib_resources-6.3.1.tar.gz", hash = "sha256:29a3d16556e330c3c8fb8202118c5ff41241cc34cbfb25989bbad226d99b7995"}, + {file = "importlib_resources-6.3.2-py3-none-any.whl", hash = "sha256:f41f4098b16cd140a97d256137cfd943d958219007990b2afb00439fc623f580"}, + {file = "importlib_resources-6.3.2.tar.gz", hash = "sha256:963eb79649252b0160c1afcfe5a1d3fe3ad66edd0a8b114beacffb70c0674223"}, ] [package.dependencies] @@ -3050,13 +3050,13 @@ pbr = "*" [[package]] name = "sentry-sdk" -version = "1.42.0" +version = "1.43.0" description = "Python client for Sentry (https://sentry.io)" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.42.0.tar.gz", hash = "sha256:4a8364b8f7edbf47f95f7163e48334c96100d9c098f0ae6606e2e18183c223e6"}, - {file = "sentry_sdk-1.42.0-py2.py3-none-any.whl", hash = "sha256:a654ee7e497a3f5f6368b36d4f04baeab1fe92b3105f7f6965d6ef0de35a9ba4"}, + {file = "sentry-sdk-1.43.0.tar.gz", hash = "sha256:41df73af89d22921d8733714fb0fc5586c3461907e06688e6537d01a27e0e0f6"}, + {file = "sentry_sdk-1.43.0-py2.py3-none-any.whl", hash = "sha256:8d768724839ca18d7b4c7463ef7528c40b7aa2bfbf7fe554d5f9a7c044acfd36"}, ] [package.dependencies] @@ -3070,6 +3070,7 @@ asyncpg = ["asyncpg (>=0.23)"] beam = ["apache-beam (>=2.12)"] bottle = ["bottle (>=0.12.13)"] celery = ["celery (>=3)"] +celery-redbeat = ["celery-redbeat (>=2)"] chalice = ["chalice (>=1.16.0)"] clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] django = ["django (>=1.8)"] @@ -3570,4 +3571,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0.0" -content-hash = "496e44e1025f23545a301e2fa8e10e376269edf7d773dd2a538be65a065d1c49" +content-hash = "be227f1c6fc44926df2c3570363f653448e2d61416f456c50943ea7729dc7bb8" From fe9c763fac18dda592a17102ba5e9f9bc90392be Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Thu, 21 Mar 2024 01:37:00 +0000 Subject: [PATCH 14/30] add set_attribute in tracer --- .../tracing/provider/otel/otel_tracer.py | 1 + .../tracing/provider/xray/xray_tracer.py | 17 +++---- aws_lambda_powertools/tracing/tracer.py | 45 +++++++++++++++---- examples/tracer/src/sdk_escape_hatch.py | 6 +-- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py index 7280af4fe53..2ca6852e8f3 100644 --- a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py +++ b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py @@ -80,4 +80,5 @@ def patch(self, modules: Sequence[str]) -> None: pass def patch_all(self) -> None: + # OTEL sdk doesn't have patch pass diff --git a/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py index 54afe897f0e..6530eb85623 100644 --- a/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py +++ b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py @@ -2,7 +2,7 @@ from contextlib import asynccontextmanager, contextmanager from numbers import Number -from typing import AsyncGenerator, Generator, Sequence +from typing import Any, AsyncGenerator, Generator, Sequence from ....shared import constants from ....shared.lazy_import import LazyLoader @@ -37,12 +37,8 @@ def __init__(self, xray_recorder=None): if not xray_recorder: from aws_xray_sdk.core import xray_recorder self.recorder = xray_recorder - self.patch = aws_xray_sdk.core.patch - self.patch_all = aws_xray_sdk.core.patch_all self.in_subsegment = self.recorder.in_subsegment self.in_subsegment_async = self.recorder.in_subsegment_async - self.put_annotation = self.recorder.put_annotation - self.put_metadata = self.recorder.put_metadata @contextmanager def trace(self, name: str, **kwargs) -> Generator[XraySpan, None, None]: @@ -60,9 +56,14 @@ def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: else: self.put_annotation(key=key, value=value) + def put_annotation(self, key: str, value: str | Number | bool) -> None: + return self.recorder.put_annotation(key=key, value=value) + + def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: + return self.recorder.put_metadata(key=key, value=value, namespace=namespace) + def patch(self, modules: Sequence[str]) -> None: - # defined in init - pass + return aws_xray_sdk.core.patch(modules) def patch_all(self) -> None: - pass + return aws_xray_sdk.core.patch_all() diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 701d5572132..e718c95507e 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -11,8 +11,8 @@ from aws_lambda_powertools.shared.functions import resolve_env_var_choice, resolve_truthy_env_var_choice from aws_lambda_powertools.shared.lazy_import import LazyLoader from aws_lambda_powertools.shared.types import AnyCallableT -from aws_lambda_powertools.tracing.base import BaseProvider, BaseSegment from aws_lambda_powertools.tracing.provider import XrayProvider +from aws_lambda_powertools.tracing.provider.base import BaseProvider, BaseSpan is_cold_start = True logger = logging.getLogger(__name__) @@ -172,9 +172,17 @@ def __init__( self.patch(modules=patch_modules) if self._is_xray_provider(): - # TO why? self._disable_xray_trace_batching() + def set_attribute(self, key: str, value: Union[str, numbers.Number, bool]): + if self.disabled: + logger.debug("Tracing has been disabled, aborting put_annotation") + return + + logger.debug(f"setting attribute on key '{key}' with '{value}'") + + self.provider.set_attribute(key=key, value=value) + def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]): """Adds annotation to existing segment or subsegment @@ -197,7 +205,11 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]): return logger.debug(f"Annotating on key '{key}' with '{value}'") - self.provider.put_annotation(key=key, value=value) + + if not self._is_custom_provider(): + self.provider.put_annotation(key=key, value=value) # type: ignore + else: + self.provider.set_attribute(key=key, value=value) def put_metadata(self, key: str, value: Any, namespace: Optional[str] = None): """Adds metadata to existing segment or subsegment @@ -225,7 +237,10 @@ def put_metadata(self, key: str, value: Any, namespace: Optional[str] = None): namespace = namespace or self.service logger.debug(f"Adding metadata on key '{key}' with '{value}' at namespace '{namespace}'") - self.provider.put_metadata(key=key, value=value, namespace=namespace) + if not self._is_custom_provider(): + self.provider.put_metadata(key=key, value=value, namespace=namespace) # type: ignore + else: + self.provider.set_attribute(key=f"{namespace}.{key}", value=value) def patch(self, modules: Optional[Sequence[str]] = None): """Patch modules for instrumentation. @@ -700,7 +715,7 @@ def _add_response_as_metadata( self, method_name: Optional[str] = None, data: Optional[Any] = None, - subsegment: Optional[BaseSegment] = None, + subsegment: Optional[BaseSpan] = None, capture_response: Optional[Union[bool, str]] = None, ): """Add response as metadata for given subsegment @@ -718,14 +733,16 @@ def _add_response_as_metadata( """ if data is None or not capture_response or subsegment is None: return - - subsegment.put_metadata(key=f"{method_name} response", value=data, namespace=self.service) + if not self._is_custom_provider(): + subsegment.put_metadata(key=f"{method_name} response", value=data, namespace=self.service) # type: ignore + else: + subsegment.set_attribute(key=f"{method_name} response", value=data, namespace=self.service) def _add_full_exception_as_metadata( self, method_name: str, error: Exception, - subsegment: BaseSegment, + subsegment: BaseSpan, capture_error: Optional[bool] = None, ): """Add full exception object as metadata for given subsegment @@ -744,7 +761,14 @@ def _add_full_exception_as_metadata( if not capture_error: return - subsegment.put_metadata(key=f"{method_name} error", value=error, namespace=self.service) + if not self._is_custom_provider(): + subsegment.put_metadata(key=f"{method_name} error", value=error, namespace=self.service) # type: ignore + else: + subsegment.set_attribute( + key=f"{self.service}.{method_name} error", + value=str(error), + namespace=self.service, + ) @staticmethod def _disable_tracer_provider(): @@ -827,6 +851,9 @@ def _disable_xray_trace_batching(self): def _is_xray_provider(self): return isinstance(self.provider, XrayProvider) + def _is_custom_provider(self): + return not self._is_xray_provider and isinstance(self.provider, BaseProvider) + def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None): """If you want to ignore certain httplib requests you can do so based on the hostname or URL that is being requested. diff --git a/examples/tracer/src/sdk_escape_hatch.py b/examples/tracer/src/sdk_escape_hatch.py index e7024016697..57af1ee72ab 100644 --- a/examples/tracer/src/sdk_escape_hatch.py +++ b/examples/tracer/src/sdk_escape_hatch.py @@ -11,9 +11,9 @@ def collect_payment(charge_id: str) -> str: @tracer.capture_lambda_handler def lambda_handler(event: dict, context: LambdaContext) -> str: charge_id = event.get("charge_id", "") - with tracer.provider.in_subsegment("## collect_payment") as subsegment: - subsegment.put_annotation(key="PaymentId", value=charge_id) + with tracer.provider.trace("## collect_payment") as span: + span.set_attribute(key="PaymentId", value=charge_id) ret = collect_payment(charge_id=charge_id) - subsegment.put_metadata(key="payment_response", value=ret) + span.set_attribute(key="payment_response", value=ret) return ret From d49e1cc6899b66a19c5349ec8ddbf4b1773a13b7 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Thu, 21 Mar 2024 02:07:20 +0000 Subject: [PATCH 15/30] fix context datadog --- .../tracing/provider/datadog/dd_tracer.py | 6 +++--- aws_lambda_powertools/tracing/tracer.py | 12 +++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py index 6773c5febf3..34e1d730842 100644 --- a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py +++ b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py @@ -53,13 +53,13 @@ def trace( span_type: Optional[str] = None, **kwargs, ) -> Generator[DDSpan, None, None]: - dd_span = self.dd_tracer.trace( + with self.dd_tracer.trace( name=name, service=service, resource=resource, span_type=span_type, - ) - yield DDSpan(dd_span=dd_span) + ) as dd_span: + yield DDSpan(dd_span=dd_span) in_subsegment = trace diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index e718c95507e..cb204070027 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -348,13 +348,19 @@ def decorate(event, context, **kwargs): finally: global is_cold_start logger.debug("Annotating cold start") - subsegment.put_annotation(key="ColdStart", value=is_cold_start) + if not self._is_custom_provider(): + subsegment.put_annotation(key="ColdStart", value=is_cold_start) + else: + subsegment.set_attribute(key="ColdStart", value=is_cold_start) if is_cold_start: is_cold_start = False if self.service: - subsegment.put_annotation(key="Service", value=self.service) + if not self._is_custom_provider(): + subsegment.put_annotation(key="Service", value=self.service) + else: + subsegment.set_attribute(key="Service", value=self.service) return response @@ -852,7 +858,7 @@ def _is_xray_provider(self): return isinstance(self.provider, XrayProvider) def _is_custom_provider(self): - return not self._is_xray_provider and isinstance(self.provider, BaseProvider) + return not self._is_xray_provider() and isinstance(self.provider, BaseProvider) def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None): """If you want to ignore certain httplib requests you can do so based on the hostname or URL that is being From 1b28653ff21ba29a985fc8aaac08c3d9b3ca1741 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Thu, 21 Mar 2024 16:26:37 +0000 Subject: [PATCH 16/30] Merging from develop --- poetry.lock | 156 ++++++++++++++++++++++++++-------------------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/poetry.lock b/poetry.lock index 330cef49650..d650ff87e35 100644 --- a/poetry.lock +++ b/poetry.lock @@ -349,17 +349,17 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.34.66" +version = "1.34.67" description = "The AWS SDK for Python" optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "boto3-1.34.66-py3-none-any.whl", hash = "sha256:036989117c0bc4029daaa4cf713c4ff8c227b3eac6ef0e2118eb4098c114080e"}, - {file = "boto3-1.34.66.tar.gz", hash = "sha256:b1d6be3d5833e56198dc635ff4b428b93e5a2a2bd9bc4d94581a572a1ce97cfe"}, + {file = "boto3-1.34.67-py3-none-any.whl", hash = "sha256:473febdf2606cf36f14c470dc3ff1b986efac15f69e37eb0fd728d42749065dd"}, + {file = "boto3-1.34.67.tar.gz", hash = "sha256:950161d438ae1bf31374f04175e5f2624a5de8109674ff80f4de5d962313072a"}, ] [package.dependencies] -botocore = ">=1.34.66,<1.35.0" +botocore = ">=1.34.67,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -368,13 +368,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.66" +version = "1.34.67" description = "Low-level, data-driven core of boto 3." optional = false -python-versions = ">= 3.8" +python-versions = ">=3.8" files = [ - {file = "botocore-1.34.66-py3-none-any.whl", hash = "sha256:92560f8fbdaa9dd221212a3d3a7609219ba0bbf308c13571674c0cda9d8f39e1"}, - {file = "botocore-1.34.66.tar.gz", hash = "sha256:fd7d8742007c220f897cb126b8916ca0cf3724a739d4d716aa5385d7f9d8aeb1"}, + {file = "botocore-1.34.67-py3-none-any.whl", hash = "sha256:56002d7c046ec134811dd079469692ab82919c9840ea4e1c1c373a1d228e37ae"}, + {file = "botocore-1.34.67.tar.gz", hash = "sha256:fc094c055a6ac151820a4d8e28f7b30d03e02695ce180527520a5e219b14e8a1"}, ] [package.dependencies] @@ -429,13 +429,13 @@ ujson = ["ujson (>=5.7.0)"] [[package]] name = "cdk-nag" -version = "2.28.69" +version = "2.28.70" description = "Check CDK v2 applications for best practices using a combination on available rule packs." optional = false python-versions = "~=3.8" files = [ - {file = "cdk-nag-2.28.69.tar.gz", hash = "sha256:7b71070e1f70ba6642a9f03f01dd9d8a603122c41c3e6ddd831cd950bbdfad4a"}, - {file = "cdk_nag-2.28.69-py3-none-any.whl", hash = "sha256:6a4616c0b66a99776a68549ae4f470a6bc7451e6348dd9a25b80f9fe66ff3082"}, + {file = "cdk-nag-2.28.70.tar.gz", hash = "sha256:b617b9d870e706afe469057f574f53d525cd67dc96833f5b285ff556d2992802"}, + {file = "cdk_nag-2.28.70-py3-none-any.whl", hash = "sha256:e4e863cca2aeeba12004632e2b38acc66b9e1116d21ed4b967a5b31775a9773b"}, ] [package.dependencies] @@ -888,71 +888,71 @@ six = "*" [[package]] name = "ddtrace" -version = "2.7.3" +version = "2.7.4" description = "Datadog APM client library" optional = false python-versions = ">=3.7" files = [ - {file = "ddtrace-2.7.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:7cf0ab04549f467997e208f7628048c103cd03e8d304054e22c64fac9881ba85"}, - {file = "ddtrace-2.7.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:61fe0379f5ddac692536cd9b5ed71e4f1b0377495c8c437fcb1d850cfb8421de"}, - {file = "ddtrace-2.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d58f6be5f38002f4d804030497e03657fe656dc199015671a90aaa09c82d6070"}, - {file = "ddtrace-2.7.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:181bf1d9729c986a42cd2e529b8e9651449445d0617b123a21207e334d1c179a"}, - {file = "ddtrace-2.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1edd7098e73f719a2439ac25906577c49173f40b988122ce467abbd5c76a876e"}, - {file = "ddtrace-2.7.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e0c0109868b4f1ffe04201c192a5f16b7996e7627685f86585cb36cd9b1732b6"}, - {file = "ddtrace-2.7.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fae9a893379fb81e6357dedd7a74401c4f933e8f02271e83c76ff5ae08ade813"}, - {file = "ddtrace-2.7.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:94e7bf7a57526260ff5cc176ff6be7423e38e255305b211f5efacda75d00a0f7"}, - {file = "ddtrace-2.7.3-cp310-cp310-win32.whl", hash = "sha256:94802877b996af8128f21f5c682fe8d825aa650203fb5518b0fae351fae80b6c"}, - {file = "ddtrace-2.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:8ded033e6b0860d96d7dc99cb2d38798ded30358897e172d868377132b74ed2f"}, - {file = "ddtrace-2.7.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:17f6de9b996de2846a7097506795634695b1764dce239e9afa39884771a41c90"}, - {file = "ddtrace-2.7.3-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:7cc19d8996fc3c77bc83ca47469ec2dae3166948384583a49890a10c59f1ce82"}, - {file = "ddtrace-2.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb06a681cbcb4cdedc687113092d3616c56786e22253fac58921743d71dbb5f"}, - {file = "ddtrace-2.7.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87b0c3f6a4ec4c940d9ce3417962d52303629d4d73114c0cd9ab784bf5269834"}, - {file = "ddtrace-2.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2f186c5aa4031fa5ab85043b0a0b04ebc0d2d79539b19a36fdb8f500be06552"}, - {file = "ddtrace-2.7.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:df3c21eae7b1a18a52aecb4a4c48888a56e5b5fb51a893e5f02e1d6ee372e585"}, - {file = "ddtrace-2.7.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:81467308e4518038d161344ca27b16e78a147e9ab9bb0a5b9785781ec95100bb"}, - {file = "ddtrace-2.7.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eecf0f3c87dd2a882f3d1b21c717290551d54fd94bf128123dabdfd624e7ba2d"}, - {file = "ddtrace-2.7.3-cp311-cp311-win32.whl", hash = "sha256:d14e34a82135fe09c1457456e8b62d35bbfcf6f086bf7651380ebf570acf8bdb"}, - {file = "ddtrace-2.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:8f24649745767cf4f3dbe19e04bd89139a6a91d97283b65219b28f7814fec467"}, - {file = "ddtrace-2.7.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:c16e8b2f94c9bbd86a0d4d92e7fa54df7fa3c990cbd89d8445a9dcadee518875"}, - {file = "ddtrace-2.7.3-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:8f0d72bff7f6f774b365592dd654a5146328a72b1ffdd83b4c5b996e889d0ae6"}, - {file = "ddtrace-2.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce724577c41c79ca8898fdb2a188697f7526485b871fa712d5290cb9ba69fb6d"}, - {file = "ddtrace-2.7.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:20214ad0df36ddb7a516b440c88642d3dc8bb0e3eb669705ab039eceb12183fe"}, - {file = "ddtrace-2.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65a81942b0b3746a323ef0e042711eb013ee7bc8e694489c88d62170f1044fa8"}, - {file = "ddtrace-2.7.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5c7192382af94a720b8412486ca5d6796478cf892d3c004be168e0ff4cb7be08"}, - {file = "ddtrace-2.7.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2167f70df9add00f61f9da6d14abd6b6adb7425f974fb89c34ea7fe55d0edeb3"}, - {file = "ddtrace-2.7.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a27f09aa6caddf9f7b2ea4235cf8b26cd18d835db4de452549cb7f04ceab8f57"}, - {file = "ddtrace-2.7.3-cp312-cp312-win32.whl", hash = "sha256:773a2156ebade5c73f3dc6d4f8dfc1509e5c2b82501126d1313f55da912c7473"}, - {file = "ddtrace-2.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:c8699fc29e1a6413400e15c1b3178414ee54ff6dc83b787e82bb4229ebfe97b3"}, - {file = "ddtrace-2.7.3-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:4d83c5b9bbf5d1861e9bb90c0860e3df717ba09179ed116d476c6d8fdcea0f2b"}, - {file = "ddtrace-2.7.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:284f28d56ecd3f280a73782115cc5d5e141d4c4d6bf3ffca22317ea2fe845f91"}, - {file = "ddtrace-2.7.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:29d74998b0ed24336274dbdc1f895345e3e84b352c125282cb9ef3965d6512d1"}, - {file = "ddtrace-2.7.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55aa867e180cc5c19154f9efa0ac9157d49afde2bee883c59dab7b83ef2dd131"}, - {file = "ddtrace-2.7.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:87c2dc92324c6068339162616f318030b345d07e8aeabd1724eec91a1d9fe662"}, - {file = "ddtrace-2.7.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c9cccf5f367f47b5b7e1be883852cc8940d87a4c8eb926371333fa5582f243ca"}, - {file = "ddtrace-2.7.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ecd0078a1b1acebd65628113c339efa0889348fae93cda8100aea4b0f3ed157b"}, - {file = "ddtrace-2.7.3-cp37-cp37m-win32.whl", hash = "sha256:48fba020dca6fb64103f8750e2940f262bc27ae717a5510113509354577cacdb"}, - {file = "ddtrace-2.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:63bb5d11b1b09f865f69103c123eb7ec71af148ab33202388e078d5c2fc8b693"}, - {file = "ddtrace-2.7.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:dd6824f9e39a3646c447bb51a7447967dfa19e8d396be2a75c503ac13e7be256"}, - {file = "ddtrace-2.7.3-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:f90d055b48452e40365abbd8e9f184fe6d4151783fa2cba0c321d24cf9d23e24"}, - {file = "ddtrace-2.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a376809d875e430dc6a569dd5b165c18514eb4dd21757a167759d88a73f63388"}, - {file = "ddtrace-2.7.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a301e5735cb694f887f7c1a8186fdd35377cf63bc93daf8e6226bd024bcf282e"}, - {file = "ddtrace-2.7.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:260acffae66cd09a57770dc9db2359de54a72fa0423e5e45dddd0241eb19f711"}, - {file = "ddtrace-2.7.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a301d87b4228b8482fdfd1e24e817c14d7f4cc8006249b15d172818aefe4a7dd"}, - {file = "ddtrace-2.7.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:010bd1c8699749917808a73b935a9503c68a3aaf76f65f421ff7c50ac3a57cbf"}, - {file = "ddtrace-2.7.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0c07ab74aa1f14ed430b5aa67a4f928261b4091ba1a573b7455fbcde39f71231"}, - {file = "ddtrace-2.7.3-cp38-cp38-win32.whl", hash = "sha256:3ab401eb59fa8b29c7333b1c45a96e80ee7158e44ec492a4623a8835e4a03bc1"}, - {file = "ddtrace-2.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:e7b1666da41ef3b5083cf900b5737fa1fcd4c93fa2e3735b6758552c8498d9f8"}, - {file = "ddtrace-2.7.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:1423d47ff6ad401e3fefc69177ba7bb1c759636a33997290fa6d4f764a6f1fde"}, - {file = "ddtrace-2.7.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:3e64e09b778f3bc4a9bc2ca54d6d131d99b64375da7fe389a83c98d249a9cf23"}, - {file = "ddtrace-2.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18be89cbc3d50f803cb376b180e41eaf052c5b357682a1b6ee8755875da77d33"}, - {file = "ddtrace-2.7.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4de46b58177c736a372622109125908e115655166d4a254c52412c579a838571"}, - {file = "ddtrace-2.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093f7b97361764cc0ba013e87fb351ec1483c2fcf91678e4f97b486a445a2551"}, - {file = "ddtrace-2.7.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7d49db42db227e9c33df2c81fb33e5004bdf97c8e0cbc82b3cae58863b02c932"}, - {file = "ddtrace-2.7.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e4d6c3fd81465f31b5465341c89bc5313e547c7a30e70d46e3be73079961ca34"}, - {file = "ddtrace-2.7.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:abaca327d20a2433b1378b1a665230876f301ea8a0bc94055ff46bd362d772cc"}, - {file = "ddtrace-2.7.3-cp39-cp39-win32.whl", hash = "sha256:16d1d6cb4c9667cfe1f4d01b99bb52f96a53bf930701c4b388e83bb8ba81fd40"}, - {file = "ddtrace-2.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:dd29f6c385492af55918fa3b86ae2752dc259030badffbff31c2a740d5c1a53c"}, - {file = "ddtrace-2.7.3.tar.gz", hash = "sha256:0fac852dc84902a5e7528ebbb17907a41b674cbcff8884321a911f2dacbac5bd"}, + {file = "ddtrace-2.7.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:d9ce0875e0427d11dfa21327f6bb675f197c8b17d7ee32e608c2c5c6c1ac6ee0"}, + {file = "ddtrace-2.7.4-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:9a22bbbae6f6024db161aee4ab321090819b9b85b7714bf0795b51d3ad4a9adc"}, + {file = "ddtrace-2.7.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5866a718016efa6ad853ee708fcd8c5abe72328ffaf5116f567ae02ad385ba"}, + {file = "ddtrace-2.7.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:39a049e82017682c13f219f82c701eab5edf194b61b428a6c0b55e901304a892"}, + {file = "ddtrace-2.7.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9816573b8c5bffc271fc20c1b5e2913ea207984e6853f0de27e5f6eb87f31f7"}, + {file = "ddtrace-2.7.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:39b0ca5541bf07de35b9a18c1a730b6924c479a3eb7812cf530639b8260bfbc9"}, + {file = "ddtrace-2.7.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5550be06b32ee281ceb7f25af2e525f2c39bf63a0ff24b47ad861d7a62d7b60f"}, + {file = "ddtrace-2.7.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d4caf67a0c9bf98e259c42ced503e6b785806259d34d559838cda7fffb64d79a"}, + {file = "ddtrace-2.7.4-cp310-cp310-win32.whl", hash = "sha256:b5474fa7a2f6173c229c5dd103d8b255917c999613f4e491aa272e8ce0f2c7db"}, + {file = "ddtrace-2.7.4-cp310-cp310-win_amd64.whl", hash = "sha256:5f500e404dd9f55a739291516ad5e6c9836d672b6024ff5def58c37434838c29"}, + {file = "ddtrace-2.7.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:f209baee9ecbe96ed7acbfda6d861dbcc868f28fbbcc9ee8e050b42171b523c7"}, + {file = "ddtrace-2.7.4-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:a2f51a029435767561f27133af7c86f837d4064e4ae20c9adb5b5b2fea4d5304"}, + {file = "ddtrace-2.7.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eab77f9938f711c87494ca4430b46260ab619d413a961a98fc38b8af6fdf4679"}, + {file = "ddtrace-2.7.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ff933d2d6f031416d6b10c942253f64ea39eaf2e2a2cba32fd23dca0a1329c1"}, + {file = "ddtrace-2.7.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0e1eb3d08f39bf67d4880f646d3f1a7b4668d9c9c0c3792638f87dc19180240"}, + {file = "ddtrace-2.7.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9d644d6f1739cad3f72c24b5ce3f2a99f0f16dd8dd30e15e89bc44674f0635c3"}, + {file = "ddtrace-2.7.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0a6c4589d97e68c97b0c5d29719cffbd4d72439bcbbab25edd3784596845976a"}, + {file = "ddtrace-2.7.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b635237a70e77f50e7d9b1896d85cf5378a29283522acbd1ea3c22769721d128"}, + {file = "ddtrace-2.7.4-cp311-cp311-win32.whl", hash = "sha256:d9aa5ceba995385af11de2c84d9e30bb878bc2d3b6eb2b90531ef82a7303c8f5"}, + {file = "ddtrace-2.7.4-cp311-cp311-win_amd64.whl", hash = "sha256:305ccd7ba020c0eb581372bc60db5fb48b64d699cacdebae73b574db44db3b5a"}, + {file = "ddtrace-2.7.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:c18a685ca88243ff6a17031d785d8dceee8f19f53a59591f06fccf2d778ebfc6"}, + {file = "ddtrace-2.7.4-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:6be55e9cff512e96e6dc672ccfba2f18567e20951242222d962ab44a6c49f395"}, + {file = "ddtrace-2.7.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ca99bca19bfab5debd1a60765943d93987a6aa3c7f516c4a3001e8d41bbc36a"}, + {file = "ddtrace-2.7.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0253964faa8ae44a2023ffc845ddb500ad8d0e947f8faf7a78f0e16e0e5cda57"}, + {file = "ddtrace-2.7.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc8c267265fe5642efdb3f47d82990fc1749b348ec07186a9dd70ae11812eab2"}, + {file = "ddtrace-2.7.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:25936792cbbe43b7ae8dca4fe90514ce557f6a63fac1fd8ff2c850bea6ebd628"}, + {file = "ddtrace-2.7.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2013961b257eb7386904b66392414593916069dcec7f59c44cc7b5bec3d6eb40"}, + {file = "ddtrace-2.7.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b81589f548b799f9313aa63cf096b480384dd037d912797ef0595ce5d78b341e"}, + {file = "ddtrace-2.7.4-cp312-cp312-win32.whl", hash = "sha256:82d557683dcd48c34661cafb982ec769e53de2bd3a23cba3cd559f7f46c4ef89"}, + {file = "ddtrace-2.7.4-cp312-cp312-win_amd64.whl", hash = "sha256:43e58bdcec4ab37712bd88b0f0bf0c254c18a800664aa63509c133868646bad2"}, + {file = "ddtrace-2.7.4-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:1b0ae712deaa21d9d116e38010e2188b5bee30424492fa529569ced99aa4c4bd"}, + {file = "ddtrace-2.7.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f344e7c2d12161d2be7fe4973a2060e1218e2f725f312d9d778e3001df263a65"}, + {file = "ddtrace-2.7.4-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f1cc93867c7a58e796ae08466cf08d866086901b09dacb014dcdd6ce53d77a3"}, + {file = "ddtrace-2.7.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832a790b632dd2aa39f06d2c141f85bc792ef32ed96d794947a04256608f21fb"}, + {file = "ddtrace-2.7.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:eb5ba6d1061aed9706db7c796755c582e98d32fbca858d55ded4b65b842bc2db"}, + {file = "ddtrace-2.7.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:957bf17eca2b7902d169f3ff75ea09275ab19f21eb79a90fe23f91950dfae211"}, + {file = "ddtrace-2.7.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d5ea49d1fd4b647005af65564ab6720b3ec31174f073a4d0f233a2c86001a9b7"}, + {file = "ddtrace-2.7.4-cp37-cp37m-win32.whl", hash = "sha256:f225c68717c1b98ac0fc22a5d7c746daefb5c6f385891db9d8d7e3203a673130"}, + {file = "ddtrace-2.7.4-cp37-cp37m-win_amd64.whl", hash = "sha256:4aaf2a606ea837059978ddf082f9ac9c3f9f8449678c1ec15c1b66bf0ac02e9c"}, + {file = "ddtrace-2.7.4-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:2bb2cf976ae993d73235f1c79f9e9fb6aeb42bb987e664a31b1c3d98dcfd963a"}, + {file = "ddtrace-2.7.4-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:69d784740da1bae5069e8be897ca048a2fc2d8368a92084d83207a78c46299e8"}, + {file = "ddtrace-2.7.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a82947e19319a220933c4ee28fa4c8b589dedce94d257cb872f9b48946680"}, + {file = "ddtrace-2.7.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f51a55b8626c11639336471be0931dff2b3cb6c66ea5e39baf1175dae8f4ab75"}, + {file = "ddtrace-2.7.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:080dab9baca5f101299b0e1ea14475a452a4263dc83f2918cd79be639a5855ba"}, + {file = "ddtrace-2.7.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bd492f0e93f78d65bef73666a61fd836db1dc438c2569fa892efb8b2fabf40e4"}, + {file = "ddtrace-2.7.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:db480780761bc9a8cd854dae00555e9dde30c82ad7174b122aa87f6d7b749452"}, + {file = "ddtrace-2.7.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1b597a0d32c11e9d3971b22f0328c94c13688f122bea3d508c804b1a4f830255"}, + {file = "ddtrace-2.7.4-cp38-cp38-win32.whl", hash = "sha256:ab7af52ce526fd4d5117ba94007de1ad52c1d7e39b451945a633cae8e540a9c9"}, + {file = "ddtrace-2.7.4-cp38-cp38-win_amd64.whl", hash = "sha256:4126ef09ca85672d3e9b3c8f289aef3789fd23e70bd4c7d13f91c5cdbd9bd2f4"}, + {file = "ddtrace-2.7.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:90bf4e4b8182cbfc8f0a860c3bf06e598aceaeca3b13da71866a012bb4c8d60c"}, + {file = "ddtrace-2.7.4-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:c29292228270e1663abfa7cb6df94151361b12c4f6030cd4c541834d1be91e55"}, + {file = "ddtrace-2.7.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95c6bcc2501bc5834a0ea1d31e264fd3e9f688e201e366d52bc50622d2c3635f"}, + {file = "ddtrace-2.7.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226889e0e62791611ca658a847153986d77787795ebf2a0438ec9e95a7a49ca7"}, + {file = "ddtrace-2.7.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1559ac67db8714edd0c3685c34537b7490ba96a86237fba30cf729f96f255059"}, + {file = "ddtrace-2.7.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:982c97e1abd97e446deb187c40d64adec65796ec10bee0298a73535beb036964"}, + {file = "ddtrace-2.7.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3b222b21c967e79ae7b16d5e1e28142c137fc6b90f89a5b816d60478e88e0cfc"}, + {file = "ddtrace-2.7.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f607404ad5e806426964fa9fd2a2d3818813193b139406b0d587b17374a339a9"}, + {file = "ddtrace-2.7.4-cp39-cp39-win32.whl", hash = "sha256:f144fababdf082e413c3fc6db89fd819b10b8732b05fb2210866df68f98ec0d0"}, + {file = "ddtrace-2.7.4-cp39-cp39-win_amd64.whl", hash = "sha256:a0e29a91b2a485195e3decb77e1603c75c5efa632f7e41dc2452e701a15807bf"}, + {file = "ddtrace-2.7.4.tar.gz", hash = "sha256:67f9d0c0dd306f28eed2be1bcc45748b7f6b093c26525d422c7abf19a4c1669e"}, ] [package.dependencies] @@ -1377,13 +1377,13 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs [[package]] name = "importlib-resources" -version = "6.3.2" +version = "6.4.0" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.3.2-py3-none-any.whl", hash = "sha256:f41f4098b16cd140a97d256137cfd943d958219007990b2afb00439fc623f580"}, - {file = "importlib_resources-6.3.2.tar.gz", hash = "sha256:963eb79649252b0160c1afcfe5a1d3fe3ad66edd0a8b114beacffb70c0674223"}, + {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, + {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, ] [package.dependencies] @@ -1391,7 +1391,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["jaraco.collections", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -3571,4 +3571,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0.0" -content-hash = "be227f1c6fc44926df2c3570363f653448e2d61416f456c50943ea7729dc7bb8" +content-hash = "6a800d710d9daf5932230547957f91f109e45003504da9bae232860e77327d4c" From b1a2b34e612da7bbb6954265a91511381f5c2abd Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Mon, 25 Mar 2024 22:39:08 +0000 Subject: [PATCH 17/30] fix docstring, add conversion in otel --- .../tracing/provider/base.py | 19 +++---- .../tracing/provider/datadog/dd_tracer.py | 20 ++----- .../tracing/provider/otel/otel_tracer.py | 8 +-- aws_lambda_powertools/tracing/tracer.py | 55 ++++++++++++------- 4 files changed, 50 insertions(+), 52 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/base.py b/aws_lambda_powertools/tracing/provider/base.py index e5953459cf6..069604909ca 100644 --- a/aws_lambda_powertools/tracing/provider/base.py +++ b/aws_lambda_powertools/tracing/provider/base.py @@ -4,22 +4,19 @@ from typing import AsyncGenerator, Generator, Sequence, Union -## TO-Discuss how to refactor this one. Segment seems X-ray exclusive concept class BaseSpan(abc.ABC): - """Holds common properties and methods on segment and subsegment.""" + """Holds common properties and methods on span.""" @abc.abstractmethod def set_attribute(self, key: str, value: Union[str, numbers.Number, bool], **kwargs) -> None: - """Annotate segment or subsegment with a key-value pair. - - Note: Annotations will be indexed for later search query. + """set attribute for span with a key-value pair. Parameters ---------- key: str - Metadata key + Attribute key value: Union[str, numbers.Number, bool] - Annotation value + Attribute value """ @abc.abstractmethod @@ -63,16 +60,14 @@ def trace_async(self, name: str, **kwargs) -> AsyncGenerator[BaseSpan, None]: @abc.abstractmethod def set_attribute(self, key: str, value: Union[str, numbers.Number, bool], **kwargs) -> None: - """Annotate current active trace entity with a key-value pair. - - Note: Annotations will be indexed for later search query. + """set attribute on current active trace entity with a key-value pair. Parameters ---------- key: str - Metadata key + attribute key value: Union[str, numbers.Number, bool] - Annotation value + attribute value """ @abc.abstractmethod diff --git a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py index 34e1d730842..a473be94ff6 100644 --- a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py +++ b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py @@ -27,18 +27,6 @@ def record_exception(self, exception: BaseException, attributes: Optional[Dict] _attributes = kwargs self.dd_span.set_tags(tags=attributes) - def __enter__(self): - print("entered") - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - try: - if exc_type: - self.dd_span.set_exc_info(exc_type, exc_val, exc_tb) - self.dd_span.finish() - except Exception as e: - logger.exception(f"error closing trace {e}") - class DDTraceProvider(BaseProvider): def __init__(self, dd_tracer: ddtrace.Tracer): @@ -72,19 +60,19 @@ async def trace_async( span_type: Optional[str] = None, **kwargs, ) -> AsyncGenerator[DDSpan, None]: - dd_span = self.dd_tracer.trace( + with self.dd_tracer.trace( name=name, service=service, resource=resource, span_type=span_type, - ) - yield DDSpan(dd_span=dd_span) + ) as dd_span: + yield DDSpan(dd_span=dd_span) def set_attribute(self, key: str | bytes, value: Any, **kwargs: Any) -> None: span = self.dd_tracer.context_provider.active() + # ignore if no active span if isinstance(span, ddtrace.Span): span.set_tag(key=key, value=value) - # ignore if no active span def patch(self, modules: Sequence[str]) -> None: module_to_patch = {} diff --git a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py index 2ca6852e8f3..83758525a28 100644 --- a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py +++ b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py @@ -11,16 +11,14 @@ logger = logging.getLogger(__name__) -# optl terminology first -# 1. Provider based on OTel terminology -# 2. X-Ray provider on top of the new BaseProvider -# 3. Datadog provider on top of the new BaseProvider -# access xray sdk class OtelSpan(BaseSpan): def __init__(self, otel_span=otel_trace.Span): self.otel_span = otel_span def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: # type: ignore[override] + if not isinstance(value, (str, bool, int, float)): + # convert value to str if value is not a supported structur + value = str(value) self.otel_span.set_attribute(key=key, value=value) def record_exception( diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index cb204070027..af6ba85e667 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -175,8 +175,24 @@ def __init__( self._disable_xray_trace_batching() def set_attribute(self, key: str, value: Union[str, numbers.Number, bool]): + """Set attribute on current active trace entity with a key-value pair. + + Parameters + ---------- + key : str + attribute key + value : Union[str, numbers.Number, bool] + Value for attribute + + Example + ------- + Set attribute for a pseudo service named payment + + tracer = Tracer(service="payment") + tracer.set_attribute("PaymentStatus", "CONFIRMED") + """ if self.disabled: - logger.debug("Tracing has been disabled, aborting put_annotation") + logger.debug("Tracing has been disabled, aborting set_attribute") return logger.debug(f"setting attribute on key '{key}' with '{value}'") @@ -206,10 +222,10 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]): logger.debug(f"Annotating on key '{key}' with '{value}'") - if not self._is_custom_provider(): - self.provider.put_annotation(key=key, value=value) # type: ignore - else: + if self._is_custom_provider(): self.provider.set_attribute(key=key, value=value) + else: + self.provider.put_annotation(key=key, value=value) # type: ignore def put_metadata(self, key: str, value: Any, namespace: Optional[str] = None): """Adds metadata to existing segment or subsegment @@ -237,10 +253,10 @@ def put_metadata(self, key: str, value: Any, namespace: Optional[str] = None): namespace = namespace or self.service logger.debug(f"Adding metadata on key '{key}' with '{value}' at namespace '{namespace}'") - if not self._is_custom_provider(): - self.provider.put_metadata(key=key, value=value, namespace=namespace) # type: ignore - else: + if self._is_custom_provider(): self.provider.set_attribute(key=f"{namespace}.{key}", value=value) + else: + self.provider.put_metadata(key=key, value=value, namespace=namespace) # type: ignore def patch(self, modules: Optional[Sequence[str]] = None): """Patch modules for instrumentation. @@ -348,19 +364,19 @@ def decorate(event, context, **kwargs): finally: global is_cold_start logger.debug("Annotating cold start") - if not self._is_custom_provider(): - subsegment.put_annotation(key="ColdStart", value=is_cold_start) - else: + if self._is_custom_provider(): subsegment.set_attribute(key="ColdStart", value=is_cold_start) + else: + subsegment.put_annotation(key="ColdStart", value=is_cold_start) if is_cold_start: is_cold_start = False if self.service: - if not self._is_custom_provider(): - subsegment.put_annotation(key="Service", value=self.service) - else: + if self._is_custom_provider(): subsegment.set_attribute(key="Service", value=self.service) + else: + subsegment.put_annotation(key="Service", value=self.service) return response @@ -739,10 +755,10 @@ def _add_response_as_metadata( """ if data is None or not capture_response or subsegment is None: return - if not self._is_custom_provider(): - subsegment.put_metadata(key=f"{method_name} response", value=data, namespace=self.service) # type: ignore - else: + if self._is_custom_provider(): subsegment.set_attribute(key=f"{method_name} response", value=data, namespace=self.service) + else: + subsegment.put_metadata(key=f"{method_name} response", value=data, namespace=self.service) # type: ignore def _add_full_exception_as_metadata( self, @@ -767,14 +783,14 @@ def _add_full_exception_as_metadata( if not capture_error: return - if not self._is_custom_provider(): - subsegment.put_metadata(key=f"{method_name} error", value=error, namespace=self.service) # type: ignore - else: + if self._is_custom_provider(): subsegment.set_attribute( key=f"{self.service}.{method_name} error", value=str(error), namespace=self.service, ) + else: + subsegment.put_metadata(key=f"{method_name} error", value=error, namespace=self.service) # type: ignore @staticmethod def _disable_tracer_provider(): @@ -858,6 +874,7 @@ def _is_xray_provider(self): return isinstance(self.provider, XrayProvider) def _is_custom_provider(self): + # check if provider is not default xray_provider. Avoid test conflits return not self._is_xray_provider() and isinstance(self.provider, BaseProvider) def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None): From ed1059be64bcb60a6295e31cff465efdb6252fdc Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Mon, 25 Mar 2024 22:44:00 +0000 Subject: [PATCH 18/30] fix typo --- aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py index a473be94ff6..3daffdc567a 100644 --- a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py +++ b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py @@ -25,7 +25,7 @@ def record_exception(self, exception: BaseException, attributes: Optional[Dict] kwargs.update(_attributes) # attribute should overwrite kwargs _attributes = kwargs - self.dd_span.set_tags(tags=attributes) + self.dd_span.set_tags(tags=_attributes) class DDTraceProvider(BaseProvider): From 4639bd4a41edb40b1bf51e8690992b13d2c7c17f Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Mon, 25 Mar 2024 22:45:15 +0000 Subject: [PATCH 19/30] add in_subsegment_async in dd --- aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py index 3daffdc567a..8d0fbecb7ee 100644 --- a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py +++ b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py @@ -68,6 +68,8 @@ async def trace_async( ) as dd_span: yield DDSpan(dd_span=dd_span) + in_subsegment_async = trace_async + def set_attribute(self, key: str | bytes, value: Any, **kwargs: Any) -> None: span = self.dd_tracer.context_provider.active() # ignore if no active span From 012f8a8efed6f5f476d04b43f231a175ab2e4d00 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 26 Mar 2024 15:46:02 +0000 Subject: [PATCH 20/30] remove discuss --- aws_lambda_powertools/tracing/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws_lambda_powertools/tracing/base.py b/aws_lambda_powertools/tracing/base.py index 3b71b6d5fbd..383014b147d 100644 --- a/aws_lambda_powertools/tracing/base.py +++ b/aws_lambda_powertools/tracing/base.py @@ -5,7 +5,7 @@ from typing import Any, Generator, List, Optional, Sequence, Union -## TO-Discuss how to refactor this one. Segment seems X-ray exclusive concept +## this class is deprecated, keep this class for backwards compatibility class BaseSegment(abc.ABC): """Holds common properties and methods on segment and subsegment.""" From f0561cc078b6e4656e3df007c2ff4bb833e69f20 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Tue, 26 Mar 2024 15:50:26 +0000 Subject: [PATCH 21/30] fix poetry --- poetry.lock | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/poetry.lock b/poetry.lock index de074b1486b..be825f2d5e6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -349,17 +349,17 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.34.67" +version = "1.34.70" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.67-py3-none-any.whl", hash = "sha256:473febdf2606cf36f14c470dc3ff1b986efac15f69e37eb0fd728d42749065dd"}, - {file = "boto3-1.34.67.tar.gz", hash = "sha256:950161d438ae1bf31374f04175e5f2624a5de8109674ff80f4de5d962313072a"}, + {file = "boto3-1.34.70-py3-none-any.whl", hash = "sha256:8d7902e2c0c62837457ba18146e3feaf1dec62018617edc5c0336b65b305b682"}, + {file = "boto3-1.34.70.tar.gz", hash = "sha256:54150a52eb93028b8e09df00319e8dcb68be7459333d5da00d706d75ba5130d6"}, ] [package.dependencies] -botocore = ">=1.34.67,<1.35.0" +botocore = ">=1.34.70,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -368,13 +368,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.67" +version = "1.34.70" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.67-py3-none-any.whl", hash = "sha256:56002d7c046ec134811dd079469692ab82919c9840ea4e1c1c373a1d228e37ae"}, - {file = "botocore-1.34.67.tar.gz", hash = "sha256:fc094c055a6ac151820a4d8e28f7b30d03e02695ce180527520a5e219b14e8a1"}, + {file = "botocore-1.34.70-py3-none-any.whl", hash = "sha256:c86944114e85c8a8d5da06fb84f2609ed3bd23cd2fc06b30250bef7e37e8c589"}, + {file = "botocore-1.34.70.tar.gz", hash = "sha256:fa03d4972cd57d505e6c0eb5d7c7a1caeb7dd49e84f963f7ebeca41fe8ab736e"}, ] [package.dependencies] @@ -429,13 +429,13 @@ ujson = ["ujson (>=5.7.0)"] [[package]] name = "cdk-nag" -version = "2.28.70" +version = "2.28.73" description = "Check CDK v2 applications for best practices using a combination on available rule packs." optional = false python-versions = "~=3.8" files = [ - {file = "cdk-nag-2.28.70.tar.gz", hash = "sha256:b617b9d870e706afe469057f574f53d525cd67dc96833f5b285ff556d2992802"}, - {file = "cdk_nag-2.28.70-py3-none-any.whl", hash = "sha256:e4e863cca2aeeba12004632e2b38acc66b9e1116d21ed4b967a5b31775a9773b"}, + {file = "cdk-nag-2.28.73.tar.gz", hash = "sha256:c2d6b931182a54228d00db399b5fea40ff5d8c42e8c31ef8f05e5715eed7fbd7"}, + {file = "cdk_nag-2.28.73-py3-none-any.whl", hash = "sha256:f5ce8889eebe8e187c52c89c33cc65094cbd5f62f76ea0469ed9a2f5cda08f39"}, ] [package.dependencies] @@ -447,13 +447,13 @@ typeguard = ">=2.13.3,<2.14.0" [[package]] name = "cdklabs-generative-ai-cdk-constructs" -version = "0.1.101" +version = "0.1.104" description = "AWS Generative AI CDK Constructs is a library for well-architected generative AI patterns." optional = false python-versions = "~=3.8" files = [ - {file = "cdklabs.generative-ai-cdk-constructs-0.1.101.tar.gz", hash = "sha256:13911b9fa8e648168df55f6dc2739907f926bc6b194523afa7f95a133daadb6b"}, - {file = "cdklabs.generative_ai_cdk_constructs-0.1.101-py3-none-any.whl", hash = "sha256:ce0eaf206c8d968253ec21b6c4fbc28ac84da10f7647632c18be319eb5843f1e"}, + {file = "cdklabs.generative-ai-cdk-constructs-0.1.104.tar.gz", hash = "sha256:69f0b6e1fcf53f8f509d2ce2d60a18bd6b07114b07c519ab0189d3f1902b3026"}, + {file = "cdklabs.generative_ai_cdk_constructs-0.1.104-py3-none-any.whl", hash = "sha256:6fb3862d3050be498ba3a03b0d65e1ee0df0b91b7150fc52f26d4bdde1f93b56"}, ] [package.dependencies] @@ -3571,5 +3571,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0.0" -content-hash = "6a800d710d9daf5932230547957f91f109e45003504da9bae232860e77327d4c" - +content-hash = "ead51f5d07778cb18aee659e19bc803ea1b3054d308365cc25b5b47ab8f5e413" From 844d85b96b443068217bda9a2e2afd47bc12e0bf Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Wed, 10 Apr 2024 23:35:19 +0000 Subject: [PATCH 22/30] address ruben comments --- .../tracing/provider/__init__.py | 5 -- .../tracing/provider/base.py | 61 +++++++++++++------ .../tracing/provider/otel/otel_tracer.py | 4 +- .../tracing/provider/xray/xray_tracer.py | 20 +++--- aws_lambda_powertools/tracing/tracer.py | 13 ++-- 5 files changed, 66 insertions(+), 37 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/__init__.py b/aws_lambda_powertools/tracing/provider/__init__.py index fbcd106d742..e69de29bb2d 100644 --- a/aws_lambda_powertools/tracing/provider/__init__.py +++ b/aws_lambda_powertools/tracing/provider/__init__.py @@ -1,5 +0,0 @@ -from .datadog.dd_tracer import DDTraceProvider -from .otel.otel_tracer import OtelProvider -from .xray.xray_tracer import XrayProvider - -__all__ = ["DDTraceProvider", "OtelProvider", "XrayProvider"] diff --git a/aws_lambda_powertools/tracing/provider/base.py b/aws_lambda_powertools/tracing/provider/base.py index 069604909ca..977c9b85cf7 100644 --- a/aws_lambda_powertools/tracing/provider/base.py +++ b/aws_lambda_powertools/tracing/provider/base.py @@ -1,78 +1,105 @@ import abc -import numbers from contextlib import asynccontextmanager, contextmanager -from typing import AsyncGenerator, Generator, Sequence, Union +from typing import Any, AsyncGenerator, Generator, Sequence class BaseSpan(abc.ABC): - """Holds common properties and methods on span.""" + """A span represents a unit of work or operation within a trace. + Spans are the building blocks of Traces.""" @abc.abstractmethod - def set_attribute(self, key: str, value: Union[str, numbers.Number, bool], **kwargs) -> None: - """set attribute for span with a key-value pair. + def set_attribute(self, key: str, value: Any, **kwargs) -> None: + """Set an attribute for a span with a key-value pair. Parameters ---------- key: str Attribute key - value: Union[str, numbers.Number, bool] + value: Any Attribute value + kwargs: Optional[dict] + Optional parameters """ @abc.abstractmethod def record_exception(self, exception: BaseException, **kwargs): - """Add an exception to trace entities. + """Records an exception to this Span. Parameters ---------- exception: Exception - Caught exception - Output from `traceback.extract_stack()`. + Caught exception during the exectution of this Span + kwargs: Optional[dict] + Optional parameters """ class BaseProvider(abc.ABC): + """BaseProvider is an abstract base class that defines the expected behavior for tracing providers + used by Tracer. Inheriting classes must implement this interface to be compatible with Tracer. + """ + @abc.abstractmethod @contextmanager def trace(self, name: str, **kwargs) -> Generator[BaseSpan, None, None]: - """Return a span context manger. + """Context manager for creating a new span and set it + as the current span in this tracer's context. + + Exiting the context manager will call the span's end method, + as well as return the current span to its previous value by + returning to the previous context. Parameters ---------- name: str Span name kwargs: Optional[dict] - Optional parameters to be propagated to span + Optional parameters to be propagated to the span """ @abc.abstractmethod @asynccontextmanager def trace_async(self, name: str, **kwargs) -> AsyncGenerator[BaseSpan, None]: - """Return a async span context manger. + """Async Context manager for creating a new span and set it + as the current span in this tracer's context. + + Exiting the context manager will call the span's end method, + as well as return the current span to its previous value by + returning to the previous context. Parameters ---------- name: str Span name kwargs: Optional[dict] - Optional parameters to be propagated to span + Optional parameters to be propagated to the span """ @abc.abstractmethod - def set_attribute(self, key: str, value: Union[str, numbers.Number, bool], **kwargs) -> None: - """set attribute on current active trace entity with a key-value pair. + def set_attribute(self, key: str, value: Any, **kwargs) -> None: + """set attribute on current active span with a key-value pair. Parameters ---------- key: str attribute key - value: Union[str, numbers.Number, bool] + value: Any attribute value + kwargs: Optional[dict] + Optional parameters to be propagated to the span """ @abc.abstractmethod def patch(self, modules: Sequence[str]) -> None: - """Instrument a set of supported libraries + """Instrument a set of given libraries if supported by provider + See specific provider for more detail + + Exmaple + ------- + tracer = Tracer(service="payment") + libraries = (['aioboto3',mysql]) + # provider.patch will be called by tracer.patch + tracer.patch(libraries) Parameters ---------- diff --git a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py index 83758525a28..5ec5aaeee1c 100644 --- a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py +++ b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py @@ -15,7 +15,7 @@ class OtelSpan(BaseSpan): def __init__(self, otel_span=otel_trace.Span): self.otel_span = otel_span - def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: # type: ignore[override] + def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: if not isinstance(value, (str, bool, int, float)): # convert value to str if value is not a supported structur value = str(value) @@ -69,7 +69,7 @@ async def trace_async(self, name: str, **kwargs) -> AsyncGenerator[OtelSpan, Non in_subsegment_async = trace_async - def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: # type: ignore[override] + def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: active_span = otel_trace.get_current_span() active_span.set_attribute(key=key, value=value) diff --git a/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py index 6530eb85623..dfdb8a9634f 100644 --- a/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py +++ b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py @@ -2,7 +2,7 @@ from contextlib import asynccontextmanager, contextmanager from numbers import Number -from typing import Any, AsyncGenerator, Generator, Sequence +from typing import Any, AsyncGenerator, Generator, Sequence, Union from ....shared import constants from ....shared.lazy_import import LazyLoader @@ -21,7 +21,7 @@ def __init__(self, subsegment): self.add_exception = self.subsegment.add_exception self.close = self.subsegment.close - def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: + def set_attribute(self, key: str, value: Any, **kwargs) -> None: if kwargs.get("namespace", "") != "": self.put_metadata(key=key, value=value, namespace=kwargs["namespace"]) else: @@ -42,21 +42,23 @@ def __init__(self, xray_recorder=None): @contextmanager def trace(self, name: str, **kwargs) -> Generator[XraySpan, None, None]: - with self.in_subsegment(name=name) as sub_segment: + with self.in_subsegment(name=name, **kwargs) as sub_segment: yield XraySpan(subsegment=sub_segment) @asynccontextmanager async def trace_async(self, name: str, **kwargs) -> AsyncGenerator[XraySpan, None]: - async with self.in_subsegment_async(name=name) as subsegment: + async with self.in_subsegment_async(name=name, **kwargs) as subsegment: yield XraySpan(subsegment=subsegment) - def set_attribute(self, key: str, value: str | Number | bool, **kwargs) -> None: - if kwargs.get("namespace", "") != "": - self.put_metadata(key=key, value=value, namespace=kwargs["namespace"]) - else: + def set_attribute(self, key: str, value: Any, **kwargs) -> None: + # for x_ray, put annotation support str, Number, bool + # we use put_metadata if any unsupported values are provided + if isinstance(value, (str, Number, bool)): self.put_annotation(key=key, value=value) + else: + self.put_metadata(key=key, value=value, namespace=kwargs["namespace"]) - def put_annotation(self, key: str, value: str | Number | bool) -> None: + def put_annotation(self, key: str, value: Union[str, Number, bool]) -> None: return self.recorder.put_annotation(key=key, value=value) def put_metadata(self, key: str, value: Any, namespace: str = "default") -> None: diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 6fad2ca0c5a..3695ee1bc76 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -15,8 +15,8 @@ ) from aws_lambda_powertools.shared.lazy_import import LazyLoader from aws_lambda_powertools.shared.types import AnyCallableT -from aws_lambda_powertools.tracing.provider import XrayProvider from aws_lambda_powertools.tracing.provider.base import BaseProvider, BaseSpan +from aws_lambda_powertools.tracing.provider.xray.xray_tracer import XrayProvider is_cold_start = True logger = logging.getLogger(__name__) @@ -178,15 +178,17 @@ def __init__( if self._is_xray_provider(): self._disable_xray_trace_batching() - def set_attribute(self, key: str, value: Union[str, numbers.Number, bool]): + def set_attribute(self, key: str, value: Any, **kwargs): """Set attribute on current active trace entity with a key-value pair. Parameters ---------- key : str attribute key - value : Union[str, numbers.Number, bool] + value : Any Value for attribute + kwargs: Optional[dict] + Optional parameters to be passed to provider.set_attributes Example ------- @@ -201,7 +203,10 @@ def set_attribute(self, key: str, value: Union[str, numbers.Number, bool]): logger.debug(f"setting attribute on key '{key}' with '{value}'") - self.provider.set_attribute(key=key, value=value) + namespace = kwargs.get("namespace") or self.service + kwargs.update({"namespace": namespace}) + + self.provider.set_attribute(key=key, value=value, **kwargs) def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]): """Adds annotation to existing segment or subsegment From cedb2af2801f8a03a6eb3561c0840900bdd2adec Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Wed, 10 Apr 2024 23:41:08 +0000 Subject: [PATCH 23/30] change to new example --- examples/tracer/src/sdk_escape_hatch copy.py | 19 +++++++++++++++++++ ...hatch.py => xray_provider_escape_hatch.py} | 0 2 files changed, 19 insertions(+) create mode 100644 examples/tracer/src/sdk_escape_hatch copy.py rename examples/tracer/src/{sdk_escape_hatch.py => xray_provider_escape_hatch.py} (100%) diff --git a/examples/tracer/src/sdk_escape_hatch copy.py b/examples/tracer/src/sdk_escape_hatch copy.py new file mode 100644 index 00000000000..825c3797029 --- /dev/null +++ b/examples/tracer/src/sdk_escape_hatch copy.py @@ -0,0 +1,19 @@ +from aws_lambda_powertools import Tracer +from aws_lambda_powertools.utilities.typing import LambdaContext + +tracer = Tracer() + + +def collect_payment(charge_id: str) -> str: + return f"dummy payment collected for charge: {charge_id}" + + +@tracer.capture_lambda_handler +def lambda_handler(event: dict, context: LambdaContext) -> str: + charge_id = event.get("charge_id", "") + with tracer.provider.in_subsegment("## collect_payment") as subsegment: # type: ignore + subsegment.put_annotation(key="PaymentId", value=charge_id) + ret = collect_payment(charge_id=charge_id) + subsegment.put_metadata(key="payment_response", value=ret) + + return ret diff --git a/examples/tracer/src/sdk_escape_hatch.py b/examples/tracer/src/xray_provider_escape_hatch.py similarity index 100% rename from examples/tracer/src/sdk_escape_hatch.py rename to examples/tracer/src/xray_provider_escape_hatch.py From ee869b3d1890ab52a2120a08b713f9a1afeef83b Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Wed, 10 Apr 2024 23:41:45 +0000 Subject: [PATCH 24/30] change to new example --- .../tracer/src/{sdk_escape_hatch copy.py => sdk_escape_hatch.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/tracer/src/{sdk_escape_hatch copy.py => sdk_escape_hatch.py} (100%) diff --git a/examples/tracer/src/sdk_escape_hatch copy.py b/examples/tracer/src/sdk_escape_hatch.py similarity index 100% rename from examples/tracer/src/sdk_escape_hatch copy.py rename to examples/tracer/src/sdk_escape_hatch.py From 68ee4034b5c9820cacac30bc5130b5e612fc7441 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Thu, 11 Apr 2024 00:18:49 +0000 Subject: [PATCH 25/30] docstring in current base class --- aws_lambda_powertools/tracing/base.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/aws_lambda_powertools/tracing/base.py b/aws_lambda_powertools/tracing/base.py index 383014b147d..bd3cf842deb 100644 --- a/aws_lambda_powertools/tracing/base.py +++ b/aws_lambda_powertools/tracing/base.py @@ -5,19 +5,18 @@ from typing import Any, Generator, List, Optional, Sequence, Union -## this class is deprecated, keep this class for backwards compatibility +## MAINTENANCE: deprecated, kept for backwards compatibility until v3 is released class BaseSegment(abc.ABC): """Holds common properties and methods on segment and subsegment.""" @abc.abstractmethod - def close(self, end_time: Optional[int] = None): + def close(self, end_time: Optional[float] = None): """Close the trace entity by setting `end_time` and flip the in progress flag to False. Parameters ---------- - end_time: int - # TO-discuss: Providers typically use ns(time_ns -> nanosecond) as start or close time + end_time: float Time in epoch seconds, by default current time will be used. """ From 07714e9c78f6d46130441c750244be4b03f128d9 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Fri, 12 Apr 2024 22:39:15 +0000 Subject: [PATCH 26/30] refactor set_attribute, fix test --- .../tracing/provider/datadog/dd_tracer.py | 86 ---------- .../tracing/provider/otel/otel_tracer.py | 82 ---------- .../tracing/provider/xray/xray_tracer.py | 90 ++++++++-- aws_lambda_powertools/tracing/tracer.py | 46 ++---- poetry.lock | 154 ++++++++---------- poetry.toml | 2 - pyproject.toml | 5 +- tests/unit/test_tracing.py | 48 +++++- 8 files changed, 200 insertions(+), 313 deletions(-) delete mode 100644 aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py delete mode 100644 aws_lambda_powertools/tracing/provider/otel/otel_tracer.py diff --git a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py b/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py deleted file mode 100644 index 8d0fbecb7ee..00000000000 --- a/aws_lambda_powertools/tracing/provider/datadog/dd_tracer.py +++ /dev/null @@ -1,86 +0,0 @@ -from __future__ import annotations - -import logging -from contextlib import asynccontextmanager, contextmanager -from typing import Any, AsyncGenerator, Dict, Generator, Optional, Sequence - -import ddtrace - -from ..base import BaseProvider, BaseSpan - -logger = logging.getLogger(__name__) - - -class DDSpan(BaseSpan): - def __init__(self, dd_span=ddtrace.Span): - self.dd_span = dd_span - - def set_attribute(self, key: str | bytes, value: Any, **kwargs) -> None: - self.dd_span.set_tag(key=key, value=value) - - def record_exception(self, exception: BaseException, attributes: Optional[Dict] = None, **kwargs): - self.dd_span.set_traceback() - _attributes = attributes or {} - if kwargs: - kwargs.update(_attributes) - # attribute should overwrite kwargs - _attributes = kwargs - self.dd_span.set_tags(tags=_attributes) - - -class DDTraceProvider(BaseProvider): - def __init__(self, dd_tracer: ddtrace.Tracer): - self.dd_tracer = dd_tracer - - @contextmanager - def trace( - self, - name: str, - service: Optional[str] = None, - resource: Optional[str] = None, - span_type: Optional[str] = None, - **kwargs, - ) -> Generator[DDSpan, None, None]: - with self.dd_tracer.trace( - name=name, - service=service, - resource=resource, - span_type=span_type, - ) as dd_span: - yield DDSpan(dd_span=dd_span) - - in_subsegment = trace - - @asynccontextmanager - async def trace_async( - self, - name: str, - service: Optional[str] = None, - resource: Optional[str] = None, - span_type: Optional[str] = None, - **kwargs, - ) -> AsyncGenerator[DDSpan, None]: - with self.dd_tracer.trace( - name=name, - service=service, - resource=resource, - span_type=span_type, - ) as dd_span: - yield DDSpan(dd_span=dd_span) - - in_subsegment_async = trace_async - - def set_attribute(self, key: str | bytes, value: Any, **kwargs: Any) -> None: - span = self.dd_tracer.context_provider.active() - # ignore if no active span - if isinstance(span, ddtrace.Span): - span.set_tag(key=key, value=value) - - def patch(self, modules: Sequence[str]) -> None: - module_to_patch = {} - for m in modules: - module_to_patch[m] = True - ddtrace.patch(**module_to_patch) # type:ignore - - def patch_all(self) -> None: - ddtrace.patch_all() diff --git a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py b/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py deleted file mode 100644 index 5ec5aaeee1c..00000000000 --- a/aws_lambda_powertools/tracing/provider/otel/otel_tracer.py +++ /dev/null @@ -1,82 +0,0 @@ -from __future__ import annotations - -import logging -from contextlib import asynccontextmanager, contextmanager -from typing import AsyncGenerator, Dict, Generator, Optional, Sequence - -from opentelemetry import trace as otel_trace - -from ..base import BaseProvider, BaseSpan - -logger = logging.getLogger(__name__) - - -class OtelSpan(BaseSpan): - def __init__(self, otel_span=otel_trace.Span): - self.otel_span = otel_span - - def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: - if not isinstance(value, (str, bool, int, float)): - # convert value to str if value is not a supported structur - value = str(value) - self.otel_span.set_attribute(key=key, value=value) - - def record_exception( - self, - exception: BaseException, - attributes: Optional[Dict] = None, - timestamp: Optional[int] = None, - escaped: bool = False, - **kwargs, - ): - _attributes = attributes or {} - if kwargs: - kwargs.update(_attributes) - # attribute should overwrite kwargs - _attributes = kwargs - self.otel_span.record_exception( - exception=exception, - attributes=_attributes, - timestamp=timestamp, - escaped=escaped, - ) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - try: - self.otel_span.__exit__(exc_type, exc_val, exc_tb) - except Exception as e: - logger.exception(f"error closing trace {e}") - - -class OtelProvider(BaseProvider): - def __init__(self, otel_tracer: otel_trace.Tracer): - self.otel_tracer = otel_tracer - - @contextmanager - def trace(self, name: str, **kwargs) -> Generator[OtelSpan, None, None]: - with self.otel_tracer.start_as_current_span(name=name, **kwargs) as otel_span: - yield OtelSpan(otel_span=otel_span) - - in_subsegment = trace - - @asynccontextmanager - async def trace_async(self, name: str, **kwargs) -> AsyncGenerator[OtelSpan, None]: - with self.otel_tracer.start_as_current_span(name=name, **kwargs) as otel_span: - yield OtelSpan(otel_span=otel_span) - - in_subsegment_async = trace_async - - def set_attribute(self, key: str, value: str | float | bool, **kwargs) -> None: - active_span = otel_trace.get_current_span() - active_span.set_attribute(key=key, value=value) - - def patch(self, modules: Sequence[str]) -> None: - # OTEL sdk doesn't have patch - pass - - def patch_all(self) -> None: - # OTEL sdk doesn't have patch - pass diff --git a/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py index dfdb8a9634f..df5549a1c8a 100644 --- a/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py +++ b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py @@ -2,7 +2,7 @@ from contextlib import asynccontextmanager, contextmanager from numbers import Number -from typing import Any, AsyncGenerator, Generator, Sequence, Union +from typing import Any, AsyncGenerator, Generator, Literal, Sequence, Union from ....shared import constants from ....shared.lazy_import import LazyLoader @@ -21,11 +21,47 @@ def __init__(self, subsegment): self.add_exception = self.subsegment.add_exception self.close = self.subsegment.close - def set_attribute(self, key: str, value: Any, **kwargs) -> None: - if kwargs.get("namespace", "") != "": - self.put_metadata(key=key, value=value, namespace=kwargs["namespace"]) - else: + def set_attribute( + self, + key: str, + value: Any, + category: Literal["Annotation", "Metadata", "Auto"] = "Auto", + **kwargs, + ) -> None: + """ + Set attribute on this span with a key-value pair. + + Parameters + ---------- + key : str + attribute key + value : Any + Value for attribute + category : Literal["Annotation","Metadata","Auto"] = "Auto" + This parameter specifies the category of attribute to set. + - **"Annotation"**: Sets the attribute as an Annotation. + - **"Metadata"**: Sets the attribute as Metadata. + - **"Auto" (default)**: Automatically determines the attribute + type based on its value. + + kwargs: Optional[dict] + Optional parameters to be passed to provider.set_attributes + """ + if category == "Annotation": self.put_annotation(key=key, value=value) + return + + if category == "Metadata": + self.put_metadata(key=key, value=value, namespace=kwargs.get("namespace", "dafault")) + return + + # Auto + if isinstance(value, (str, Number, bool)): + self.put_annotation(key=key, value=value) + return + + # Auto & not in (str, Number, bool) + self.put_metadata(key=key, value=value, namespace=kwargs.get("namespace", "dafault")) def record_exception(self, exception: BaseException, **kwargs): stack = aws_xray_sdk.core.utils.stacktrace.get_stacktrace() @@ -50,13 +86,47 @@ async def trace_async(self, name: str, **kwargs) -> AsyncGenerator[XraySpan, Non async with self.in_subsegment_async(name=name, **kwargs) as subsegment: yield XraySpan(subsegment=subsegment) - def set_attribute(self, key: str, value: Any, **kwargs) -> None: - # for x_ray, put annotation support str, Number, bool - # we use put_metadata if any unsupported values are provided + def set_attribute( + self, + key: str, + value: Any, + category: Literal["Annotation", "Metadata", "Auto"] = "Auto", + **kwargs, + ) -> None: + """ + Set attribute on the current active span with a key-value pair. + + Parameters + ---------- + key : str + attribute key + value : Any + Value for attribute + category : Literal["Annotation","Metadata","Auto"] = "Auto" + This parameter specifies the type of attribute to set. + - **"Annotation"**: Sets the attribute as an Annotation. + - **"Metadata"**: Sets the attribute as Metadata. + - **"Auto" (default)**: Automatically determines the attribute + type based on its value. + + kwargs: Optional[dict] + Optional parameters to be passed to provider.set_attributes + """ + if category == "Annotation": + self.put_annotation(key=key, value=value) + return + + if category == "Metadata": + self.put_metadata(key=key, value=value, namespace=kwargs.get("namespace", "dafault")) + return + + # Auto if isinstance(value, (str, Number, bool)): self.put_annotation(key=key, value=value) - else: - self.put_metadata(key=key, value=value, namespace=kwargs["namespace"]) + return + + # Auto & not in (str, Number, bool) + self.put_metadata(key=key, value=value, namespace=kwargs.get("namespace", "dafault")) def put_annotation(self, key: str, value: Union[str, Number, bool]) -> None: return self.recorder.put_annotation(key=key, value=value) diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 3695ee1bc76..ef4d87405e0 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -179,7 +179,7 @@ def __init__( self._disable_xray_trace_batching() def set_attribute(self, key: str, value: Any, **kwargs): - """Set attribute on current active trace entity with a key-value pair. + """Set attribute on current active span with a key-value pair. Parameters ---------- @@ -231,10 +231,7 @@ def put_annotation(self, key: str, value: Union[str, numbers.Number, bool]): logger.debug(f"Annotating on key '{key}' with '{value}'") - if self._is_custom_provider(): - self.provider.set_attribute(key=key, value=value) - else: - self.provider.put_annotation(key=key, value=value) # type: ignore + self.provider.set_attribute(key=key, value=value, category="Annotation") def put_metadata(self, key: str, value: Any, namespace: Optional[str] = None): """Adds metadata to existing segment or subsegment @@ -262,10 +259,8 @@ def put_metadata(self, key: str, value: Any, namespace: Optional[str] = None): namespace = namespace or self.service logger.debug(f"Adding metadata on key '{key}' with '{value}' at namespace '{namespace}'") - if self._is_custom_provider(): - self.provider.set_attribute(key=f"{namespace}.{key}", value=value) - else: - self.provider.put_metadata(key=key, value=value, namespace=namespace) # type: ignore + + self.provider.set_attribute(key=key, value=value, namespace=namespace, category="Metadata") def patch(self, modules: Optional[Sequence[str]] = None): """Patch modules for instrumentation. @@ -373,19 +368,13 @@ def decorate(event, context, **kwargs): finally: global is_cold_start logger.debug("Annotating cold start") - if self._is_custom_provider(): - subsegment.set_attribute(key="ColdStart", value=is_cold_start) - else: - subsegment.put_annotation(key="ColdStart", value=is_cold_start) + subsegment.set_attribute(key="ColdStart", value=is_cold_start) if is_cold_start: is_cold_start = False if self.service: - if self._is_custom_provider(): - subsegment.set_attribute(key="Service", value=self.service) - else: - subsegment.put_annotation(key="Service", value=self.service) + subsegment.set_attribute(key="Service", value=self.service) return response @@ -765,10 +754,7 @@ def _add_response_as_metadata( """ if data is None or not capture_response or subsegment is None: return - if self._is_custom_provider(): - subsegment.set_attribute(key=f"{method_name} response", value=data, namespace=self.service) - else: - subsegment.put_metadata(key=f"{method_name} response", value=data, namespace=self.service) # type: ignore + subsegment.set_attribute(key=f"{method_name} response", value=data, namespace=self.service, category="Metadata") def _add_full_exception_as_metadata( self, @@ -793,14 +779,12 @@ def _add_full_exception_as_metadata( if not capture_error: return - if self._is_custom_provider(): - subsegment.set_attribute( - key=f"{self.service}.{method_name} error", - value=str(error), - namespace=self.service, - ) - else: - subsegment.put_metadata(key=f"{method_name} error", value=error, namespace=self.service) # type: ignore + subsegment.set_attribute( + key=f"{method_name} error", + value=error, + namespace=self.service, + category="Metadata", + ) @staticmethod def _disable_tracer_provider(): @@ -883,10 +867,6 @@ def _disable_xray_trace_batching(self): def _is_xray_provider(self): return isinstance(self.provider, XrayProvider) - def _is_custom_provider(self): - # check if provider is not default xray_provider. Avoid test conflits - return not self._is_xray_provider() and isinstance(self.provider, BaseProvider) - def ignore_endpoint(self, hostname: Optional[str] = None, urls: Optional[List[str]] = None): """If you want to ignore certain httplib requests you can do so based on the hostname or URL that is being requested. diff --git a/poetry.lock b/poetry.lock index d933fccd239..d448e2e087c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -158,17 +158,17 @@ typeguard = ">=2.13.3,<2.14.0" [[package]] name = "aws-cdk-aws-lambda-python-alpha" -version = "2.136.1a0" +version = "2.137.0a0" description = "The CDK Construct Library for AWS Lambda in Python" optional = false python-versions = "~=3.8" files = [ - {file = "aws-cdk.aws-lambda-python-alpha-2.136.1a0.tar.gz", hash = "sha256:f27dea386144ad906539a2578151b43e59971fa1bdc86b16bd91a4ec0e34e938"}, - {file = "aws_cdk.aws_lambda_python_alpha-2.136.1a0-py3-none-any.whl", hash = "sha256:4ebaa2adf57e1a64ff30398bed1514cbdeb5260e9a0bbacf8b687b21d59196ca"}, + {file = "aws-cdk.aws-lambda-python-alpha-2.137.0a0.tar.gz", hash = "sha256:6168c6d0394d4c908da921fa8e3822a7a1dda8a7b27c28b597466be598385d05"}, + {file = "aws_cdk.aws_lambda_python_alpha-2.137.0a0-py3-none-any.whl", hash = "sha256:4e58fb736cee3a44ae735edd5b90cadebc6aacb63a58f2baff44d27d0d5a803d"}, ] [package.dependencies] -aws-cdk-lib = ">=2.136.1,<3.0.0" +aws-cdk-lib = ">=2.137.0,<3.0.0" constructs = ">=10.0.0,<11.0.0" jsii = ">=1.96.0,<2.0.0" publication = ">=0.0.3" @@ -176,13 +176,13 @@ typeguard = ">=2.13.3,<2.14.0" [[package]] name = "aws-cdk-lib" -version = "2.136.1" +version = "2.137.0" description = "Version 2 of the AWS Cloud Development Kit library" optional = false python-versions = "~=3.8" files = [ - {file = "aws-cdk-lib-2.136.1.tar.gz", hash = "sha256:7ce50fe8b0c7415a70d4c3ca90cd69a4f7207dbbc46c5d04c73e1b49beb27283"}, - {file = "aws_cdk_lib-2.136.1-py3-none-any.whl", hash = "sha256:d9b324919d2cdab3c51f52f165d4ade74f8c620fa8d3e3d416d8b645962cf474"}, + {file = "aws-cdk-lib-2.137.0.tar.gz", hash = "sha256:ecdd3aaf6a25cefecb6dd16342013d792e9315798854eb1251a442331e27aef9"}, + {file = "aws_cdk_lib-2.137.0-py3-none-any.whl", hash = "sha256:0cd861c315e4711dbffbc4ee3afe3cdc18c5154d4ab078a355e92011a6416c59"}, ] [package.dependencies] @@ -303,33 +303,33 @@ yaml = ["PyYAML"] [[package]] name = "black" -version = "24.3.0" +version = "24.4.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, - {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, - {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, - {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, - {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, - {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, - {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, - {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, - {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, - {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, - {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, - {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, - {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, - {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, - {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, - {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, - {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, - {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, - {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, - {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, - {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, - {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, + {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"}, + {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"}, + {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"}, + {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"}, + {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"}, + {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"}, + {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"}, + {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"}, + {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"}, + {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"}, + {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"}, + {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"}, + {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"}, + {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"}, + {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"}, + {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"}, + {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"}, + {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"}, + {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"}, + {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"}, + {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"}, + {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"}, ] [package.dependencies] @@ -349,17 +349,17 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.34.81" +version = "1.34.84" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.34.81-py3-none-any.whl", hash = "sha256:18224d206a8a775bcaa562d22ed3d07854934699190e12b52fcde87aac76a80e"}, - {file = "boto3-1.34.81.tar.gz", hash = "sha256:004dad209d37b3d2df88f41da13b7ad702a751904a335fac095897ff7a19f82b"}, + {file = "boto3-1.34.84-py3-none-any.whl", hash = "sha256:7a02f44af32095946587d748ebeb39c3fa15b9d7275307ff612a6760ead47e04"}, + {file = "boto3-1.34.84.tar.gz", hash = "sha256:91e6343474173e9b82f603076856e1d5b7b68f44247bdd556250857a3f16b37b"}, ] [package.dependencies] -botocore = ">=1.34.81,<1.35.0" +botocore = ">=1.34.84,<1.35.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -368,13 +368,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.34.81" +version = "1.34.84" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.34.81-py3-none-any.whl", hash = "sha256:85f6fd7c5715eeef7a236c50947de00f57d72e7439daed1125491014b70fab01"}, - {file = "botocore-1.34.81.tar.gz", hash = "sha256:f79bf122566cc1f09d71cc9ac9fcf52d47ba48b761cbc3f064017b36a3c40eb8"}, + {file = "botocore-1.34.84-py3-none-any.whl", hash = "sha256:da1ae0a912e69e10daee2a34dafd6c6c106450d20b8623665feceb2d96c173eb"}, + {file = "botocore-1.34.84.tar.gz", hash = "sha256:a2b309bf5594f0eb6f63f355ade79ba575ce8bf672e52e91da1a7933caa245e6"}, ] [package.dependencies] @@ -429,13 +429,13 @@ ujson = ["ujson (>=5.7.0)"] [[package]] name = "cdk-nag" -version = "2.28.86" +version = "2.28.88" description = "Check CDK v2 applications for best practices using a combination on available rule packs." optional = false python-versions = "~=3.8" files = [ - {file = "cdk-nag-2.28.86.tar.gz", hash = "sha256:48bfcd9616752bb9daabcb24aa0783ab73cebfe38206c2e87fa8523475ff561e"}, - {file = "cdk_nag-2.28.86-py3-none-any.whl", hash = "sha256:68c64976f4d9769e3fdc5eb987417151aa39eeedc6e6922384b91d333e5e4a77"}, + {file = "cdk-nag-2.28.88.tar.gz", hash = "sha256:7341bebaca3e1d825709f700b0719124a15b7d21502a139cb7003e5e35d1f56e"}, + {file = "cdk_nag-2.28.88-py3-none-any.whl", hash = "sha256:06c7099765eefe3e569b7b5d19145153b34f8c5f4e2b1301db1002cdaabcb7ca"}, ] [package.dependencies] @@ -1253,13 +1253,13 @@ parser = ["pyhcl (>=0.4.4,<0.5.0)"] [[package]] name = "idna" -version = "3.6" +version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] @@ -1516,18 +1516,19 @@ ply = "*" [[package]] name = "jsonpickle" -version = "3.0.3" -description = "Python library for serializing any arbitrary object graph into JSON" +version = "3.0.4" +description = "Serialize any Python object to JSON" optional = false python-versions = ">=3.7" files = [ - {file = "jsonpickle-3.0.3-py3-none-any.whl", hash = "sha256:e8d6dcc58f6722bea0321cd328fbda81c582461185688a535df02be0f699afb4"}, - {file = "jsonpickle-3.0.3.tar.gz", hash = "sha256:5691f44495327858ab3a95b9c440a79b41e35421be1a6e09a47b6c9b9421fd06"}, + {file = "jsonpickle-3.0.4-py3-none-any.whl", hash = "sha256:04ae7567a14269579e3af66b76bda284587458d7e8a204951ca8f71a3309952e"}, + {file = "jsonpickle-3.0.4.tar.gz", hash = "sha256:a1b14c8d6221cd8f394f2a97e735ea1d7edc927fbd135b26f2f8700657c8c62b"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] -testing = ["ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-ruff", "scikit-learn", "simplejson", "sqlalchemy", "ujson"] +docs = ["furo", "rst.linker (>=1.9)", "sphinx"] +packaging = ["build", "twine"] +testing = ["bson", "ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=3.5,!=3.7.3)", "pytest-benchmark", "pytest-benchmark[histogram]", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-ruff (>=0.2.1)", "scikit-learn", "scipy", "scipy (>=1.9.3)", "simplejson", "sqlalchemy", "ujson"] [[package]] name = "jsonpointer" @@ -1986,13 +1987,13 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""} [[package]] name = "mypy-boto3-cloudformation" -version = "1.34.77" -description = "Type annotations for boto3.CloudFormation 1.34.77 service generated with mypy-boto3-builder 7.23.2" +version = "1.34.84" +description = "Type annotations for boto3.CloudFormation 1.34.84 service generated with mypy-boto3-builder 7.23.2" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-boto3-cloudformation-1.34.77.tar.gz", hash = "sha256:990014182681f9df2257f185056126c0db30bf1260f6cd3d46cedb3cf467f0ad"}, - {file = "mypy_boto3_cloudformation-1.34.77-py3-none-any.whl", hash = "sha256:4a32a2f7e1e06a1ed4daa1e278149b1a70876b108f0056507f7d7483856a93a6"}, + {file = "mypy_boto3_cloudformation-1.34.84-py3-none-any.whl", hash = "sha256:580954031cb3650588b91f592e8f51855b2ff435d763ac0d69cf271c8433315f"}, + {file = "mypy_boto3_cloudformation-1.34.84.tar.gz", hash = "sha256:82d14df3757f30b5a1d34650839d415d265d4de41cf355d63e10221fcc67f177"}, ] [package.dependencies] @@ -2000,13 +2001,13 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""} [[package]] name = "mypy-boto3-cloudwatch" -version = "1.34.75" -description = "Type annotations for boto3.CloudWatch 1.34.75 service generated with mypy-boto3-builder 7.23.2" +version = "1.34.83" +description = "Type annotations for boto3.CloudWatch 1.34.83 service generated with mypy-boto3-builder 7.23.2" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-boto3-cloudwatch-1.34.75.tar.gz", hash = "sha256:434433f34f68e8570de4e568b4b2c5ddf81e6edfb699b16785f04229364ed1a5"}, - {file = "mypy_boto3_cloudwatch-1.34.75-py3-none-any.whl", hash = "sha256:5954e58b0f929863e8468a158d01a604e641f6d6f0510a9eba6912436e0a4abf"}, + {file = "mypy-boto3-cloudwatch-1.34.83.tar.gz", hash = "sha256:766e166c5b463d9885a5929dc16bb592e0fa7d7beaf569aa4f501d85a848bc13"}, + {file = "mypy_boto3_cloudwatch-1.34.83-py3-none-any.whl", hash = "sha256:6af4fff0ec7c09e423df5a69fff4df8a74044462686e8679b4fe73c106787854"}, ] [package.dependencies] @@ -2154,33 +2155,6 @@ files = [ deprecated = ">=1.2.6" importlib-metadata = ">=6.0,<=7.0" -[[package]] -name = "opentelemetry-sdk" -version = "1.24.0" -description = "OpenTelemetry Python SDK" -optional = false -python-versions = ">=3.8" -files = [ - {file = "opentelemetry_sdk-1.24.0-py3-none-any.whl", hash = "sha256:fa731e24efe832e98bcd90902085b359dcfef7d9c9c00eb5b9a18587dae3eb59"}, - {file = "opentelemetry_sdk-1.24.0.tar.gz", hash = "sha256:75bc0563affffa827700e0f4f4a68e1e257db0df13372344aebc6f8a64cde2e5"}, -] - -[package.dependencies] -opentelemetry-api = "1.24.0" -opentelemetry-semantic-conventions = "0.45b0" -typing-extensions = ">=3.7.4" - -[[package]] -name = "opentelemetry-semantic-conventions" -version = "0.45b0" -description = "OpenTelemetry Semantic Conventions" -optional = false -python-versions = ">=3.8" -files = [ - {file = "opentelemetry_semantic_conventions-0.45b0-py3-none-any.whl", hash = "sha256:a4a6fb9a7bacd9167c082aa4681009e9acdbfa28ffb2387af50c2fef3d30c864"}, - {file = "opentelemetry_semantic_conventions-0.45b0.tar.gz", hash = "sha256:7c84215a44ac846bc4b8e32d5e78935c5c43482e491812a0bb8aaf87e4d92118"}, -] - [[package]] name = "packaging" version = "24.0" @@ -3101,18 +3075,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "69.2.0" +version = "69.4.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.2.0-py3-none-any.whl", hash = "sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c"}, - {file = "setuptools-69.2.0.tar.gz", hash = "sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e"}, + {file = "setuptools-69.4.0-py3-none-any.whl", hash = "sha256:b6df12d754b505e4ca283c61582d5578db83ae2f56a979b3bc9a8754705ae3bf"}, + {file = "setuptools-69.4.tar.gz", hash = "sha256:659e902e587e77fab8212358f5b03977b5f0d18d4724310d4a093929fee4ca1a"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -3575,4 +3549,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0.0" -content-hash = "d2efd600360dced321a6c082b07702b3b99f5d9c27c51efd18308cba84e1ccb7" +content-hash = "ea94851ee2a7b5379efeadd83e424e41b99b5a4da43a9ffb2027af8abcb9a267" diff --git a/poetry.toml b/poetry.toml index ab1033bd372..e69de29bb2d 100644 --- a/poetry.toml +++ b/poetry.toml @@ -1,2 +0,0 @@ -[virtualenvs] -in-project = true diff --git a/pyproject.toml b/pyproject.toml index eb49f106c77..d5595289956 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,11 +47,7 @@ redis = { version = ">=4.4,<6.0", optional = true } typing-extensions = "^4.11.0" datadog-lambda = { version = ">=4.77,<6.0", optional = true } aws-encryption-sdk = { version = "^3.1.1", optional = true } -ddtrace = "^2.5.2" -opentelemetry-api = "^1.22.0" -opentelemetry-sdk = "^1.22.0" jsonpath-ng = { version = "^1.6.0", optional = true } -types-aws-xray-sdk = "^2.13.0.20240308" [tool.poetry.dev-dependencies] coverage = { extras = ["toml"], version = "^7.4" } @@ -77,6 +73,7 @@ aws-cdk-lib = "^2.136.0" "cdklabs.generative-ai-cdk-constructs" = "^0.1.110" pytest-benchmark = "^4.0.0" mypy-boto3-appconfig = "^1.34.58" +types-aws-xray-sdk = "^2.13.0.20240308" mypy-boto3-cloudformation = "^1.34.77" mypy-boto3-cloudwatch = "^1.34.75" mypy-boto3-dynamodb = "^1.34.67" diff --git a/tests/unit/test_tracing.py b/tests/unit/test_tracing.py index 9922ac71c7f..a016ce99698 100644 --- a/tests/unit/test_tracing.py +++ b/tests/unit/test_tracing.py @@ -1,4 +1,5 @@ import contextlib +from numbers import Number from typing import NamedTuple from unittest import mock from unittest.mock import MagicMock @@ -38,6 +39,18 @@ def __init__( self.in_subsegment_async = in_subsegment_async or mocker.MagicMock(spec=True) self.trace_async = self.in_subsegment_async + def set_attribute(self, *args, **kwargs): + if kwargs.get("category") == "Metadata": + return self.put_metadata(*args, **kwargs) + + if kwargs.get("category") == "Annotation": + return self.put_annotation(*args, **kwargs) + + if isinstance(kwargs.get("value"), (str, Number, bool)): + return self.put_annotation(*args, **kwargs) + + return self.put_metadata(*args, **kwargs) + def put_metadata(self, *args, **kwargs): return self.put_metadata_mock(*args, **kwargs) @@ -67,8 +80,8 @@ def reset_tracing_config(mocker): @pytest.fixture -def in_subsegment_mock(): - class AsyncContextManager(mock.MagicMock): +def in_subsegment_mock(mocker): + class AsyncContextManager(mocker.MagicMock): async def __aenter__(self, *args, **kwargs): return self.__enter__() @@ -76,14 +89,32 @@ async def __aexit__(self, *args, **kwargs): return self.__exit__(*args, **kwargs) class InSubsegment(NamedTuple): - in_subsegment: mock.MagicMock = AsyncContextManager() - put_annotation: mock.MagicMock = mock.MagicMock() - put_metadata: mock.MagicMock = mock.MagicMock() + in_subsegment: mocker.MagicMock = AsyncContextManager() + put_annotation: mocker.MagicMock = mocker.MagicMock() + put_metadata: mocker.MagicMock = mocker.MagicMock() + + def set_attribute(self, *args, **kwargs): + if kwargs.get("category") == "Metadata": + print("meta") + kwargs.pop("category") + return self.put_metadata(*args, **kwargs) + + if kwargs.get("category") == "Annotation": + print("anno") + kwargs.pop("category") + return self.put_annotation(*args, **kwargs) + + if isinstance(kwargs.get("value"), (str, Number, bool)): + return self.put_annotation(*args, **kwargs) + + return self.put_metadata(*args, **kwargs) in_subsegment = InSubsegment() in_subsegment.in_subsegment.return_value.__enter__.return_value.put_annotation = in_subsegment.put_annotation in_subsegment.in_subsegment.return_value.__enter__.return_value.put_metadata = in_subsegment.put_metadata in_subsegment.in_subsegment.return_value.__aenter__.return_value.put_metadata = in_subsegment.put_metadata + in_subsegment.in_subsegment.return_value.__enter__.return_value.set_attribute = in_subsegment.set_attribute + in_subsegment.in_subsegment.return_value.__aenter__.return_value.set_attribute = in_subsegment.set_attribute yield in_subsegment @@ -157,6 +188,7 @@ def test_tracer_custom_metadata(monkeypatch, mocker, dummy_response, provider_st key=annotation_key, value=annotation_value, namespace="booking", + category="Metadata", ) @@ -174,7 +206,11 @@ def test_tracer_custom_annotation(monkeypatch, mocker, dummy_response, provider_ # THEN we should have an annotation as expected assert put_annotation_mock.call_count == 1 - assert put_annotation_mock.call_args == mocker.call(key=annotation_key, value=annotation_value) + assert put_annotation_mock.call_args == mocker.call( + key=annotation_key, + value=annotation_value, + category="Annotation", + ) @mock.patch("aws_lambda_powertools.tracing.Tracer.patch") From 5fb7990b6f08935eb91d9a833a72c7339af5a541 Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Fri, 12 Apr 2024 22:41:18 +0000 Subject: [PATCH 27/30] fix poetry --- poetry.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 5f3fedcdf60..7bbf5b8fca8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3549,4 +3549,4 @@ validation = ["fastjsonschema"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<4.0.0" -content-hash = "b5c68b5ceb3698ae79719b05033c334987ec40ce8923383a38d4b7626096f9bb" +content-hash = "48e18f76218824ad11a379c4ad0765ad835e8ddd3449a1448d5cb2eb163cd233" From aeaf6ff70499a61b55127890adf27711acd7334c Mon Sep 17 00:00:00 2001 From: Roger Zhang Date: Fri, 12 Apr 2024 22:51:12 +0000 Subject: [PATCH 28/30] remote print --- tests/unit/test_tracing.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/unit/test_tracing.py b/tests/unit/test_tracing.py index a016ce99698..a65033c5849 100644 --- a/tests/unit/test_tracing.py +++ b/tests/unit/test_tracing.py @@ -89,18 +89,16 @@ async def __aexit__(self, *args, **kwargs): return self.__exit__(*args, **kwargs) class InSubsegment(NamedTuple): - in_subsegment: mocker.MagicMock = AsyncContextManager() - put_annotation: mocker.MagicMock = mocker.MagicMock() - put_metadata: mocker.MagicMock = mocker.MagicMock() + in_subsegment: mock.MagicMock = AsyncContextManager() + put_annotation: mock.MagicMock = mock.MagicMock() + put_metadata: mock.MagicMock = mock.MagicMock() def set_attribute(self, *args, **kwargs): if kwargs.get("category") == "Metadata": - print("meta") kwargs.pop("category") return self.put_metadata(*args, **kwargs) if kwargs.get("category") == "Annotation": - print("anno") kwargs.pop("category") return self.put_annotation(*args, **kwargs) From 024db2515a57c321f6df76222f8c1d1f38072bf1 Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 17 Apr 2024 13:39:01 +0100 Subject: [PATCH 29/30] Addressing Ruben's feedback --- .../tracing/provider/xray/xray_tracer.py | 16 ++++++++-------- aws_lambda_powertools/tracing/tracer.py | 16 +++++----------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py index df5549a1c8a..2c8f905a50d 100644 --- a/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py +++ b/aws_lambda_powertools/tracing/provider/xray/xray_tracer.py @@ -29,15 +29,15 @@ def set_attribute( **kwargs, ) -> None: """ - Set attribute on this span with a key-value pair. + Set an attribute on this span with a key-value pair. Parameters ---------- - key : str + key: str attribute key - value : Any + value: Any Value for attribute - category : Literal["Annotation","Metadata","Auto"] = "Auto" + category: Literal["Annotation","Metadata","Auto"] = "Auto" This parameter specifies the category of attribute to set. - **"Annotation"**: Sets the attribute as an Annotation. - **"Metadata"**: Sets the attribute as Metadata. @@ -94,15 +94,15 @@ def set_attribute( **kwargs, ) -> None: """ - Set attribute on the current active span with a key-value pair. + Set an attribute on the current active span with a key-value pair. Parameters ---------- - key : str + key: str attribute key - value : Any + value: Any Value for attribute - category : Literal["Annotation","Metadata","Auto"] = "Auto" + category: Literal["Annotation","Metadata","Auto"] = "Auto" This parameter specifies the type of attribute to set. - **"Annotation"**: Sets the attribute as an Annotation. - **"Metadata"**: Sets the attribute as Metadata. diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index ef4d87405e0..2c33729fb02 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -179,20 +179,20 @@ def __init__( self._disable_xray_trace_batching() def set_attribute(self, key: str, value: Any, **kwargs): - """Set attribute on current active span with a key-value pair. + """Set an attribute on current active span with a key-value pair. Parameters ---------- - key : str + key: str attribute key - value : Any + value: Any Value for attribute kwargs: Optional[dict] Optional parameters to be passed to provider.set_attributes Example ------- - Set attribute for a pseudo service named payment + Set an attribute for a pseudo service named payment tracer = Tracer(service="payment") tracer.set_attribute("PaymentStatus", "CONFIRMED") @@ -846,13 +846,7 @@ def _reset_config(cls): cls._config = copy.copy(cls._default_config) def _patch_xray_provider(self): - # Due to Lazy Import, we need to activate `core` attrib via import - # we also need to include `patch`, `patch_all` methods - # to ensure patch calls are done via the provider - - provider = XrayProvider() - - return provider + return XrayProvider() def _disable_xray_trace_batching(self): """Configure X-Ray SDK to send subsegment individually over batching From 80739fd0b350be1b2be9c3829c3843c9b7fe0e8b Mon Sep 17 00:00:00 2001 From: Leandro Damascena Date: Wed, 17 Apr 2024 13:59:55 +0100 Subject: [PATCH 30/30] Reveting possible breaking change --- aws_lambda_powertools/tracing/base.py | 4 ++-- aws_lambda_powertools/tracing/tracer.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aws_lambda_powertools/tracing/base.py b/aws_lambda_powertools/tracing/base.py index bd3cf842deb..770789f57f0 100644 --- a/aws_lambda_powertools/tracing/base.py +++ b/aws_lambda_powertools/tracing/base.py @@ -10,13 +10,13 @@ class BaseSegment(abc.ABC): """Holds common properties and methods on segment and subsegment.""" @abc.abstractmethod - def close(self, end_time: Optional[float] = None): + def close(self, end_time: Optional[int] = None): """Close the trace entity by setting `end_time` and flip the in progress flag to False. Parameters ---------- - end_time: float + end_time: int Time in epoch seconds, by default current time will be used. """ diff --git a/aws_lambda_powertools/tracing/tracer.py b/aws_lambda_powertools/tracing/tracer.py index 2c33729fb02..f90789b201c 100644 --- a/aws_lambda_powertools/tracing/tracer.py +++ b/aws_lambda_powertools/tracing/tracer.py @@ -747,7 +747,7 @@ def _add_response_as_metadata( method name to add as metadata key, by default None data : Any, optional data to add as subsegment metadata, by default None - subsegment : BaseSegment, optional + subsegment : BaseSpan, optional existing subsegment to add metadata on, by default None capture_response : bool, optional Do not include response as metadata @@ -771,7 +771,7 @@ def _add_full_exception_as_metadata( method name to add as metadata key, by default None error : Exception error to add as subsegment metadata, by default None - subsegment : BaseSegment + subsegment : BaseSpan existing subsegment to add metadata on, by default None capture_error : bool, optional Do not include error as metadata, by default True