AWS X-Ray Remote Sampler Part 1 - Initial Classes and Rules Poller Implementation (#3366)
* remote sampling - initial classes and rules poller * run generate-workflows and ruff * add component owner for aws sampler, run lint * move sampler into aws sdk-extensions * move sampler tests to trace dir, update otel api/sdk deps, update changelog * move mock_clock into tests dir * update component owners for sdk-extension-aws * ruff and lint * address comments * make sampler implementation internal until completion, update tests to not make http requests * remove use of Optional, restore README of the package * remove unused clock and client_id * Update component_owners.yml * Update CHANGELOG.md --------- Co-authored-by: Riccardo Magliocchetti <riccardo.magliocchetti@gmail.com>
This commit is contained in:
parent
973d10d218
commit
032d6c67be
|
|
@ -33,12 +33,11 @@ components:
|
|||
- dorkolog
|
||||
|
||||
propagator/opentelemetry-propagator-aws-xray:
|
||||
- NathanielRN
|
||||
- jj22ee
|
||||
|
||||
sdk-extension/opentelemetry-sdk-extension-aws:
|
||||
- NathanielRN
|
||||
- Kausik-A
|
||||
- srprash
|
||||
- jj22ee
|
||||
|
||||
instrumentation/opentelemetry-instrumentation-tortoiseorm:
|
||||
- tonybaloney
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
([#3685](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3685))
|
||||
- `opentelemetry-instrumentation-system-metrics`: Add `cpython.gc.collected_objects` and `cpython.gc.uncollectable_objects` metrics
|
||||
([#3666](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3666))
|
||||
|
||||
- `opentelemetry-sdk-extension-aws` Add AWS X-Ray Remote Sampler with initial Rules Poller implementation
|
||||
([#3366](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3366))
|
||||
|
||||
## Version 1.36.0/0.57b0 (2025-07-29)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@ classifiers = [
|
|||
"Programming Language :: Python :: 3.13",
|
||||
]
|
||||
dependencies = [
|
||||
"opentelemetry-sdk ~= 1.12",
|
||||
"opentelemetry-api ~= 1.23",
|
||||
"opentelemetry-sdk ~= 1.23",
|
||||
"opentelemetry-instrumentation ~= 0.44b0",
|
||||
"opentelemetry-semantic-conventions ~= 0.44b0",
|
||||
"requests ~= 2.28",
|
||||
]
|
||||
|
||||
[project.entry-points.opentelemetry_id_generator]
|
||||
|
|
@ -38,6 +42,10 @@ aws_eks = "opentelemetry.sdk.extension.aws.resource.eks:AwsEksResourceDetector"
|
|||
aws_elastic_beanstalk = "opentelemetry.sdk.extension.aws.resource.beanstalk:AwsBeanstalkResourceDetector"
|
||||
aws_lambda = "opentelemetry.sdk.extension.aws.resource._lambda:AwsLambdaResourceDetector"
|
||||
|
||||
# TODO: Uncomment this when Sampler implementation is complete
|
||||
# [project.entry-points.opentelemetry_sampler]
|
||||
# aws_xray_remote_sampler = "opentelemetry.sdk.extension.aws.trace.sampler:AwsXRayRemoteSampler"
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/sdk-extension/opentelemetry-sdk-extension-aws"
|
||||
Repository = "https://github.com/open-telemetry/opentelemetry-python-contrib"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler.aws_xray_remote_sampler import (
|
||||
_AwsXRayRemoteSampler,
|
||||
)
|
||||
|
||||
__all__ = ["_AwsXRayRemoteSampler"]
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from logging import getLogger
|
||||
from typing import List
|
||||
|
||||
import requests
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.instrumentation.utils import suppress_instrumentation
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._sampling_rule import (
|
||||
_SamplingRule,
|
||||
)
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._sampling_target import (
|
||||
_SamplingTargetResponse,
|
||||
)
|
||||
|
||||
_logger = getLogger(__name__)
|
||||
DEFAULT_SAMPLING_PROXY_ENDPOINT = "http://127.0.0.1:2000"
|
||||
|
||||
|
||||
class _AwsXRaySamplingClient:
|
||||
def __init__(
|
||||
self,
|
||||
endpoint: str = DEFAULT_SAMPLING_PROXY_ENDPOINT,
|
||||
log_level: str | None = None,
|
||||
):
|
||||
# Override default log level
|
||||
if log_level is not None:
|
||||
_logger.setLevel(log_level)
|
||||
|
||||
self.__get_sampling_rules_endpoint = endpoint + "/GetSamplingRules"
|
||||
self.__get_sampling_targets_endpoint = endpoint + "/SamplingTargets"
|
||||
|
||||
self.__session = requests.Session()
|
||||
|
||||
def get_sampling_rules(self) -> List[_SamplingRule]:
|
||||
sampling_rules: List["_SamplingRule"] = []
|
||||
headers = {"content-type": "application/json"}
|
||||
|
||||
with suppress_instrumentation():
|
||||
try:
|
||||
xray_response = self.__session.post(
|
||||
url=self.__get_sampling_rules_endpoint,
|
||||
headers=headers,
|
||||
timeout=20,
|
||||
)
|
||||
sampling_rules_response = xray_response.json()
|
||||
if (
|
||||
sampling_rules_response is None
|
||||
or "SamplingRuleRecords" not in sampling_rules_response
|
||||
):
|
||||
_logger.error(
|
||||
"SamplingRuleRecords is missing in getSamplingRules response: %s",
|
||||
sampling_rules_response,
|
||||
)
|
||||
return []
|
||||
sampling_rules_records = sampling_rules_response[
|
||||
"SamplingRuleRecords"
|
||||
]
|
||||
for record in sampling_rules_records:
|
||||
if "SamplingRule" not in record:
|
||||
_logger.error(
|
||||
"SamplingRule is missing in SamplingRuleRecord"
|
||||
)
|
||||
else:
|
||||
sampling_rules.append(
|
||||
_SamplingRule(**record["SamplingRule"])
|
||||
)
|
||||
|
||||
except requests.exceptions.RequestException as req_err:
|
||||
_logger.error("Request error occurred: %s", req_err)
|
||||
except json.JSONDecodeError as json_err:
|
||||
_logger.error("Error in decoding JSON response: %s", json_err)
|
||||
# pylint: disable=broad-exception-caught
|
||||
except Exception as err:
|
||||
_logger.error(
|
||||
"Error occurred when attempting to fetch rules: %s", err
|
||||
)
|
||||
|
||||
return sampling_rules
|
||||
|
||||
def get_sampling_targets(
|
||||
self, statistics: List["dict[str, str | float | int]"]
|
||||
) -> _SamplingTargetResponse:
|
||||
sampling_targets_response = _SamplingTargetResponse(
|
||||
LastRuleModification=None,
|
||||
SamplingTargetDocuments=None,
|
||||
UnprocessedStatistics=None,
|
||||
)
|
||||
headers = {"content-type": "application/json"}
|
||||
|
||||
with suppress_instrumentation():
|
||||
try:
|
||||
xray_response = self.__session.post(
|
||||
url=self.__get_sampling_targets_endpoint,
|
||||
headers=headers,
|
||||
timeout=20,
|
||||
json={"SamplingStatisticsDocuments": statistics},
|
||||
)
|
||||
xray_response_json = xray_response.json()
|
||||
if (
|
||||
xray_response_json is None
|
||||
or "SamplingTargetDocuments" not in xray_response_json
|
||||
or "LastRuleModification" not in xray_response_json
|
||||
):
|
||||
_logger.debug(
|
||||
"getSamplingTargets response is invalid. Unable to update targets."
|
||||
)
|
||||
return sampling_targets_response
|
||||
|
||||
sampling_targets_response = _SamplingTargetResponse(
|
||||
**xray_response_json
|
||||
)
|
||||
except requests.exceptions.RequestException as req_err:
|
||||
_logger.debug("Request error occurred: %s", req_err)
|
||||
except json.JSONDecodeError as json_err:
|
||||
_logger.debug("Error in decoding JSON response: %s", json_err)
|
||||
# pylint: disable=broad-exception-caught
|
||||
except Exception as err:
|
||||
_logger.debug(
|
||||
"Error occurred when attempting to fetch targets: %s", err
|
||||
)
|
||||
|
||||
return sampling_targets_response
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
class _Clock:
|
||||
def __init__(self):
|
||||
self.__datetime = datetime.datetime
|
||||
|
||||
def now(self) -> datetime.datetime:
|
||||
return self.__datetime.now()
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
def from_timestamp(self, timestamp: float) -> datetime.datetime:
|
||||
return datetime.datetime.fromtimestamp(timestamp)
|
||||
|
||||
def time_delta(self, seconds: float) -> datetime.timedelta:
|
||||
return datetime.timedelta(seconds=seconds)
|
||||
|
||||
def max(self) -> datetime.datetime:
|
||||
return datetime.datetime.max
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
# Disable snake_case naming style so this class can match the sampling rules response from X-Ray
|
||||
# pylint: disable=invalid-name
|
||||
class _SamplingRule:
|
||||
def __init__(
|
||||
self,
|
||||
Attributes: dict[str, str] | None = None,
|
||||
FixedRate: float | None = None,
|
||||
HTTPMethod: str | None = None,
|
||||
Host: str | None = None,
|
||||
Priority: int | None = None,
|
||||
ReservoirSize: int | None = None,
|
||||
ResourceARN: str | None = None,
|
||||
RuleARN: str | None = None,
|
||||
RuleName: str | None = None,
|
||||
ServiceName: str | None = None,
|
||||
ServiceType: str | None = None,
|
||||
URLPath: str | None = None,
|
||||
Version: int | None = None,
|
||||
):
|
||||
self.Attributes = Attributes if Attributes is not None else {}
|
||||
self.FixedRate = FixedRate if FixedRate is not None else 0.0
|
||||
self.HTTPMethod = HTTPMethod if HTTPMethod is not None else ""
|
||||
self.Host = Host if Host is not None else ""
|
||||
# Default to value with lower priority than default rule
|
||||
self.Priority = Priority if Priority is not None else 10001
|
||||
self.ReservoirSize = ReservoirSize if ReservoirSize is not None else 0
|
||||
self.ResourceARN = ResourceARN if ResourceARN is not None else ""
|
||||
self.RuleARN = RuleARN if RuleARN is not None else ""
|
||||
self.RuleName = RuleName if RuleName is not None else ""
|
||||
self.ServiceName = ServiceName if ServiceName is not None else ""
|
||||
self.ServiceType = ServiceType if ServiceType is not None else ""
|
||||
self.URLPath = URLPath if URLPath is not None else ""
|
||||
self.Version = Version if Version is not None else 0
|
||||
|
||||
def __lt__(self, other: "_SamplingRule") -> bool:
|
||||
if self.Priority == other.Priority:
|
||||
# String order priority example:
|
||||
# "A","Abc","a","ab","abc","abcdef"
|
||||
return self.RuleName < other.RuleName
|
||||
return self.Priority < other.Priority
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, _SamplingRule):
|
||||
return False
|
||||
return (
|
||||
self.FixedRate == other.FixedRate
|
||||
and self.HTTPMethod == other.HTTPMethod
|
||||
and self.Host == other.Host
|
||||
and self.Priority == other.Priority
|
||||
and self.ReservoirSize == other.ReservoirSize
|
||||
and self.ResourceARN == other.ResourceARN
|
||||
and self.RuleARN == other.RuleARN
|
||||
and self.RuleName == other.RuleName
|
||||
and self.ServiceName == other.ServiceName
|
||||
and self.ServiceType == other.ServiceType
|
||||
and self.URLPath == other.URLPath
|
||||
and self.Version == other.Version
|
||||
and self.Attributes == other.Attributes
|
||||
)
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._clock import _Clock
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._sampling_rule import (
|
||||
_SamplingRule,
|
||||
)
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._sampling_statistics_document import (
|
||||
_SamplingStatisticsDocument,
|
||||
)
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._sampling_target import (
|
||||
_SamplingTarget,
|
||||
)
|
||||
|
||||
|
||||
class _SamplingRuleApplier:
|
||||
def __init__(
|
||||
self,
|
||||
sampling_rule: _SamplingRule,
|
||||
client_id: str,
|
||||
clock: _Clock,
|
||||
statistics: _SamplingStatisticsDocument | None = None,
|
||||
target: _SamplingTarget | None = None,
|
||||
):
|
||||
self.__client_id = client_id # pylint: disable=W0238
|
||||
self._clock = clock
|
||||
self.sampling_rule = sampling_rule
|
||||
|
||||
# (TODO) Just store Sampling Rules for now, rest of implementation for later
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._clock import _Clock
|
||||
|
||||
|
||||
# Disable snake_case naming style so this class can match the statistics document response from X-Ray
|
||||
# pylint: disable=invalid-name
|
||||
class _SamplingStatisticsDocument:
|
||||
def __init__(
|
||||
self,
|
||||
clientID: str,
|
||||
ruleName: str,
|
||||
RequestCount: int = 0,
|
||||
BorrowCount: int = 0,
|
||||
SampleCount: int = 0,
|
||||
):
|
||||
self.ClientID = clientID
|
||||
self.RuleName = ruleName
|
||||
self.Timestamp = None
|
||||
|
||||
self.RequestCount = RequestCount
|
||||
self.BorrowCount = BorrowCount
|
||||
self.SampleCount = SampleCount
|
||||
|
||||
def snapshot(self, clock: _Clock):
|
||||
return {
|
||||
"ClientID": self.ClientID,
|
||||
"RuleName": self.RuleName,
|
||||
"Timestamp": clock.now().timestamp(),
|
||||
"RequestCount": self.RequestCount,
|
||||
"BorrowCount": self.BorrowCount,
|
||||
"SampleCount": self.SampleCount,
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from logging import getLogger
|
||||
from typing import List
|
||||
|
||||
_logger = getLogger(__name__)
|
||||
|
||||
|
||||
# Disable snake_case naming style so this class can match the sampling rules response from X-Ray
|
||||
# pylint: disable=invalid-name
|
||||
class _SamplingTarget:
|
||||
def __init__(
|
||||
self,
|
||||
FixedRate: float | None = None,
|
||||
Interval: int | None = None,
|
||||
ReservoirQuota: int | None = None,
|
||||
ReservoirQuotaTTL: float | None = None,
|
||||
RuleName: str | None = None,
|
||||
):
|
||||
self.FixedRate = FixedRate if FixedRate is not None else 0.0
|
||||
self.Interval = Interval # can be None
|
||||
self.ReservoirQuota = ReservoirQuota # can be None
|
||||
self.ReservoirQuotaTTL = ReservoirQuotaTTL # can be None
|
||||
self.RuleName = RuleName if RuleName is not None else ""
|
||||
|
||||
|
||||
class _UnprocessedStatistics:
|
||||
def __init__(
|
||||
self,
|
||||
ErrorCode: str | None = None,
|
||||
Message: str | None = None,
|
||||
RuleName: str | None = None,
|
||||
):
|
||||
self.ErrorCode = ErrorCode if ErrorCode is not None else ""
|
||||
self.Message = Message if ErrorCode is not None else ""
|
||||
self.RuleName = RuleName if ErrorCode is not None else ""
|
||||
|
||||
|
||||
class _SamplingTargetResponse:
|
||||
def __init__(
|
||||
self,
|
||||
LastRuleModification: float | None,
|
||||
SamplingTargetDocuments: List[_SamplingTarget] | None = None,
|
||||
UnprocessedStatistics: List[_UnprocessedStatistics] | None = None,
|
||||
):
|
||||
self.LastRuleModification: float = (
|
||||
LastRuleModification if LastRuleModification is not None else 0.0
|
||||
)
|
||||
|
||||
self.SamplingTargetDocuments: List[_SamplingTarget] = []
|
||||
if SamplingTargetDocuments is not None:
|
||||
for document in SamplingTargetDocuments:
|
||||
try:
|
||||
self.SamplingTargetDocuments.append(
|
||||
_SamplingTarget(**document)
|
||||
)
|
||||
except TypeError as e:
|
||||
_logger.debug("TypeError occurred: %s", e)
|
||||
|
||||
self.UnprocessedStatistics: List[_UnprocessedStatistics] = []
|
||||
if UnprocessedStatistics is not None:
|
||||
for unprocessed in UnprocessedStatistics:
|
||||
try:
|
||||
self.UnprocessedStatistics.append(
|
||||
_UnprocessedStatistics(**unprocessed)
|
||||
)
|
||||
except TypeError as e:
|
||||
_logger.debug("TypeError occurred: %s", e)
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import random
|
||||
from logging import getLogger
|
||||
from threading import Timer
|
||||
from typing import Sequence
|
||||
|
||||
from typing_extensions import override
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.context import Context
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._aws_xray_sampling_client import (
|
||||
DEFAULT_SAMPLING_PROXY_ENDPOINT,
|
||||
_AwsXRaySamplingClient,
|
||||
)
|
||||
from opentelemetry.sdk.resources import Resource
|
||||
from opentelemetry.sdk.trace.sampling import (
|
||||
Decision,
|
||||
ParentBased,
|
||||
Sampler,
|
||||
SamplingResult,
|
||||
)
|
||||
from opentelemetry.trace import Link, SpanKind
|
||||
from opentelemetry.trace.span import TraceState
|
||||
from opentelemetry.util.types import Attributes
|
||||
|
||||
_logger = getLogger(__name__)
|
||||
|
||||
DEFAULT_RULES_POLLING_INTERVAL_SECONDS = 300
|
||||
|
||||
|
||||
# WORK IN PROGRESS
|
||||
# TODO: Rename to AwsXRayRemoteSampler when the implementation is complete and is ready to use
|
||||
#
|
||||
# Wrapper class to ensure that all XRay Sampler Functionality in _InternalAwsXRayRemoteSampler
|
||||
# uses ParentBased logic to respect the parent span's sampling decision
|
||||
class _AwsXRayRemoteSampler(Sampler):
|
||||
def __init__(
|
||||
self,
|
||||
resource: Resource,
|
||||
endpoint: str | None = None,
|
||||
polling_interval: int | None = None,
|
||||
log_level: str | None = None,
|
||||
):
|
||||
self._root = ParentBased(
|
||||
_InternalAwsXRayRemoteSampler(
|
||||
resource=resource,
|
||||
endpoint=endpoint,
|
||||
polling_interval=polling_interval,
|
||||
log_level=log_level,
|
||||
)
|
||||
)
|
||||
|
||||
@override
|
||||
def should_sample(
|
||||
self,
|
||||
parent_context: Context | None,
|
||||
trace_id: int,
|
||||
name: str,
|
||||
kind: SpanKind | None = None,
|
||||
attributes: Attributes | None = None,
|
||||
links: Sequence["Link"] | None = None,
|
||||
trace_state: TraceState | None = None,
|
||||
) -> "SamplingResult":
|
||||
return self._root.should_sample(
|
||||
parent_context,
|
||||
trace_id,
|
||||
name,
|
||||
kind=kind,
|
||||
attributes=attributes,
|
||||
links=links,
|
||||
trace_state=trace_state,
|
||||
)
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
@override
|
||||
def get_description(self) -> str:
|
||||
return f"AwsXRayRemoteSampler{{root:{self._root.get_description()}}}"
|
||||
|
||||
|
||||
# WORK IN PROGRESS
|
||||
#
|
||||
# _InternalAwsXRayRemoteSampler contains all core XRay Sampler Functionality,
|
||||
# however it is NOT Parent-based (e.g. Sample logic runs for each span)
|
||||
# Not intended for external use, use Parent-based `AwsXRayRemoteSampler` instead.
|
||||
class _InternalAwsXRayRemoteSampler(Sampler):
|
||||
"""
|
||||
Remote Sampler for OpenTelemetry that gets sampling configurations from AWS X-Ray
|
||||
|
||||
Args:
|
||||
resource: OpenTelemetry Resource (Required)
|
||||
endpoint: proxy endpoint for AWS X-Ray Sampling (Optional)
|
||||
polling_interval: Polling interval for getSamplingRules call (Optional)
|
||||
log_level: custom log level configuration for remote sampler (Optional)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
resource: Resource,
|
||||
endpoint: str | None = None,
|
||||
polling_interval: int | None = None,
|
||||
log_level: str | None = None,
|
||||
):
|
||||
# Override default log level
|
||||
if log_level is not None:
|
||||
_logger.setLevel(log_level)
|
||||
|
||||
if endpoint is None:
|
||||
_logger.info(
|
||||
"`endpoint` is `None`. Defaulting to %s",
|
||||
DEFAULT_SAMPLING_PROXY_ENDPOINT,
|
||||
)
|
||||
endpoint = DEFAULT_SAMPLING_PROXY_ENDPOINT
|
||||
if polling_interval is None or polling_interval < 10:
|
||||
_logger.info(
|
||||
"`polling_interval` is `None` or too small. Defaulting to %s",
|
||||
DEFAULT_RULES_POLLING_INTERVAL_SECONDS,
|
||||
)
|
||||
polling_interval = DEFAULT_RULES_POLLING_INTERVAL_SECONDS
|
||||
|
||||
self.__xray_client = _AwsXRaySamplingClient(
|
||||
endpoint, log_level=log_level
|
||||
)
|
||||
|
||||
self.__polling_interval = polling_interval
|
||||
self.__rule_polling_jitter = random.uniform(0.0, 5.0)
|
||||
|
||||
if resource is not None:
|
||||
self.__resource = resource # pylint: disable=W0238
|
||||
else:
|
||||
_logger.warning(
|
||||
"OTel Resource provided is `None`. Defaulting to empty resource"
|
||||
)
|
||||
self.__resource = Resource.get_empty() # pylint: disable=W0238
|
||||
|
||||
# Schedule the next rule poll now
|
||||
# Python Timers only run once, so they need to be recreated for every poll
|
||||
self._rules_timer = Timer(0, self.__start_sampling_rule_poller)
|
||||
self._rules_timer.daemon = True # Ensures that when the main thread exits, the Timer threads are killed
|
||||
self._rules_timer.start()
|
||||
|
||||
# (TODO) set up the target poller to go off once after the default interval. Subsequent polls may use new intervals.
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
@override
|
||||
def should_sample(
|
||||
self,
|
||||
parent_context: Context | None,
|
||||
trace_id: int,
|
||||
name: str,
|
||||
kind: SpanKind | None = None,
|
||||
attributes: Attributes | None = None,
|
||||
links: Sequence["Link"] | None = None,
|
||||
trace_state: TraceState | None = None,
|
||||
) -> "SamplingResult":
|
||||
return SamplingResult(
|
||||
decision=Decision.DROP,
|
||||
attributes=attributes,
|
||||
trace_state=trace_state,
|
||||
)
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
@override
|
||||
def get_description(self) -> str:
|
||||
description = (
|
||||
"_InternalAwsXRayRemoteSampler{remote sampling with AWS X-Ray}"
|
||||
)
|
||||
return description
|
||||
|
||||
def __get_and_update_sampling_rules(self) -> None:
|
||||
sampling_rules = self.__xray_client.get_sampling_rules() # pylint: disable=W0612 # noqa: F841
|
||||
# (TODO) update rules cache with sampling rules
|
||||
|
||||
def __start_sampling_rule_poller(self) -> None:
|
||||
self.__get_and_update_sampling_rules()
|
||||
# Schedule the next sampling rule poll
|
||||
self._rules_timer = Timer(
|
||||
self.__polling_interval + self.__rule_polling_jitter,
|
||||
self.__start_sampling_rule_poller,
|
||||
)
|
||||
self._rules_timer.daemon = True
|
||||
self._rules_timer.start()
|
||||
|
|
@ -6,10 +6,12 @@ pluggy==1.5.0
|
|||
py-cpuinfo==9.0.0
|
||||
pytest==7.4.4
|
||||
pytest-benchmark==4.0.0
|
||||
requests==2.32.3
|
||||
tomli==2.0.1
|
||||
typing_extensions==4.12.2
|
||||
wrapt==1.16.0
|
||||
zipp==3.19.2
|
||||
opentelemetry-sdk==1.12 # when updating, also update in pyproject.toml
|
||||
opentelemetry-api==1.23 # when updating, also update in pyproject.toml
|
||||
opentelemetry-sdk==1.23 # when updating, also update in pyproject.toml
|
||||
|
||||
-e sdk-extension/opentelemetry-sdk-extension-aws
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ pluggy==1.5.0
|
|||
py-cpuinfo==9.0.0
|
||||
pytest==7.4.4
|
||||
pytest-benchmark==4.0.0
|
||||
requests==2.32.3
|
||||
tomli==2.0.1
|
||||
typing_extensions==4.12.2
|
||||
wrapt==1.16.0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import datetime
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._clock import _Clock
|
||||
|
||||
|
||||
class MockClock(_Clock):
|
||||
def __init__(self, dt: datetime.datetime = datetime.datetime.now()):
|
||||
self.time_now = dt
|
||||
super()
|
||||
|
||||
def now(self) -> datetime.datetime:
|
||||
return self.time_now
|
||||
|
||||
def add_time(self, seconds: float) -> None:
|
||||
self.time_now += self.time_delta(seconds)
|
||||
|
||||
def set_time(self, dt: datetime.datetime) -> None:
|
||||
self.time_now = dt
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"NextToken": null,
|
||||
"SamplingRuleRecords": [
|
||||
{
|
||||
"CreatedAt": 1.676038494E9,
|
||||
"ModifiedAt": 1.676038494E9,
|
||||
"SamplingRule": {
|
||||
"Attributes": {
|
||||
"foo": "bar",
|
||||
"abc": "1234"
|
||||
},
|
||||
"FixedRate": 0.05,
|
||||
"HTTPMethod": "*",
|
||||
"Host": "*",
|
||||
"Priority": 10000,
|
||||
"ReservoirSize": 100,
|
||||
"ResourceARN": "*",
|
||||
"RuleARN": "arn:aws:xray:us-east-1:999999999999:sampling-rule/Default",
|
||||
"RuleName": "Default",
|
||||
"ServiceName": "*",
|
||||
"ServiceType": "*",
|
||||
"URLPath": "*",
|
||||
"Version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"CreatedAt": 1.67799933E9,
|
||||
"ModifiedAt": 1.67799933E9,
|
||||
"SamplingRule": {
|
||||
"Attributes": {
|
||||
"abc": "1234"
|
||||
},
|
||||
"FixedRate": 0.11,
|
||||
"HTTPMethod": "*",
|
||||
"Host": "*",
|
||||
"Priority": 20,
|
||||
"ReservoirSize": 1,
|
||||
"ResourceARN": "*",
|
||||
"RuleARN": "arn:aws:xray:us-east-1:999999999999:sampling-rule/test",
|
||||
"RuleName": "test",
|
||||
"ServiceName": "*",
|
||||
"ServiceType": "*",
|
||||
"URLPath": "*",
|
||||
"Version": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"NextToken": null,
|
||||
"SamplingRuleRecords": [
|
||||
{
|
||||
"CreatedAt": 1.67799933E9,
|
||||
"ModifiedAt": 1.67799933E9,
|
||||
"SamplingRule": {
|
||||
"Attributes": {
|
||||
"foo": "bar",
|
||||
"doo": "baz"
|
||||
},
|
||||
"FixedRate": 0.05,
|
||||
"HTTPMethod": "*",
|
||||
"Host": "*",
|
||||
"Priority": 1000,
|
||||
"ReservoirSize": 10,
|
||||
"ResourceARN": "*",
|
||||
"RuleARN": "arn:aws:xray:us-west-2:123456789000:sampling-rule/Rule1",
|
||||
"RuleName": "Rule1",
|
||||
"ServiceName": "*",
|
||||
"ServiceType": "AWS::Foo::Bar",
|
||||
"URLPath": "*",
|
||||
"Version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"CreatedAt": 0.0,
|
||||
"ModifiedAt": 1.611564245E9,
|
||||
"SamplingRule": {
|
||||
"Attributes": {},
|
||||
"FixedRate": 0.05,
|
||||
"HTTPMethod": "*",
|
||||
"Host": "*",
|
||||
"Priority": 10000,
|
||||
"ReservoirSize": 1,
|
||||
"ResourceARN": "*",
|
||||
"RuleARN": "arn:aws:xray:us-west-2:123456789000:sampling-rule/Default",
|
||||
"RuleName": "Default",
|
||||
"ServiceName": "*",
|
||||
"ServiceType": "*",
|
||||
"URLPath": "*",
|
||||
"Version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"CreatedAt": 1.676038494E9,
|
||||
"ModifiedAt": 1.676038494E9,
|
||||
"SamplingRule": {
|
||||
"Attributes": {},
|
||||
"FixedRate": 0.2,
|
||||
"HTTPMethod": "GET",
|
||||
"Host": "*",
|
||||
"Priority": 1,
|
||||
"ReservoirSize": 10,
|
||||
"ResourceARN": "*",
|
||||
"RuleARN": "arn:aws:xray:us-west-2:123456789000:sampling-rule/Rule2",
|
||||
"RuleName": "Rule2",
|
||||
"ServiceName": "FooBar",
|
||||
"ServiceType": "*",
|
||||
"URLPath": "/foo/bar",
|
||||
"Version": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"LastRuleModification": 1707551387.0,
|
||||
"SamplingTargetDocuments": [
|
||||
{
|
||||
"FixedRate": 0.10,
|
||||
"Interval": 10,
|
||||
"ReservoirQuota": 30,
|
||||
"ReservoirQuotaTTL": 1707764006.0,
|
||||
"RuleName": "test"
|
||||
},
|
||||
{
|
||||
"FixedRate": 0.05,
|
||||
"Interval": 10,
|
||||
"ReservoirQuota": 0,
|
||||
"ReservoirQuotaTTL": 1707764006.0,
|
||||
"RuleName": "Default"
|
||||
}
|
||||
],
|
||||
"UnprocessedStatistics": []
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"NextToken": null,
|
||||
"SamplingRuleRecords": [
|
||||
{
|
||||
"CreatedAt": 1.676038494E9,
|
||||
"ModifiedAt": 1.676038494E9,
|
||||
"SamplingRule": {
|
||||
"Attributes": {},
|
||||
"FixedRate": 1.0,
|
||||
"HTTPMethod": "*",
|
||||
"Host": "*",
|
||||
"Priority": 10000,
|
||||
"ReservoirSize": 0,
|
||||
"ResourceARN": "*",
|
||||
"RuleARN": "arn:aws:xray:us-east-1:999999999999:sampling-rule/Default",
|
||||
"RuleName": "Default",
|
||||
"ServiceName": "*",
|
||||
"ServiceType": "*",
|
||||
"URLPath": "*",
|
||||
"Version": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"CreatedAt": 1.67799933E9,
|
||||
"ModifiedAt": 1.67799933E9,
|
||||
"SamplingRule": {
|
||||
"Attributes": {
|
||||
"abc": "1234"
|
||||
},
|
||||
"FixedRate": 0,
|
||||
"HTTPMethod": "*",
|
||||
"Host": "*",
|
||||
"Priority": 20,
|
||||
"ReservoirSize": 0,
|
||||
"ResourceARN": "*",
|
||||
"RuleARN": "arn:aws:xray:us-east-1:999999999999:sampling-rule/test",
|
||||
"RuleName": "test",
|
||||
"ServiceName": "*",
|
||||
"ServiceType": "*",
|
||||
"URLPath": "*",
|
||||
"Version": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"LastRuleModification": 1707551387.0,
|
||||
"SamplingTargetDocuments": [
|
||||
{
|
||||
"FixedRate": 0.0,
|
||||
"Interval": 100000,
|
||||
"ReservoirQuota": 100000,
|
||||
"ReservoirQuotaTTL": 9999999999.0,
|
||||
"RuleName": "test"
|
||||
},
|
||||
{
|
||||
"FixedRate": 0.0,
|
||||
"Interval": 1000,
|
||||
"ReservoirQuota": 100,
|
||||
"ReservoirQuotaTTL": 9999999999.0,
|
||||
"RuleName": "Default"
|
||||
}
|
||||
],
|
||||
"UnprocessedStatistics": []
|
||||
}
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import json
|
||||
import os
|
||||
from logging import DEBUG
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler.aws_xray_remote_sampler import (
|
||||
_AwsXRayRemoteSampler,
|
||||
)
|
||||
from opentelemetry.sdk.resources import Resource
|
||||
from opentelemetry.sdk.trace.sampling import Decision
|
||||
|
||||
TEST_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
DATA_DIR = os.path.join(TEST_DIR, "data")
|
||||
|
||||
|
||||
def create_spans(
|
||||
sampled_array, thread_id, span_attributes, remote_sampler, number_of_spans
|
||||
):
|
||||
sampled = 0
|
||||
for _ in range(0, number_of_spans):
|
||||
if (
|
||||
remote_sampler.should_sample(
|
||||
None, 0, "name", attributes=span_attributes
|
||||
).decision
|
||||
!= Decision.DROP
|
||||
):
|
||||
sampled += 1
|
||||
sampled_array[thread_id] = sampled
|
||||
|
||||
|
||||
def mocked_requests_get(*args, **kwargs):
|
||||
class MockResponse:
|
||||
def __init__(self, json_data, status_code):
|
||||
self.json_data = json_data
|
||||
self.status_code = status_code
|
||||
|
||||
def json(self):
|
||||
return self.json_data
|
||||
|
||||
if kwargs["url"] == "http://127.0.0.1:2000/GetSamplingRules":
|
||||
with open(
|
||||
f"{DATA_DIR}/test-remote-sampler_sampling-rules-response-sample.json",
|
||||
encoding="UTF-8",
|
||||
) as file:
|
||||
sample_response = json.load(file)
|
||||
file.close()
|
||||
return MockResponse(sample_response, 200)
|
||||
if kwargs["url"] == "http://127.0.0.1:2000/SamplingTargets":
|
||||
with open(
|
||||
f"{DATA_DIR}/test-remote-sampler_sampling-targets-response-sample.json",
|
||||
encoding="UTF-8",
|
||||
) as file:
|
||||
sample_response = json.load(file)
|
||||
file.close()
|
||||
return MockResponse(sample_response, 200)
|
||||
return MockResponse(None, 404)
|
||||
|
||||
|
||||
class TestAwsXRayRemoteSampler(TestCase):
|
||||
def setUp(self):
|
||||
self.rs = None
|
||||
|
||||
def tearDown(self):
|
||||
# Clean up timers
|
||||
if self.rs is not None:
|
||||
self.rs._root._root._rules_timer.cancel()
|
||||
|
||||
@patch(
|
||||
"opentelemetry.sdk.extension.aws.trace.sampler._aws_xray_sampling_client._AwsXRaySamplingClient.get_sampling_rules",
|
||||
return_value=None,
|
||||
)
|
||||
def test_create_remote_sampler_with_empty_resource(
|
||||
self, mocked_get_sampling_rules
|
||||
):
|
||||
self.rs = _AwsXRayRemoteSampler(resource=Resource.get_empty())
|
||||
self.assertIsNotNone(self.rs._root._root._rules_timer)
|
||||
self.assertEqual(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__polling_interval,
|
||||
300,
|
||||
)
|
||||
self.assertIsNotNone(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__xray_client
|
||||
)
|
||||
self.assertIsNotNone(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__resource
|
||||
)
|
||||
|
||||
@patch(
|
||||
"opentelemetry.sdk.extension.aws.trace.sampler._aws_xray_sampling_client._AwsXRaySamplingClient.get_sampling_rules",
|
||||
return_value=None,
|
||||
)
|
||||
def test_create_remote_sampler_with_populated_resource(
|
||||
self, mocked_get_sampling_rules
|
||||
):
|
||||
self.rs = _AwsXRayRemoteSampler(
|
||||
resource=Resource.create(
|
||||
{
|
||||
"service.name": "test-service-name",
|
||||
"cloud.platform": "test-cloud-platform",
|
||||
}
|
||||
)
|
||||
)
|
||||
self.assertIsNotNone(self.rs._root._root._rules_timer)
|
||||
self.assertEqual(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__polling_interval,
|
||||
300,
|
||||
)
|
||||
self.assertIsNotNone(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__xray_client
|
||||
)
|
||||
self.assertIsNotNone(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__resource
|
||||
)
|
||||
self.assertEqual(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__resource.attributes[
|
||||
"service.name"
|
||||
],
|
||||
"test-service-name",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__resource.attributes[
|
||||
"cloud.platform"
|
||||
],
|
||||
"test-cloud-platform",
|
||||
)
|
||||
|
||||
@patch(
|
||||
"opentelemetry.sdk.extension.aws.trace.sampler._aws_xray_sampling_client._AwsXRaySamplingClient.get_sampling_rules",
|
||||
return_value=None,
|
||||
)
|
||||
def test_create_remote_sampler_with_all_fields_populated(
|
||||
self, mocked_get_sampling_rules
|
||||
):
|
||||
self.rs = _AwsXRayRemoteSampler(
|
||||
resource=Resource.create(
|
||||
{
|
||||
"service.name": "test-service-name",
|
||||
"cloud.platform": "test-cloud-platform",
|
||||
}
|
||||
),
|
||||
endpoint="http://abc.com",
|
||||
polling_interval=120,
|
||||
log_level=DEBUG,
|
||||
)
|
||||
self.assertIsNotNone(self.rs._root._root._rules_timer)
|
||||
self.assertEqual(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__polling_interval,
|
||||
120,
|
||||
)
|
||||
self.assertIsNotNone(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__xray_client
|
||||
)
|
||||
self.assertIsNotNone(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__resource
|
||||
)
|
||||
self.assertEqual(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__xray_client._AwsXRaySamplingClient__get_sampling_rules_endpoint,
|
||||
"http://abc.com/GetSamplingRules",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__resource.attributes[
|
||||
"service.name"
|
||||
],
|
||||
"test-service-name",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.rs._root._root._InternalAwsXRayRemoteSampler__resource.attributes[
|
||||
"cloud.platform"
|
||||
],
|
||||
"test-cloud-platform",
|
||||
)
|
||||
|
||||
@patch(
|
||||
"opentelemetry.sdk.extension.aws.trace.sampler._aws_xray_sampling_client._AwsXRaySamplingClient.get_sampling_rules",
|
||||
return_value=None,
|
||||
)
|
||||
def test_get_description(self, mocked_get_sampling_rules) -> str:
|
||||
self.rs: _AwsXRayRemoteSampler = _AwsXRayRemoteSampler(
|
||||
resource=Resource.create({"service.name": "dummy_name"})
|
||||
)
|
||||
self.assertEqual(
|
||||
self.rs.get_description(),
|
||||
"AwsXRayRemoteSampler{root:ParentBased{root:_InternalAwsXRayRemoteSampler{remote sampling with AWS X-Ray},remoteParentSampled:AlwaysOnSampler,remoteParentNotSampled:AlwaysOffSampler,localParentSampled:AlwaysOnSampler,localParentNotSampled:AlwaysOffSampler}}", # noqa: E501
|
||||
)
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import json
|
||||
import os
|
||||
from logging import getLogger
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._aws_xray_sampling_client import (
|
||||
_AwsXRaySamplingClient,
|
||||
)
|
||||
|
||||
SAMPLING_CLIENT_LOGGER_NAME = (
|
||||
"opentelemetry.sdk.extension.aws.trace.sampler._aws_xray_sampling_client"
|
||||
)
|
||||
_sampling_client_logger = getLogger(SAMPLING_CLIENT_LOGGER_NAME)
|
||||
|
||||
TEST_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
DATA_DIR = os.path.join(TEST_DIR, "data")
|
||||
|
||||
|
||||
class TestAwsXRaySamplingClient(TestCase):
|
||||
@patch("requests.Session.post")
|
||||
def test_get_no_sampling_rules(self, mock_post=None):
|
||||
mock_post.return_value.configure_mock(
|
||||
**{"json.return_value": {"SamplingRuleRecords": []}}
|
||||
)
|
||||
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
|
||||
sampling_rules = client.get_sampling_rules()
|
||||
self.assertTrue(len(sampling_rules) == 0)
|
||||
|
||||
@patch("requests.Session.post")
|
||||
def test_get_invalid_responses(self, mock_post=None):
|
||||
mock_post.return_value.configure_mock(**{"json.return_value": {}})
|
||||
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
|
||||
with self.assertLogs(_sampling_client_logger, level="ERROR"):
|
||||
sampling_rules = client.get_sampling_rules()
|
||||
self.assertTrue(len(sampling_rules) == 0)
|
||||
|
||||
@patch("requests.Session.post")
|
||||
def test_get_sampling_rule_missing_in_records(self, mock_post=None):
|
||||
mock_post.return_value.configure_mock(
|
||||
**{"json.return_value": {"SamplingRuleRecords": [{}]}}
|
||||
)
|
||||
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
|
||||
with self.assertLogs(_sampling_client_logger, level="ERROR"):
|
||||
sampling_rules = client.get_sampling_rules()
|
||||
self.assertTrue(len(sampling_rules) == 0)
|
||||
|
||||
@patch("requests.Session.post")
|
||||
def test_default_values_used_when_missing_properties_in_sampling_rule(
|
||||
self, mock_post=None
|
||||
):
|
||||
mock_post.return_value.configure_mock(
|
||||
**{
|
||||
"json.return_value": {
|
||||
"SamplingRuleRecords": [{"SamplingRule": {}}]
|
||||
}
|
||||
}
|
||||
)
|
||||
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
|
||||
sampling_rules = client.get_sampling_rules()
|
||||
self.assertTrue(len(sampling_rules) == 1)
|
||||
|
||||
sampling_rule = sampling_rules[0]
|
||||
self.assertEqual(sampling_rule.Attributes, {})
|
||||
self.assertEqual(sampling_rule.FixedRate, 0.0)
|
||||
self.assertEqual(sampling_rule.HTTPMethod, "")
|
||||
self.assertEqual(sampling_rule.Host, "")
|
||||
self.assertEqual(sampling_rule.Priority, 10001)
|
||||
self.assertEqual(sampling_rule.ReservoirSize, 0)
|
||||
self.assertEqual(sampling_rule.ResourceARN, "")
|
||||
self.assertEqual(sampling_rule.RuleARN, "")
|
||||
self.assertEqual(sampling_rule.RuleName, "")
|
||||
self.assertEqual(sampling_rule.ServiceName, "")
|
||||
self.assertEqual(sampling_rule.ServiceType, "")
|
||||
self.assertEqual(sampling_rule.URLPath, "")
|
||||
self.assertEqual(sampling_rule.Version, 0)
|
||||
|
||||
@patch("requests.Session.post")
|
||||
def test_get_correct_number_of_sampling_rules(self, mock_post=None):
|
||||
sampling_records = []
|
||||
with open(
|
||||
f"{DATA_DIR}/get-sampling-rules-response-sample.json",
|
||||
encoding="UTF-8",
|
||||
) as file:
|
||||
sample_response = json.load(file)
|
||||
sampling_records = sample_response["SamplingRuleRecords"]
|
||||
mock_post.return_value.configure_mock(
|
||||
**{"json.return_value": sample_response}
|
||||
)
|
||||
file.close()
|
||||
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
|
||||
sampling_rules = client.get_sampling_rules()
|
||||
self.assertEqual(len(sampling_rules), 3)
|
||||
self.assertEqual(len(sampling_rules), len(sampling_records))
|
||||
self.validate_match_sampling_rules_properties_with_records(
|
||||
sampling_rules, sampling_records
|
||||
)
|
||||
|
||||
def validate_match_sampling_rules_properties_with_records(
|
||||
self, sampling_rules, sampling_records
|
||||
):
|
||||
for _, (sampling_rule, sampling_record) in enumerate(
|
||||
zip(sampling_rules, sampling_records)
|
||||
):
|
||||
self.assertIsNotNone(sampling_rule.Attributes)
|
||||
self.assertEqual(
|
||||
sampling_rule.Attributes,
|
||||
sampling_record["SamplingRule"]["Attributes"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.FixedRate)
|
||||
self.assertEqual(
|
||||
sampling_rule.FixedRate,
|
||||
sampling_record["SamplingRule"]["FixedRate"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.HTTPMethod)
|
||||
self.assertEqual(
|
||||
sampling_rule.HTTPMethod,
|
||||
sampling_record["SamplingRule"]["HTTPMethod"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.Host)
|
||||
self.assertEqual(
|
||||
sampling_rule.Host, sampling_record["SamplingRule"]["Host"]
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.Priority)
|
||||
self.assertEqual(
|
||||
sampling_rule.Priority,
|
||||
sampling_record["SamplingRule"]["Priority"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.ReservoirSize)
|
||||
self.assertEqual(
|
||||
sampling_rule.ReservoirSize,
|
||||
sampling_record["SamplingRule"]["ReservoirSize"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.ResourceARN)
|
||||
self.assertEqual(
|
||||
sampling_rule.ResourceARN,
|
||||
sampling_record["SamplingRule"]["ResourceARN"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.RuleARN)
|
||||
self.assertEqual(
|
||||
sampling_rule.RuleARN,
|
||||
sampling_record["SamplingRule"]["RuleARN"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.RuleName)
|
||||
self.assertEqual(
|
||||
sampling_rule.RuleName,
|
||||
sampling_record["SamplingRule"]["RuleName"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.ServiceName)
|
||||
self.assertEqual(
|
||||
sampling_rule.ServiceName,
|
||||
sampling_record["SamplingRule"]["ServiceName"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.ServiceType)
|
||||
self.assertEqual(
|
||||
sampling_rule.ServiceType,
|
||||
sampling_record["SamplingRule"]["ServiceType"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.URLPath)
|
||||
self.assertEqual(
|
||||
sampling_rule.URLPath,
|
||||
sampling_record["SamplingRule"]["URLPath"],
|
||||
)
|
||||
self.assertIsNotNone(sampling_rule.Version)
|
||||
self.assertEqual(
|
||||
sampling_rule.Version,
|
||||
sampling_record["SamplingRule"]["Version"],
|
||||
)
|
||||
|
||||
@patch("requests.Session.post")
|
||||
def test_get_sampling_targets(self, mock_post=None):
|
||||
with open(
|
||||
f"{DATA_DIR}/get-sampling-targets-response-sample.json",
|
||||
encoding="UTF-8",
|
||||
) as file:
|
||||
sample_response = json.load(file)
|
||||
mock_post.return_value.configure_mock(
|
||||
**{"json.return_value": sample_response}
|
||||
)
|
||||
file.close()
|
||||
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
|
||||
sampling_targets_response = client.get_sampling_targets(statistics=[])
|
||||
self.assertEqual(
|
||||
len(sampling_targets_response.SamplingTargetDocuments), 2
|
||||
)
|
||||
self.assertEqual(
|
||||
len(sampling_targets_response.UnprocessedStatistics), 0
|
||||
)
|
||||
self.assertEqual(
|
||||
sampling_targets_response.LastRuleModification, 1707551387.0
|
||||
)
|
||||
|
||||
@patch("requests.Session.post")
|
||||
def test_get_invalid_sampling_targets(self, mock_post=None):
|
||||
mock_post.return_value.configure_mock(
|
||||
**{
|
||||
"json.return_value": {
|
||||
"LastRuleModification": None,
|
||||
"SamplingTargetDocuments": None,
|
||||
"UnprocessedStatistics": None,
|
||||
}
|
||||
}
|
||||
)
|
||||
client = _AwsXRaySamplingClient("http://127.0.0.1:2000")
|
||||
sampling_targets_response = client.get_sampling_targets(statistics=[])
|
||||
self.assertEqual(sampling_targets_response.SamplingTargetDocuments, [])
|
||||
self.assertEqual(sampling_targets_response.UnprocessedStatistics, [])
|
||||
self.assertEqual(sampling_targets_response.LastRuleModification, 0.0)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._clock import _Clock
|
||||
|
||||
|
||||
class TestClock(TestCase):
|
||||
def test_from_timestamp(self):
|
||||
pass
|
||||
|
||||
def test_time_delta(self):
|
||||
clock = _Clock()
|
||||
dt = clock.from_timestamp(1707551387.0)
|
||||
delta = clock.time_delta(3600)
|
||||
new_dt = dt + delta
|
||||
self.assertTrue(new_dt.timestamp() - dt.timestamp() == 3600)
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._sampling_rule import (
|
||||
_SamplingRule,
|
||||
)
|
||||
|
||||
|
||||
class TestSamplingRule(TestCase):
|
||||
def test_sampling_rule_ordering(self):
|
||||
rule1 = _SamplingRule(Priority=1, RuleName="abcdef", Version=1)
|
||||
rule2 = _SamplingRule(Priority=100, RuleName="A", Version=1)
|
||||
rule3 = _SamplingRule(Priority=100, RuleName="Abc", Version=1)
|
||||
rule4 = _SamplingRule(Priority=100, RuleName="ab", Version=1)
|
||||
rule5 = _SamplingRule(Priority=100, RuleName="abc", Version=1)
|
||||
rule6 = _SamplingRule(Priority=200, RuleName="abcdef", Version=1)
|
||||
|
||||
self.assertTrue(rule1 < rule2 < rule3 < rule4 < rule5 < rule6)
|
||||
|
||||
def test_sampling_rule_equality(self):
|
||||
sampling_rule = _SamplingRule(
|
||||
Attributes={"abc": "123", "def": "4?6", "ghi": "*89"},
|
||||
FixedRate=0.11,
|
||||
HTTPMethod="GET",
|
||||
Host="localhost",
|
||||
Priority=20,
|
||||
ReservoirSize=1,
|
||||
ResourceARN="*",
|
||||
RuleARN="arn:aws:xray:us-east-1:999999999999:sampling-rule/test",
|
||||
RuleName="test",
|
||||
ServiceName="myServiceName",
|
||||
ServiceType="AWS::EKS::Container",
|
||||
URLPath="/helloworld",
|
||||
Version=1,
|
||||
)
|
||||
|
||||
sampling_rule_attr_unordered = _SamplingRule(
|
||||
Attributes={"ghi": "*89", "abc": "123", "def": "4?6"},
|
||||
FixedRate=0.11,
|
||||
HTTPMethod="GET",
|
||||
Host="localhost",
|
||||
Priority=20,
|
||||
ReservoirSize=1,
|
||||
ResourceARN="*",
|
||||
RuleARN="arn:aws:xray:us-east-1:999999999999:sampling-rule/test",
|
||||
RuleName="test",
|
||||
ServiceName="myServiceName",
|
||||
ServiceType="AWS::EKS::Container",
|
||||
URLPath="/helloworld",
|
||||
Version=1,
|
||||
)
|
||||
|
||||
self.assertTrue(sampling_rule == sampling_rule_attr_unordered)
|
||||
|
||||
sampling_rule_updated = _SamplingRule(
|
||||
Attributes={"ghi": "*89", "abc": "123", "def": "4?6"},
|
||||
FixedRate=0.11,
|
||||
HTTPMethod="GET",
|
||||
Host="localhost",
|
||||
Priority=20,
|
||||
ReservoirSize=1,
|
||||
ResourceARN="*",
|
||||
RuleARN="arn:aws:xray:us-east-1:999999999999:sampling-rule/test",
|
||||
RuleName="test",
|
||||
ServiceName="myServiceName",
|
||||
ServiceType="AWS::EKS::Container",
|
||||
URLPath="/helloworld_new",
|
||||
Version=1,
|
||||
)
|
||||
|
||||
sampling_rule_updated_2 = _SamplingRule(
|
||||
Attributes={"abc": "128", "def": "4?6", "ghi": "*89"},
|
||||
FixedRate=0.11,
|
||||
HTTPMethod="GET",
|
||||
Host="localhost",
|
||||
Priority=20,
|
||||
ReservoirSize=1,
|
||||
ResourceARN="*",
|
||||
RuleARN="arn:aws:xray:us-east-1:999999999999:sampling-rule/test",
|
||||
RuleName="test",
|
||||
ServiceName="myServiceName",
|
||||
ServiceType="AWS::EKS::Container",
|
||||
URLPath="/helloworld",
|
||||
Version=1,
|
||||
)
|
||||
|
||||
self.assertFalse(sampling_rule == sampling_rule_updated)
|
||||
self.assertFalse(sampling_rule == sampling_rule_updated_2)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
from unittest import TestCase
|
||||
|
||||
TEST_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
DATA_DIR = os.path.join(TEST_DIR, "data")
|
||||
|
||||
CLIENT_ID = "12345678901234567890abcd"
|
||||
|
||||
|
||||
# pylint: disable=no-member
|
||||
class TestSamplingRuleApplier(TestCase):
|
||||
pass
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import datetime
|
||||
from unittest import TestCase
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._sampling_statistics_document import (
|
||||
_SamplingStatisticsDocument,
|
||||
)
|
||||
|
||||
from ._mock_clock import MockClock
|
||||
|
||||
|
||||
class TestSamplingStatisticsDocument(TestCase):
|
||||
def test_sampling_statistics_document_inputs(self):
|
||||
statistics = _SamplingStatisticsDocument("", "")
|
||||
self.assertEqual(statistics.ClientID, "")
|
||||
self.assertEqual(statistics.RuleName, "")
|
||||
self.assertEqual(statistics.BorrowCount, 0)
|
||||
self.assertEqual(statistics.SampleCount, 0)
|
||||
self.assertEqual(statistics.RequestCount, 0)
|
||||
|
||||
statistics = _SamplingStatisticsDocument(
|
||||
"client_id", "rule_name", 1, 2, 3
|
||||
)
|
||||
self.assertEqual(statistics.ClientID, "client_id")
|
||||
self.assertEqual(statistics.RuleName, "rule_name")
|
||||
self.assertEqual(statistics.RequestCount, 1)
|
||||
self.assertEqual(statistics.BorrowCount, 2)
|
||||
self.assertEqual(statistics.SampleCount, 3)
|
||||
|
||||
clock = MockClock(datetime.datetime.fromtimestamp(1707551387.0))
|
||||
snapshot = statistics.snapshot(clock)
|
||||
self.assertEqual(snapshot.get("ClientID"), "client_id")
|
||||
self.assertEqual(snapshot.get("RuleName"), "rule_name")
|
||||
self.assertEqual(snapshot.get("Timestamp"), 1707551387.0)
|
||||
self.assertEqual(snapshot.get("RequestCount"), 1)
|
||||
self.assertEqual(snapshot.get("BorrowCount"), 2)
|
||||
self.assertEqual(snapshot.get("SampleCount"), 3)
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Includes work from:
|
||||
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
# pylint: disable=no-name-in-module
|
||||
from opentelemetry.sdk.extension.aws.trace.sampler._sampling_target import (
|
||||
_SamplingTargetResponse,
|
||||
)
|
||||
|
||||
|
||||
class TestSamplingTarget(TestCase):
|
||||
def test_sampling_target_response_with_none_inputs(self):
|
||||
target_response = _SamplingTargetResponse(None, None, None)
|
||||
self.assertEqual(target_response.LastRuleModification, 0.0)
|
||||
self.assertEqual(target_response.SamplingTargetDocuments, [])
|
||||
self.assertEqual(target_response.UnprocessedStatistics, [])
|
||||
|
||||
def test_sampling_target_response_with_invalid_inputs(self):
|
||||
target_response = _SamplingTargetResponse(1.0, [{}], [{}])
|
||||
self.assertEqual(target_response.LastRuleModification, 1.0)
|
||||
self.assertEqual(len(target_response.SamplingTargetDocuments), 1)
|
||||
self.assertEqual(
|
||||
target_response.SamplingTargetDocuments[0].FixedRate, 0
|
||||
)
|
||||
self.assertEqual(
|
||||
target_response.SamplingTargetDocuments[0].Interval, None
|
||||
)
|
||||
self.assertEqual(
|
||||
target_response.SamplingTargetDocuments[0].ReservoirQuota, None
|
||||
)
|
||||
self.assertEqual(
|
||||
target_response.SamplingTargetDocuments[0].ReservoirQuotaTTL, None
|
||||
)
|
||||
self.assertEqual(
|
||||
target_response.SamplingTargetDocuments[0].RuleName, ""
|
||||
)
|
||||
|
||||
self.assertEqual(len(target_response.UnprocessedStatistics), 1)
|
||||
self.assertEqual(
|
||||
target_response.UnprocessedStatistics[0].ErrorCode, ""
|
||||
)
|
||||
self.assertEqual(target_response.UnprocessedStatistics[0].Message, "")
|
||||
self.assertEqual(target_response.UnprocessedStatistics[0].RuleName, "")
|
||||
|
||||
target_response = _SamplingTargetResponse(
|
||||
1.0, [{"foo": "bar"}], [{"dog": "cat"}]
|
||||
)
|
||||
self.assertEqual(len(target_response.SamplingTargetDocuments), 0)
|
||||
self.assertEqual(len(target_response.UnprocessedStatistics), 0)
|
||||
Loading…
Reference in New Issue