odigos-opentelemetry-python 1.0.78__tar.gz → 1.0.79__tar.gz
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.
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/PKG-INFO +1 -1
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/components.py +17 -7
- odigos_opentelemetry_python-1.0.79/initializer/odigos_sampler.py +168 -0
- odigos_opentelemetry_python-1.0.79/initializer/version.py +1 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/odigos_opentelemetry_python.egg-info/PKG-INFO +1 -1
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/odigos_opentelemetry_python.egg-info/SOURCES.txt +0 -1
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/http_client.py +1 -2
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/pyproject.toml +1 -1
- odigos_opentelemetry_python-1.0.78/initializer/odigos_sampler.py +0 -98
- odigos_opentelemetry_python-1.0.78/initializer/sampling_operators.py +0 -7
- odigos_opentelemetry_python-1.0.78/initializer/version.py +0 -1
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/LICENSE +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/README.md +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/__init__.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/distro/__init__.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/distro/distro.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/distro/instrumentation_registry.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/envs.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/exit_hook.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/lib_handling.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/post_instrument.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/process_resource.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/odigos_opentelemetry_python.egg-info/dependency_links.txt +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/odigos_opentelemetry_python.egg-info/entry_points.txt +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/odigos_opentelemetry_python.egg-info/requires.txt +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/odigos_opentelemetry_python.egg-info/top_level.txt +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/__init__.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/anyvalue_pb2.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/config.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/health_status.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/opamp_pb2.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/opamp_registry.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/utils.py +0 -0
- {odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/setup.cfg +0 -0
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/components.py
RENAMED
|
@@ -53,6 +53,8 @@ from opamp.http_client import OpAMPHTTPClient, MockOpAMPClient
|
|
|
53
53
|
|
|
54
54
|
MINIMUM_PYTHON_SUPPORTED_VERSION = (3, 9)
|
|
55
55
|
|
|
56
|
+
_odigos_sampler = None
|
|
57
|
+
|
|
56
58
|
|
|
57
59
|
def initialize_components(trace_exporters = False, span_processor = None):
|
|
58
60
|
# Ensure each child process after fork gets a fresh OpAMP client
|
|
@@ -118,12 +120,14 @@ def initialize_components(trace_exporters = False, span_processor = None):
|
|
|
118
120
|
reload_distro_modules()
|
|
119
121
|
|
|
120
122
|
def initialize_traces_if_enabled(trace_exporters, resource, span_processor = None, signals = None, initial_sampler_config = None):
|
|
123
|
+
global _odigos_sampler
|
|
121
124
|
# In case collectors signals support receiving traces, initialize the tracer provider with the exporters
|
|
122
125
|
traces_enabled = signals.get("traceSignal", False)
|
|
123
126
|
|
|
124
127
|
if traces_enabled:
|
|
125
128
|
|
|
126
129
|
odigos_sampler = OdigosSampler()
|
|
130
|
+
_odigos_sampler = odigos_sampler
|
|
127
131
|
sampler = ParentBased(odigos_sampler)
|
|
128
132
|
|
|
129
133
|
# Apply initial sampler config if provided (from OpAMP first message)
|
|
@@ -250,10 +254,8 @@ def update_agent_config(conf: Config):
|
|
|
250
254
|
if 'traces' in conf.sample_config and 'headSampling' in conf.sample_config['traces']:
|
|
251
255
|
head_sampling_config = conf.sample_config['traces']['headSampling']
|
|
252
256
|
|
|
253
|
-
# Determine if we have valid sampling config (rules or fallback fraction)
|
|
254
257
|
if head_sampling_config and (
|
|
255
|
-
head_sampling_config.get('
|
|
256
|
-
head_sampling_config.get('fallbackFraction') is not None
|
|
258
|
+
head_sampling_config.get('noisyOperations') is not None
|
|
257
259
|
):
|
|
258
260
|
valid_config = head_sampling_config
|
|
259
261
|
else:
|
|
@@ -370,11 +372,19 @@ class ProxyResource(Resource):
|
|
|
370
372
|
|
|
371
373
|
def _after_in_child():
|
|
372
374
|
# This hook runs in every child process after fork().
|
|
373
|
-
# It does
|
|
374
|
-
# 1. Marks ProxyResource as "dirty"
|
|
375
|
-
# the process.* attributes are recomputed with the child’s PID.
|
|
375
|
+
# It does three things:
|
|
376
|
+
# 1. Marks ProxyResource as "dirty" so process.* attributes are recomputed.
|
|
376
377
|
# 2. Restarts the OpAMP client (threads/sockets do not survive fork).
|
|
378
|
+
# 3. Propagates the sampler to the new client so config updates reach it.
|
|
379
|
+
# Without this, the forked client's sampler is None and config updates
|
|
380
|
+
# from OpAMP heartbeats are silently dropped.
|
|
377
381
|
provider = get_tracer_provider()
|
|
378
382
|
if isinstance(getattr(provider, "resource", None), ProxyResource):
|
|
379
383
|
provider.resource._child_refreshed = False
|
|
380
|
-
start_opamp_client(OpampConnectionEvent())
|
|
384
|
+
client = start_opamp_client(OpampConnectionEvent())
|
|
385
|
+
|
|
386
|
+
if _odigos_sampler is not None:
|
|
387
|
+
client.sampler = _odigos_sampler
|
|
388
|
+
initial_config = client.get_initial_sampler_config()
|
|
389
|
+
if initial_config is not None:
|
|
390
|
+
_odigos_sampler.update_config(initial_config)
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from opentelemetry.semconv.attributes import http_attributes as http_attributes_semconv
|
|
2
|
+
from opentelemetry.semconv.attributes import server_attributes as server_attributes_semconv
|
|
3
|
+
from opentelemetry.sdk.trace.sampling import Sampler, Decision, SamplingResult, _get_parent_trace_state
|
|
4
|
+
from threading import Lock
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Setup the Sampler logger
|
|
9
|
+
sampler_logger = logging.getLogger('odigos')
|
|
10
|
+
|
|
11
|
+
class OdigosSampler(Sampler):
|
|
12
|
+
|
|
13
|
+
# For compatibility with 64 bit trace IDs, the sampler checks the 64
|
|
14
|
+
# low-order bits of the trace ID to decide whether to sample a given trace.
|
|
15
|
+
TRACE_ID_LIMIT = (1 << 64) - 1
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self._lock = Lock()
|
|
19
|
+
self._config = None
|
|
20
|
+
|
|
21
|
+
def _trace_id_based_sampling(self, trace_id: int, percentage_to_sample: float) -> bool:
|
|
22
|
+
"""Mimic OpenTelemetry's trace ID-based sampling logic with 64-bit range."""
|
|
23
|
+
# Since percentage to sample is in percents and we need a fraction, divide by 100
|
|
24
|
+
fraction = percentage_to_sample / 100
|
|
25
|
+
|
|
26
|
+
# Calculate the bound based on the fraction using OpenTelemetry's approach
|
|
27
|
+
bound = round(fraction * (self.TRACE_ID_LIMIT + 1))
|
|
28
|
+
# Apply the TRACE_ID_LIMIT mask to ensure 64-bit range and compare it to the bound
|
|
29
|
+
return (trace_id & self.TRACE_ID_LIMIT) < bound
|
|
30
|
+
|
|
31
|
+
def should_sample(self, parent_context, trace_id, name, kind, attributes, links):
|
|
32
|
+
attributes = attributes or {}
|
|
33
|
+
with self._lock:
|
|
34
|
+
# sampler_logger.debug(f'Running Should_sample a span with the following attributes: {attributes}')
|
|
35
|
+
|
|
36
|
+
if self._config is None:
|
|
37
|
+
# sampler_logger.debug('No configuration is set, returning RECORD_AND_SAMPLE')
|
|
38
|
+
return SamplingResult(Decision.RECORD_AND_SAMPLE, attributes=attributes, trace_state=_get_parent_trace_state(parent_context))
|
|
39
|
+
|
|
40
|
+
noisy_operations = self._config.get('noisyOperations', [])
|
|
41
|
+
lowest_percentage = None
|
|
42
|
+
|
|
43
|
+
for noisy_operation in noisy_operations:
|
|
44
|
+
if noisy_operation.get("disabled"):
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
percentage_to_sample = noisy_operation.get("percentageAtMost", 0.0)
|
|
48
|
+
|
|
49
|
+
http_operation_sample = noisy_operation.get("operation")
|
|
50
|
+
if not http_operation_sample:
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
# sampler_logger.debug(f'Evaluating noisy operation: {noisy_operation}')
|
|
54
|
+
|
|
55
|
+
matched = False
|
|
56
|
+
|
|
57
|
+
if http_server_sample := http_operation_sample.get("httpServer"):
|
|
58
|
+
matched = self._match_http_server_sample_rule(http_server_sample, attributes) or matched
|
|
59
|
+
|
|
60
|
+
if http_client_sample := http_operation_sample.get("httpClient"):
|
|
61
|
+
matched = self._match_http_client_sample_rule(http_client_sample, attributes) or matched
|
|
62
|
+
|
|
63
|
+
# sampler_logger.debug(f'Noisy operation matched: {matched}, percentage: {percentage_to_sample}')
|
|
64
|
+
if matched: # For matched operations, compute the minimal lowest percentage
|
|
65
|
+
if lowest_percentage is None:
|
|
66
|
+
lowest_percentage = percentage_to_sample
|
|
67
|
+
else:
|
|
68
|
+
lowest_percentage = min(lowest_percentage, percentage_to_sample)
|
|
69
|
+
|
|
70
|
+
if lowest_percentage is not None:
|
|
71
|
+
if self._trace_id_based_sampling(trace_id, lowest_percentage):
|
|
72
|
+
# sampler_logger.debug(f'Trace [{trace_id}] is sampled with lowest percentage {lowest_percentage}')
|
|
73
|
+
return SamplingResult(Decision.RECORD_AND_SAMPLE, attributes=attributes, trace_state=_get_parent_trace_state(parent_context))
|
|
74
|
+
else:
|
|
75
|
+
# sampler_logger.debug(f'Trace [{trace_id}] is dropped with lowest percentage {lowest_percentage}')
|
|
76
|
+
return SamplingResult(Decision.DROP)
|
|
77
|
+
|
|
78
|
+
# sampler_logger.debug(f'No noisy operation matched, sampling the trace')
|
|
79
|
+
return SamplingResult(Decision.RECORD_AND_SAMPLE, attributes=attributes, trace_state=_get_parent_trace_state(parent_context))
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def _get_attribute(span_attributes: dict[str, str], *keys: str):
|
|
83
|
+
"""Return the first non-None value found for the given attribute keys."""
|
|
84
|
+
for key in keys:
|
|
85
|
+
value = span_attributes.get(key)
|
|
86
|
+
if value is not None:
|
|
87
|
+
return value
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
def _match_http_server_sample_rule(self, http_server_rule: dict[str, str], span_attributes: dict[str, str]) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
Try to match all the http server's attributes to the definitions of the noisy action
|
|
93
|
+
"""
|
|
94
|
+
span_route = span_attributes.get(http_attributes_semconv.HTTP_ROUTE)
|
|
95
|
+
# Old semconv: "http.method" → new semconv: "http.request.method"
|
|
96
|
+
span_method = self._get_attribute(span_attributes, http_attributes_semconv.HTTP_REQUEST_METHOD, "http.method")
|
|
97
|
+
|
|
98
|
+
# Some instrumentations send target instead of route
|
|
99
|
+
target = self._get_attribute(span_attributes, "http.target", "url.path")
|
|
100
|
+
|
|
101
|
+
if http_server_rule.get("route") and http_server_rule["route"] != span_route:
|
|
102
|
+
# If we have route in the span attributes, evaluate it first (use template matching
|
|
103
|
+
# since param names may differ, e.g. /grml/{grml_id} vs /grml/{path_param})
|
|
104
|
+
if span_route:
|
|
105
|
+
return self._match_rule_route_to_span(http_server_rule["route"], span_route)
|
|
106
|
+
# If we have target, try to match it with the route in the sampling rule
|
|
107
|
+
if target:
|
|
108
|
+
return self._match_rule_route_to_span(http_server_rule["route"], target)
|
|
109
|
+
|
|
110
|
+
if http_server_rule.get("routePrefix") and not (span_route or "").startswith(http_server_rule["routePrefix"]):
|
|
111
|
+
return False
|
|
112
|
+
if http_server_rule.get("method") and http_server_rule["method"] != span_method:
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
def _match_http_client_sample_rule(self, http_client_rule: dict[str, str], span_attributes: dict[str, str]) -> bool:
|
|
118
|
+
"""
|
|
119
|
+
Try to match all the http client's attributes to the definitions of the noisy action
|
|
120
|
+
"""
|
|
121
|
+
# Old semconv: "net.peer.name" / "http.host" → new semconv: "server.address"
|
|
122
|
+
server_address = self._get_attribute(span_attributes, server_attributes_semconv.SERVER_ADDRESS, "net.peer.name", "http.host")
|
|
123
|
+
url_template = span_attributes.get("url.template")
|
|
124
|
+
# Old semconv: "http.method" → new semconv: "http.request.method"
|
|
125
|
+
method = self._get_attribute(span_attributes, http_attributes_semconv.HTTP_REQUEST_METHOD, "http.method")
|
|
126
|
+
|
|
127
|
+
if http_client_rule.get("serverAddress") and http_client_rule["serverAddress"] != server_address:
|
|
128
|
+
return False
|
|
129
|
+
if http_client_rule.get("templatedPath") and http_client_rule["templatedPath"] != url_template:
|
|
130
|
+
return False
|
|
131
|
+
if http_client_rule.get("templatedPathPrefix") and not (url_template or "").startswith(http_client_rule["templatedPathPrefix"]):
|
|
132
|
+
return False
|
|
133
|
+
if http_client_rule.get("method") and http_client_rule["method"] != method:
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
return True
|
|
137
|
+
|
|
138
|
+
def _match_rule_route_to_span(self, rule_route: str, span_value: str) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
Match a sampling rule route (e.g. /item/{id}) against a span value, which can be either
|
|
141
|
+
a templated route (e.g. /item/{item_id}) or a full URL target (e.g. /item/123?param=hello).
|
|
142
|
+
Path param segments ({...}) in the rule are treated as wildcards.
|
|
143
|
+
"""
|
|
144
|
+
span_value = span_value.split('?')[0]
|
|
145
|
+
rule_parts = [part for part in rule_route.split('/') if part]
|
|
146
|
+
span_parts = [part for part in span_value.split('/') if part]
|
|
147
|
+
|
|
148
|
+
if len(rule_parts) != len(span_parts):
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
# Traverse both rule and span segments
|
|
152
|
+
for i in range(len(rule_parts)):
|
|
153
|
+
# If we hit a path param in the rule, it matches any span segment
|
|
154
|
+
if rule_parts[i].startswith('{'):
|
|
155
|
+
continue
|
|
156
|
+
if rule_parts[i] != span_parts[i]:
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
return True
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def get_description(self):
|
|
163
|
+
return "OdigosSampler"
|
|
164
|
+
|
|
165
|
+
def update_config(self, new_config):
|
|
166
|
+
with self._lock:
|
|
167
|
+
# sampler_logger.debug(f'Updating the configuration with the new configuration: {new_config}')
|
|
168
|
+
self._config = new_config
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "1.0.79"
|
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/http_client.py
RENAMED
|
@@ -452,8 +452,7 @@ class OpAMPHTTPClient:
|
|
|
452
452
|
|
|
453
453
|
# Check if we have valid sampling config (same validation as update_agent_config)
|
|
454
454
|
if head_sampling_config and (
|
|
455
|
-
head_sampling_config.get('
|
|
456
|
-
head_sampling_config.get('fallbackFraction') is not None
|
|
455
|
+
head_sampling_config.get('noisyOperations') is not None
|
|
457
456
|
):
|
|
458
457
|
return head_sampling_config
|
|
459
458
|
else:
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
from .sampling_operators import SamplingOperators
|
|
2
|
-
from opentelemetry.sdk.trace.sampling import Sampler, Decision, SamplingResult, _get_parent_trace_state
|
|
3
|
-
from threading import Lock
|
|
4
|
-
import logging
|
|
5
|
-
|
|
6
|
-
# Setup the Sampler logger
|
|
7
|
-
sampler_logger = logging.getLogger('odigos')
|
|
8
|
-
|
|
9
|
-
class OdigosSampler(Sampler):
|
|
10
|
-
|
|
11
|
-
# Define the sampling operator functions used in the configuration.
|
|
12
|
-
# These operations are specified in the Odigos InstrumentationConfig Kubernetes object.
|
|
13
|
-
_operations = {
|
|
14
|
-
SamplingOperators.EQUALS.value: lambda attr, value: attr == value,
|
|
15
|
-
SamplingOperators.NOT_EQUALS.value: lambda attr, value: attr != value,
|
|
16
|
-
SamplingOperators.END_WITH.value: lambda attr, value: attr.endswith(value),
|
|
17
|
-
SamplingOperators.START_WITH.value: lambda attr, value: attr.startswith(value)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
# For compatibility with 64 bit trace IDs, the sampler checks the 64
|
|
21
|
-
# low-order bits of the trace ID to decide whether to sample a given trace.
|
|
22
|
-
TRACE_ID_LIMIT = (1 << 64) - 1
|
|
23
|
-
|
|
24
|
-
def __init__(self):
|
|
25
|
-
self._lock = Lock()
|
|
26
|
-
self._config = None
|
|
27
|
-
|
|
28
|
-
def _trace_id_based_sampling(self, trace_id, fraction):
|
|
29
|
-
"""Mimic OpenTelemetry's trace ID-based sampling logic with 64-bit range."""
|
|
30
|
-
# Calculate the bound based on the fraction using OpenTelemetry's approach
|
|
31
|
-
bound = round(fraction * (self.TRACE_ID_LIMIT + 1))
|
|
32
|
-
# Apply the TRACE_ID_LIMIT mask to ensure 64-bit range and compare it to the bound
|
|
33
|
-
return (trace_id & self.TRACE_ID_LIMIT) < bound
|
|
34
|
-
|
|
35
|
-
def should_sample(self, parent_context, trace_id, name, kind, attributes, links):
|
|
36
|
-
with self._lock:
|
|
37
|
-
# sampler_logger.debug(f'Running Should_sample a span with the following attributes: {attributes}')
|
|
38
|
-
|
|
39
|
-
if self._config is None:
|
|
40
|
-
# sampler_logger.debug('No configuration is set, returning RECORD_AND_SAMPLE')
|
|
41
|
-
return SamplingResult(Decision.RECORD_AND_SAMPLE, attributes=attributes, trace_state=_get_parent_trace_state(parent_context))
|
|
42
|
-
|
|
43
|
-
rules = self._config.get('attributesAndSamplerRules', [])
|
|
44
|
-
global_fraction = self._config.get('fallbackFraction', 1) # default to 1 if not set which means always sample
|
|
45
|
-
|
|
46
|
-
for rule in rules: # The first attribute rule that evaluates to true is used to determine the sampling decision based on its fraction.
|
|
47
|
-
and_attributes_sampler_rules = rule.get('attributeConditions', [])
|
|
48
|
-
|
|
49
|
-
# sampler_logger.debug(f'"AND" rule operands are: {and_attributes_sampler_rules}')
|
|
50
|
-
|
|
51
|
-
and_rule_fraction = rule.get('fraction', 1) # default to 1 if not set which means always sample
|
|
52
|
-
and_rule_met = True
|
|
53
|
-
|
|
54
|
-
for and_rule in and_attributes_sampler_rules:
|
|
55
|
-
# If the "AND" rule is not met once, break the loop to avoid unnecessary checks
|
|
56
|
-
if not and_rule_met:
|
|
57
|
-
break
|
|
58
|
-
|
|
59
|
-
key = and_rule.get('key')
|
|
60
|
-
value = and_rule.get('val')
|
|
61
|
-
operator = and_rule.get('operator') # equals / notEquals / endWith / startWith
|
|
62
|
-
|
|
63
|
-
if key in attributes:
|
|
64
|
-
attr_value = attributes[key]
|
|
65
|
-
# Perform the corresponding operation
|
|
66
|
-
if operator in self._operations and self._operations[operator](attr_value, value):
|
|
67
|
-
pass # Condition matched
|
|
68
|
-
else:
|
|
69
|
-
# sampler_logger.debug(f'Operator {operator} is false, setting the "AND" rule flag to false')
|
|
70
|
-
and_rule_met = False
|
|
71
|
-
else:
|
|
72
|
-
# sampler_logger.debug(f'Attribute {key} is not present in the span attributes, setting the "AND" rule flag to false')
|
|
73
|
-
and_rule_met = False
|
|
74
|
-
|
|
75
|
-
if and_rule_met:
|
|
76
|
-
# Perform the sampling decision
|
|
77
|
-
if self._trace_id_based_sampling(trace_id, and_rule_fraction):
|
|
78
|
-
# sampler_logger.debug(f'Trace [{trace_id}] is sampled "And rules" are met with fraction {and_rule_fraction}')
|
|
79
|
-
return SamplingResult(Decision.RECORD_AND_SAMPLE, attributes=attributes, trace_state=_get_parent_trace_state(parent_context))
|
|
80
|
-
else:
|
|
81
|
-
# sampler_logger.debug(f'Trace [{trace_id}] is dropped "And rules" are met but fraction {and_rule_fraction} not met')
|
|
82
|
-
return SamplingResult(Decision.DROP)
|
|
83
|
-
|
|
84
|
-
# Fallback to the global fraction if no rule matches
|
|
85
|
-
# sampler_logger.debug(f'No rule matched, falling back to the global fraction {global_fraction}')
|
|
86
|
-
if self._trace_id_based_sampling(trace_id, global_fraction):
|
|
87
|
-
return SamplingResult(Decision.RECORD_AND_SAMPLE, attributes=attributes, trace_state=_get_parent_trace_state(parent_context))
|
|
88
|
-
else:
|
|
89
|
-
return SamplingResult(Decision.DROP)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def get_description(self):
|
|
93
|
-
return "OdigosSampler"
|
|
94
|
-
|
|
95
|
-
def update_config(self, new_config):
|
|
96
|
-
with self._lock:
|
|
97
|
-
# sampler_logger.debug(f'Updating the configuration with the new configuration: {new_config}')
|
|
98
|
-
self._config = new_config
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = "1.0.78"
|
|
File without changes
|
|
File without changes
|
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/envs.py
RENAMED
|
File without changes
|
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/initializer/exit_hook.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/anyvalue_pb2.py
RENAMED
|
File without changes
|
|
File without changes
|
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/health_status.py
RENAMED
|
File without changes
|
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/opamp_pb2.py
RENAMED
|
File without changes
|
{odigos_opentelemetry_python-1.0.78 → odigos_opentelemetry_python-1.0.79}/opamp/opamp_registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|