aws-advanced-python-wrapper 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CONTRIBUTING.md +63 -0
- aws_advanced_python_wrapper/__init__.py +28 -0
- aws_advanced_python_wrapper/aurora_connection_tracker_plugin.py +228 -0
- aws_advanced_python_wrapper/aurora_initial_connection_strategy_plugin.py +240 -0
- aws_advanced_python_wrapper/aws_secrets_manager_plugin.py +218 -0
- aws_advanced_python_wrapper/connect_time_plugin.py +69 -0
- aws_advanced_python_wrapper/connection_provider.py +232 -0
- aws_advanced_python_wrapper/database_dialect.py +708 -0
- aws_advanced_python_wrapper/default_plugin.py +144 -0
- aws_advanced_python_wrapper/developer_plugin.py +163 -0
- aws_advanced_python_wrapper/driver_configuration_profiles.py +44 -0
- aws_advanced_python_wrapper/driver_dialect.py +165 -0
- aws_advanced_python_wrapper/driver_dialect_codes.py +19 -0
- aws_advanced_python_wrapper/driver_dialect_manager.py +121 -0
- aws_advanced_python_wrapper/driver_info.py +18 -0
- aws_advanced_python_wrapper/errors.py +47 -0
- aws_advanced_python_wrapper/exception_handling.py +73 -0
- aws_advanced_python_wrapper/execute_time_plugin.py +58 -0
- aws_advanced_python_wrapper/failover_plugin.py +517 -0
- aws_advanced_python_wrapper/failover_result.py +42 -0
- aws_advanced_python_wrapper/fastest_response_strategy_plugin.py +345 -0
- aws_advanced_python_wrapper/federated_plugin.py +382 -0
- aws_advanced_python_wrapper/host_availability.py +86 -0
- aws_advanced_python_wrapper/host_list_provider.py +645 -0
- aws_advanced_python_wrapper/host_monitoring_plugin.py +728 -0
- aws_advanced_python_wrapper/host_selector.py +190 -0
- aws_advanced_python_wrapper/hostinfo.py +138 -0
- aws_advanced_python_wrapper/iam_plugin.py +195 -0
- aws_advanced_python_wrapper/mysql_driver_dialect.py +175 -0
- aws_advanced_python_wrapper/pep249.py +196 -0
- aws_advanced_python_wrapper/pg_driver_dialect.py +176 -0
- aws_advanced_python_wrapper/plugin.py +148 -0
- aws_advanced_python_wrapper/plugin_service.py +949 -0
- aws_advanced_python_wrapper/read_write_splitting_plugin.py +363 -0
- aws_advanced_python_wrapper/reader_failover_handler.py +252 -0
- aws_advanced_python_wrapper/resources/aws_advanced_python_wrapper_messages.properties +315 -0
- aws_advanced_python_wrapper/sql_alchemy_connection_provider.py +196 -0
- aws_advanced_python_wrapper/sqlalchemy_driver_dialect.py +127 -0
- aws_advanced_python_wrapper/stale_dns_plugin.py +209 -0
- aws_advanced_python_wrapper/states/__init__.py +13 -0
- aws_advanced_python_wrapper/states/session_state.py +94 -0
- aws_advanced_python_wrapper/states/session_state_service.py +221 -0
- aws_advanced_python_wrapper/utils/__init__.py +13 -0
- aws_advanced_python_wrapper/utils/atomic.py +51 -0
- aws_advanced_python_wrapper/utils/cache_map.py +99 -0
- aws_advanced_python_wrapper/utils/concurrent.py +100 -0
- aws_advanced_python_wrapper/utils/decorators.py +70 -0
- aws_advanced_python_wrapper/utils/failover_mode.py +39 -0
- aws_advanced_python_wrapper/utils/iamutils.py +75 -0
- aws_advanced_python_wrapper/utils/log.py +75 -0
- aws_advanced_python_wrapper/utils/messages.py +36 -0
- aws_advanced_python_wrapper/utils/mysql_exception_handler.py +73 -0
- aws_advanced_python_wrapper/utils/notifications.py +37 -0
- aws_advanced_python_wrapper/utils/pg_exception_handler.py +115 -0
- aws_advanced_python_wrapper/utils/properties.py +492 -0
- aws_advanced_python_wrapper/utils/rds_url_type.py +36 -0
- aws_advanced_python_wrapper/utils/rdsutils.py +226 -0
- aws_advanced_python_wrapper/utils/sliding_expiration_cache.py +146 -0
- aws_advanced_python_wrapper/utils/telemetry/default_telemetry_factory.py +82 -0
- aws_advanced_python_wrapper/utils/telemetry/null_telemetry.py +55 -0
- aws_advanced_python_wrapper/utils/telemetry/open_telemetry.py +189 -0
- aws_advanced_python_wrapper/utils/telemetry/telemetry.py +85 -0
- aws_advanced_python_wrapper/utils/telemetry/xray_telemetry.py +126 -0
- aws_advanced_python_wrapper/utils/utils.py +89 -0
- aws_advanced_python_wrapper/wrapper.py +322 -0
- aws_advanced_python_wrapper/writer_failover_handler.py +347 -0
- aws_advanced_python_wrapper-1.0.0.dist-info/LICENSE +201 -0
- aws_advanced_python_wrapper-1.0.0.dist-info/METADATA +261 -0
- aws_advanced_python_wrapper-1.0.0.dist-info/RECORD +70 -0
- aws_advanced_python_wrapper-1.0.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
|
4
|
+
# You may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import TYPE_CHECKING, Callable, Optional, Sequence, Union
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from opentelemetry.util.types import AttributeValue
|
|
21
|
+
|
|
22
|
+
from opentelemetry import context as context_api
|
|
23
|
+
from opentelemetry import trace
|
|
24
|
+
from opentelemetry import trace as trace_api
|
|
25
|
+
from opentelemetry.metrics import (CallbackOptions, Meter, Observation,
|
|
26
|
+
get_meter)
|
|
27
|
+
from opentelemetry.sdk.trace import ReadableSpan, Span, StatusCode, Tracer
|
|
28
|
+
|
|
29
|
+
from aws_advanced_python_wrapper.utils.log import Logger
|
|
30
|
+
from aws_advanced_python_wrapper.utils.messages import Messages
|
|
31
|
+
from aws_advanced_python_wrapper.utils.telemetry.telemetry import (
|
|
32
|
+
TelemetryConst, TelemetryContext, TelemetryCounter, TelemetryFactory,
|
|
33
|
+
TelemetryGauge, TelemetryTraceLevel)
|
|
34
|
+
|
|
35
|
+
logger = Logger(__name__)
|
|
36
|
+
|
|
37
|
+
INSTRUMENTATION_NAME = "aws-advanced-python-wrapper"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class OpenTelemetryContext(TelemetryContext):
|
|
41
|
+
def __init__(self, tracer: Tracer, name: str, trace_level: TelemetryTraceLevel,
|
|
42
|
+
start_time: Optional[int] = None, link_span: Optional[Span] = None):
|
|
43
|
+
self._name = name
|
|
44
|
+
self._tracer = tracer
|
|
45
|
+
self._span: Optional[Span]
|
|
46
|
+
self._meter: Meter
|
|
47
|
+
self._token: Optional[object] = None
|
|
48
|
+
|
|
49
|
+
current_span: Span = trace.get_current_span() # type: ignore
|
|
50
|
+
is_root = (current_span is None or current_span == trace.INVALID_SPAN)
|
|
51
|
+
|
|
52
|
+
if is_root and trace_level == TelemetryTraceLevel.NESTED:
|
|
53
|
+
trace_level = TelemetryTraceLevel.TOP_LEVEL
|
|
54
|
+
|
|
55
|
+
links: Sequence[trace_api.Link] = ()
|
|
56
|
+
if trace_level in [TelemetryTraceLevel.FORCE_TOP_LEVEL, TelemetryTraceLevel.TOP_LEVEL]:
|
|
57
|
+
if link_span is not None:
|
|
58
|
+
links = [trace.Link(link_span.get_span_context())]
|
|
59
|
+
else:
|
|
60
|
+
if not is_root:
|
|
61
|
+
links = [trace.Link(current_span.get_span_context())]
|
|
62
|
+
|
|
63
|
+
self._span = self._tracer.start_span(self._name, context=context_api.Context(),
|
|
64
|
+
links=links, start_time=start_time) # type: ignore
|
|
65
|
+
if not is_root:
|
|
66
|
+
self.set_attribute(TelemetryConst.TRACE_NAME_ANNOTATION, self._name)
|
|
67
|
+
|
|
68
|
+
ctx = trace.set_span_in_context(self._span) # type: ignore
|
|
69
|
+
self._token = context_api.attach(ctx)
|
|
70
|
+
logger.debug("OpenTelemetryContext.TelemetryTraceID", self._name,
|
|
71
|
+
self._span.get_span_context().trace_id) # type: ignore
|
|
72
|
+
|
|
73
|
+
elif trace_level == TelemetryTraceLevel.NESTED:
|
|
74
|
+
if link_span is not None:
|
|
75
|
+
links = [trace.Link(link_span.get_span_context())]
|
|
76
|
+
|
|
77
|
+
self._span = self._tracer.start_span(self._name, links=links, start_time=start_time) # type: ignore
|
|
78
|
+
ctx = trace.set_span_in_context(self._span) # type: ignore
|
|
79
|
+
self._token = context_api.attach(ctx)
|
|
80
|
+
self.set_attribute(TelemetryConst.TRACE_NAME_ANNOTATION, self._name)
|
|
81
|
+
|
|
82
|
+
elif trace_level == TelemetryTraceLevel.NO_TRACE:
|
|
83
|
+
self._span = None
|
|
84
|
+
|
|
85
|
+
def set_success(self, success: bool):
|
|
86
|
+
if self._span is not None:
|
|
87
|
+
self._span.set_status(StatusCode.OK if success else StatusCode.ERROR)
|
|
88
|
+
|
|
89
|
+
def set_attribute(self, key: str, value: AttributeValue):
|
|
90
|
+
if self._span is not None:
|
|
91
|
+
self._span.set_attribute(key, value)
|
|
92
|
+
|
|
93
|
+
def set_exception(self, exception: Exception):
|
|
94
|
+
if self._span is not None and exception is not None:
|
|
95
|
+
self._span.set_attribute(TelemetryConst.EXCEPTION_TYPE_ANNOTATION, exception.__class__.__name__)
|
|
96
|
+
self._span.set_attribute(TelemetryConst.EXCEPTION_MESSAGE_ANNOTATION, str(exception))
|
|
97
|
+
self._span.record_exception(exception)
|
|
98
|
+
|
|
99
|
+
def get_name(self) -> str:
|
|
100
|
+
return self._name
|
|
101
|
+
|
|
102
|
+
def close_context(self):
|
|
103
|
+
if self._token is not None:
|
|
104
|
+
context_api.detach(self._token)
|
|
105
|
+
if self._span is not None:
|
|
106
|
+
self._span.end()
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def tracer(self) -> Tracer:
|
|
110
|
+
return self._tracer
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def span(self) -> Optional[Span]:
|
|
114
|
+
return self._span
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def post_copy(context: OpenTelemetryContext, trace_level: TelemetryTraceLevel):
|
|
118
|
+
if trace_level == TelemetryTraceLevel.NO_TRACE:
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
_clone_and_close_context(context, trace_level)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _clone_and_close_context(context: OpenTelemetryContext, trace_level: TelemetryTraceLevel) -> OpenTelemetryContext:
|
|
125
|
+
if not isinstance(context.span, ReadableSpan):
|
|
126
|
+
raise RuntimeError(Messages.get("OpenTelemetry.InvalidContext"))
|
|
127
|
+
|
|
128
|
+
clone = OpenTelemetryContext(
|
|
129
|
+
context.tracer, TelemetryConst.COPY_TRACE_NAME_PREFIX + context.get_name(),
|
|
130
|
+
trace_level, context.span.start_time, context.span)
|
|
131
|
+
|
|
132
|
+
for key in context.span.attributes: # type: ignore
|
|
133
|
+
value = context.span.attributes[key] # type: ignore
|
|
134
|
+
clone.set_attribute(key, value)
|
|
135
|
+
|
|
136
|
+
clone.span.set_status(context.span.status) # type: ignore
|
|
137
|
+
clone.set_attribute(TelemetryConst.SOURCE_TRACE_ANNOTATION, str(context.span.get_span_context().trace_id))
|
|
138
|
+
clone.span.end(context.span.end_time) # type: ignore
|
|
139
|
+
|
|
140
|
+
return clone
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class OpenTelemetryCounter(TelemetryCounter):
|
|
144
|
+
def __init__(self, meter: Meter, name: str):
|
|
145
|
+
self._meter: Meter = meter
|
|
146
|
+
self._name: str = name
|
|
147
|
+
self._counter = meter.create_up_down_counter(self._name, unit="1")
|
|
148
|
+
|
|
149
|
+
def add(self, value):
|
|
150
|
+
self._counter.add(value)
|
|
151
|
+
|
|
152
|
+
def inc(self):
|
|
153
|
+
self._counter.add(1)
|
|
154
|
+
|
|
155
|
+
def get_name(self):
|
|
156
|
+
return self._name
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class OpenTelemetryGauge(TelemetryGauge):
|
|
160
|
+
def __init__(self, meter: Meter, name: str, callback: Callable[[], Union[float, int]]):
|
|
161
|
+
self._meter: Meter = meter
|
|
162
|
+
self.name: str = name
|
|
163
|
+
self._counter = meter.create_observable_up_down_counter(name, callbacks=[self._callback_observation], unit="1")
|
|
164
|
+
self._callback: Callable[[], float] = callback
|
|
165
|
+
|
|
166
|
+
def get_name(self):
|
|
167
|
+
return self.name
|
|
168
|
+
|
|
169
|
+
def _callback_observation(self, options: CallbackOptions):
|
|
170
|
+
value: Union[int, float] = self._callback()
|
|
171
|
+
observation = Observation(value)
|
|
172
|
+
yield observation
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class OpenTelemetryFactory(TelemetryFactory):
|
|
176
|
+
def open_telemetry_context(self, name: str, trace_level: TelemetryTraceLevel) -> TelemetryContext:
|
|
177
|
+
return OpenTelemetryContext(trace.get_tracer(INSTRUMENTATION_NAME), name, trace_level) # type: ignore
|
|
178
|
+
|
|
179
|
+
def post_copy(self, context: TelemetryContext, trace_level: TelemetryTraceLevel):
|
|
180
|
+
if isinstance(context, OpenTelemetryContext):
|
|
181
|
+
post_copy(context, trace_level)
|
|
182
|
+
else:
|
|
183
|
+
raise RuntimeError(Messages.get_formatted("OpenTelemetryFactory.WrongParameterType", type(context)))
|
|
184
|
+
|
|
185
|
+
def create_counter(self, name: str) -> TelemetryCounter:
|
|
186
|
+
return OpenTelemetryCounter(get_meter(INSTRUMENTATION_NAME), name)
|
|
187
|
+
|
|
188
|
+
def create_gauge(self, name: str, callback: Callable[[], Union[float, int]]):
|
|
189
|
+
return OpenTelemetryGauge(get_meter(INSTRUMENTATION_NAME), name, callback)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
|
4
|
+
# You may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from abc import ABC, abstractmethod
|
|
18
|
+
from enum import Enum, auto
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from opentelemetry.util.types import AttributeValue
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TelemetryConst:
|
|
26
|
+
TRACE_NAME_ANNOTATION = "trace_name"
|
|
27
|
+
SOURCE_TRACE_ANNOTATION = "source_trace_id"
|
|
28
|
+
PARENT_TRACE_ANNOTATION = "parent_trace_id"
|
|
29
|
+
EXCEPTION_TYPE_ANNOTATION = "exception_type"
|
|
30
|
+
EXCEPTION_MESSAGE_ANNOTATION = "exception_message"
|
|
31
|
+
COPY_TRACE_NAME_PREFIX = "copy: "
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class TelemetryTraceLevel(Enum):
|
|
35
|
+
FORCE_TOP_LEVEL = auto()
|
|
36
|
+
TOP_LEVEL = auto()
|
|
37
|
+
NESTED = auto()
|
|
38
|
+
NO_TRACE = auto()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TelemetryContext(ABC):
|
|
42
|
+
def set_success(self, success: bool):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
def set_attribute(self, key: str, value: AttributeValue):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
def set_exception(self, exception: Exception):
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
def get_name(self):
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
def close_context(self):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class TelemetryCounter(ABC):
|
|
59
|
+
def add(self, value):
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
def inc(self):
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class TelemetryGauge(ABC):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TelemetryFactory(ABC):
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def open_telemetry_context(self, name: str, trace_level: TelemetryTraceLevel) -> TelemetryContext:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
@abstractmethod
|
|
76
|
+
def post_copy(self, context: TelemetryContext, trace_level: TelemetryTraceLevel):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def create_counter(self, name: str) -> TelemetryCounter:
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def create_gauge(self, name: str, callback):
|
|
85
|
+
pass
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
|
4
|
+
# You may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
18
|
+
from typing import TYPE_CHECKING
|
|
19
|
+
|
|
20
|
+
from aws_xray_sdk.core import xray_recorder
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from opentelemetry.util.types import AttributeValue
|
|
24
|
+
from aws_xray_sdk.core.models.segment import Segment
|
|
25
|
+
from aws_xray_sdk.core.models.subsegment import Subsegment
|
|
26
|
+
|
|
27
|
+
from aws_advanced_python_wrapper.utils.log import Logger
|
|
28
|
+
from aws_advanced_python_wrapper.utils.messages import Messages
|
|
29
|
+
from aws_advanced_python_wrapper.utils.telemetry.telemetry import (
|
|
30
|
+
TelemetryConst, TelemetryContext, TelemetryCounter, TelemetryFactory,
|
|
31
|
+
TelemetryTraceLevel)
|
|
32
|
+
|
|
33
|
+
logger = Logger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class XRayTelemetryContext(TelemetryContext):
|
|
37
|
+
def __init__(self, name: str, trace_level: TelemetryTraceLevel):
|
|
38
|
+
self._name: str = name
|
|
39
|
+
self._trace_entity: Segment | Subsegment
|
|
40
|
+
|
|
41
|
+
if trace_level in [TelemetryTraceLevel.FORCE_TOP_LEVEL, TelemetryTraceLevel.TOP_LEVEL]:
|
|
42
|
+
self._trace_entity = xray_recorder.begin_segment(self._name)
|
|
43
|
+
self.is_segment = True
|
|
44
|
+
logger.debug("XRayTelemetryContext.TraceID", self._name, self._trace_entity.trace_id)
|
|
45
|
+
elif trace_level == TelemetryTraceLevel.NESTED:
|
|
46
|
+
self._trace_entity = xray_recorder.begin_subsegment(self._name) # type: ignore
|
|
47
|
+
self.set_attribute(TelemetryConst.TRACE_NAME_ANNOTATION, self._name)
|
|
48
|
+
self.is_segment = False
|
|
49
|
+
elif trace_level == TelemetryTraceLevel.NO_TRACE:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
def set_success(self, success: bool):
|
|
53
|
+
if self._trace_entity is not None:
|
|
54
|
+
self._trace_entity.error = not success
|
|
55
|
+
|
|
56
|
+
def set_attribute(self, key: str, value: AttributeValue):
|
|
57
|
+
if self._trace_entity is not None:
|
|
58
|
+
self._trace_entity.put_annotation(key, value)
|
|
59
|
+
|
|
60
|
+
def set_exception(self, exception: Exception):
|
|
61
|
+
if self._trace_entity is not None and exception is not None:
|
|
62
|
+
self._trace_entity.put_annotation("exception_type", exception.__class__.__name__)
|
|
63
|
+
self._trace_entity.put_annotation("exception_message", str(exception))
|
|
64
|
+
|
|
65
|
+
def get_name(self):
|
|
66
|
+
return self._name
|
|
67
|
+
|
|
68
|
+
def close_context(self, end_time=None):
|
|
69
|
+
if self._trace_entity is not None:
|
|
70
|
+
if self.is_segment:
|
|
71
|
+
xray_recorder.end_segment(end_time)
|
|
72
|
+
else:
|
|
73
|
+
xray_recorder.end_subsegment(end_time)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def post_copy(context: XRayTelemetryContext, trace_level: TelemetryTraceLevel):
|
|
77
|
+
if trace_level == TelemetryTraceLevel.NO_TRACE:
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
if trace_level in [TelemetryTraceLevel.FORCE_TOP_LEVEL, TelemetryTraceLevel.TOP_LEVEL]:
|
|
81
|
+
with ThreadPoolExecutor() as executor:
|
|
82
|
+
future = executor.submit(_clone_and_close_context, context, trace_level)
|
|
83
|
+
future.result()
|
|
84
|
+
else:
|
|
85
|
+
_clone_and_close_context(context, trace_level)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _clone_and_close_context(context: XRayTelemetryContext, trace_level: TelemetryTraceLevel) -> XRayTelemetryContext:
|
|
89
|
+
clone = XRayTelemetryContext(TelemetryConst.COPY_TRACE_NAME_PREFIX + context.get_name(), trace_level)
|
|
90
|
+
|
|
91
|
+
clone._trace_entity.start_time = context._trace_entity.start_time
|
|
92
|
+
|
|
93
|
+
for key in context._trace_entity.annotations.items():
|
|
94
|
+
value = context._trace_entity.annotations[key]
|
|
95
|
+
if key != TelemetryConst.TRACE_NAME_ANNOTATION and value is not None:
|
|
96
|
+
clone.set_attribute(key, value)
|
|
97
|
+
|
|
98
|
+
if context.is_segment and context._trace_entity.error:
|
|
99
|
+
clone._trace_entity.add_error_flag()
|
|
100
|
+
|
|
101
|
+
clone.set_attribute(TelemetryConst.SOURCE_TRACE_ANNOTATION, str(context._trace_entity.trace_id))
|
|
102
|
+
|
|
103
|
+
if context._trace_entity.parent_id is not None:
|
|
104
|
+
if trace_level == TelemetryTraceLevel.NESTED:
|
|
105
|
+
clone._trace_entity.parent_id = context._trace_entity.parent_id
|
|
106
|
+
|
|
107
|
+
clone.close_context(context._trace_entity.end_time)
|
|
108
|
+
|
|
109
|
+
return clone
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class XRayTelemetryFactory(TelemetryFactory):
|
|
113
|
+
def open_telemetry_context(self, name: str, trace_level: TelemetryTraceLevel) -> TelemetryContext:
|
|
114
|
+
return XRayTelemetryContext(name, trace_level)
|
|
115
|
+
|
|
116
|
+
def post_copy(self, context: TelemetryContext, trace_level: TelemetryTraceLevel):
|
|
117
|
+
if isinstance(context, XRayTelemetryContext):
|
|
118
|
+
post_copy(context, trace_level)
|
|
119
|
+
else:
|
|
120
|
+
raise RuntimeError(Messages.get_formatted("XRayTelemetryFactory.WrongParameterType", type(context)))
|
|
121
|
+
|
|
122
|
+
def create_counter(self, name: str) -> TelemetryCounter:
|
|
123
|
+
raise RuntimeError(Messages.get_formatted("XRayTelemetryFactory.MetricsNotSupported"))
|
|
124
|
+
|
|
125
|
+
def create_gauge(self, name: str, callback):
|
|
126
|
+
raise RuntimeError(Messages.get_formatted("XRayTelemetryFactory.MetricsNotSupported"))
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License").
|
|
4
|
+
# You may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import sys
|
|
18
|
+
from importlib import import_module
|
|
19
|
+
from logging import DEBUG, Formatter, Logger, StreamHandler
|
|
20
|
+
from queue import Empty, Queue
|
|
21
|
+
from typing import TYPE_CHECKING, Optional, Tuple
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from aws_advanced_python_wrapper.hostinfo import HostInfo
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class LogUtils:
|
|
28
|
+
@staticmethod
|
|
29
|
+
def setup_logger(logger: Logger, level: int = DEBUG, format_string: Optional[str] = None):
|
|
30
|
+
for handler in logger.handlers:
|
|
31
|
+
if isinstance(handler, StreamHandler):
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
if format_string is None:
|
|
35
|
+
format_string = \
|
|
36
|
+
"%(asctime)s.%(msecs)03d %(name)-12s:%(funcName)s [%(levelname)-8s] - %(threadName)s - %(message)s"
|
|
37
|
+
|
|
38
|
+
handler = StreamHandler(stream=sys.stdout)
|
|
39
|
+
handler.setFormatter(Formatter(format_string))
|
|
40
|
+
handler.setLevel(level)
|
|
41
|
+
|
|
42
|
+
logger.setLevel(level)
|
|
43
|
+
logger.addHandler(handler)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def log_topology(hosts: Tuple[HostInfo, ...], message_prefix: Optional[str] = None):
|
|
47
|
+
"""Returns a formatted string of the topology that looks like the following
|
|
48
|
+
Topology: {
|
|
49
|
+
HostInfo[host=url, port=123]
|
|
50
|
+
<null>
|
|
51
|
+
HostInfo[host=url1, port=234]}
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
hosts (list): a list of hosts representing the cluster topology
|
|
55
|
+
message_prefix (str): messages to prefix to the topology information
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
_type_: a formatted string of the topology
|
|
59
|
+
"""
|
|
60
|
+
msg = "\n\t".join(["<null>" if not host else str(host) for host in hosts])
|
|
61
|
+
prefix = "" if not message_prefix else message_prefix + " "
|
|
62
|
+
return prefix + f": {{\n\t{msg}}}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class QueueUtils:
|
|
66
|
+
@staticmethod
|
|
67
|
+
def get(q: Queue):
|
|
68
|
+
try:
|
|
69
|
+
return q.get_nowait()
|
|
70
|
+
except Empty:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def clear(q: Queue):
|
|
75
|
+
with q.mutex:
|
|
76
|
+
q.queue.clear()
|
|
77
|
+
q.all_tasks_done.notify_all()
|
|
78
|
+
q.unfinished_tasks = 0
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class Utils:
|
|
82
|
+
@staticmethod
|
|
83
|
+
def initialize_class(full_class_name: str, *args):
|
|
84
|
+
try:
|
|
85
|
+
parts = full_class_name.split('.')
|
|
86
|
+
m = import_module(".".join(parts[:-1]))
|
|
87
|
+
return getattr(m, parts[-1])(*args)
|
|
88
|
+
except ModuleNotFoundError:
|
|
89
|
+
return None
|