1
1
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
# SPDX-License-Identifier: Apache-2.0
3
3
from typing import Optional , Sequence
4
+ from urllib .parse import urlparse
4
5
5
6
from amazon .opentelemetry .distro .sampler ._matcher import _Matcher , cloud_platform_mapping
6
7
from amazon .opentelemetry .distro .sampler ._sampling_rule import _SamplingRule
7
8
from opentelemetry .context import Context
8
9
from opentelemetry .sdk .resources import Resource
9
10
from opentelemetry .sdk .trace .sampling import ALWAYS_ON , SamplingResult
10
- from opentelemetry .semconv .resource import ResourceAttributes
11
+ from opentelemetry .semconv .resource import CloudPlatformValues , ResourceAttributes
11
12
from opentelemetry .semconv .trace import SpanAttributes
12
13
from opentelemetry .trace import Link , SpanKind
13
14
from opentelemetry .trace .span import TraceState
14
15
from opentelemetry .util .types import Attributes
15
16
16
17
17
- class _Rule :
18
+ class _SamplingRuleApplier :
18
19
def __init__ (self , sampling_rule : _SamplingRule ):
19
20
self .sampling_rule = sampling_rule
20
21
# TODO add self.next_target_fetch_time from maybe time.process_time() or cache's datetime object
@@ -39,48 +40,49 @@ def should_sample(
39
40
)
40
41
41
42
def matches (self , resource : Resource , attributes : Attributes ) -> bool :
42
- http_target = None
43
- http_url = None
44
- http_method = None
45
- http_host = None
43
+ url_path = None
44
+ url_full = None
45
+ http_request_method = None
46
+ server_address = None
46
47
service_name = None
47
48
48
49
if attributes is not None :
49
- http_target = attributes .get (SpanAttributes .HTTP_TARGET , None )
50
- http_method = attributes .get (SpanAttributes .HTTP_METHOD , None )
51
- http_url = attributes .get (SpanAttributes .HTTP_URL , None )
52
- http_host = attributes .get (SpanAttributes .HTTP_HOST , None )
53
- # NOTE: The above span attribute keys are deprecated in favor of:
54
- # URL_PATH/URL_QUERY, HTTP_REQUEST_METHOD, URL_FULL, SERVER_ADDRESS/SERVER_PORT
55
- # For now, the old attribute keys are kept for consistency with other centralized samplers
50
+ url_path = attributes .get (SpanAttributes .URL_PATH , None )
51
+ url_full = attributes .get (SpanAttributes .URL_FULL , None )
52
+ http_request_method = attributes .get (SpanAttributes .HTTP_REQUEST_METHOD , None )
53
+ server_address = attributes .get (SpanAttributes .SERVER_ADDRESS , None )
56
54
57
55
# Resource shouldn't be none as it should default to empty resource
58
56
if resource is not None :
59
57
service_name = resource .attributes .get (ResourceAttributes .SERVICE_NAME , "" )
60
58
61
59
# target may be in url
62
- if http_target is None and http_url is not None :
63
- scheme_end_index = http_url .find ("://" )
64
- # Per spec, http.url is always populated with scheme://host/target. If scheme doesn't
65
- # match, assume it's bad instrumentation and ignore.
60
+ if url_path is None and url_full is not None :
61
+ scheme_end_index = url_full .find ("://" )
62
+ # For network calls, URL usually has `scheme://host[:port][path][?query][#fragment]` format
63
+ # Per spec, http.url is always populated with scheme://host/target.
64
+ # If scheme doesn't match, assume it's bad instrumentation and ignore.
66
65
if scheme_end_index > - 1 :
67
- path_index = http_url .find ("/" , scheme_end_index + len ("://" ))
68
- if path_index == - 1 :
69
- http_target = "/"
70
- else :
71
- http_target = http_url [path_index :]
66
+ # urlparse("scheme://netloc/path;parameters?query#fragment")
67
+ url_path = urlparse (url_full ).path
68
+ if url_path == "" :
69
+ url_path = "/"
70
+ elif url_path is None and url_full is None :
71
+ # When missing, the URL Path is assumed to be /
72
+ url_path = "/"
72
73
73
74
return (
74
75
_Matcher .attribute_match (attributes , self .sampling_rule .Attributes )
75
- and _Matcher .wild_card_match (http_target , self .sampling_rule .URLPath )
76
- and _Matcher .wild_card_match (http_method , self .sampling_rule .HTTPMethod )
77
- and _Matcher .wild_card_match (http_host , self .sampling_rule .Host )
76
+ and _Matcher .wild_card_match (url_path , self .sampling_rule .URLPath )
77
+ and _Matcher .wild_card_match (http_request_method , self .sampling_rule .HTTPMethod )
78
+ and _Matcher .wild_card_match (server_address , self .sampling_rule .Host )
78
79
and _Matcher .wild_card_match (service_name , self .sampling_rule .ServiceName )
79
- and _Matcher .wild_card_match (self .get_service_type (resource ), self .sampling_rule .ServiceType )
80
+ and _Matcher .wild_card_match (self .__get_service_type (resource ), self .sampling_rule .ServiceType )
81
+ and _Matcher .wild_card_match (self .__get_arn (resource , attributes ), self .sampling_rule .ResourceARN )
80
82
)
81
83
82
84
# pylint: disable=no-self-use
83
- def get_service_type (self , resource : Resource ) -> str :
85
+ def __get_service_type (self , resource : Resource ) -> str :
84
86
if resource is None :
85
87
return ""
86
88
@@ -89,3 +91,17 @@ def get_service_type(self, resource: Resource) -> str:
89
91
return ""
90
92
91
93
return cloud_platform_mapping .get (cloud_platform , "" )
94
+
95
+ # pylint: disable=no-self-use
96
+ def __get_arn (self , resource : Resource , attributes : Attributes ) -> str :
97
+ if resource is not None :
98
+ arn = resource .attributes .get (ResourceAttributes .AWS_ECS_CONTAINER_ARN , None )
99
+ if arn is not None :
100
+ return arn
101
+ if attributes is not None and self .__get_service_type (resource = resource ) == cloud_platform_mapping .get (
102
+ CloudPlatformValues .AWS_LAMBDA .value
103
+ ):
104
+ arn = attributes .get (SpanAttributes .CLOUD_RESOURCE_ID , None )
105
+ if arn is not None :
106
+ return arn
107
+ return ""
0 commit comments