From 3aad13d351a61e2c425081394516af53a47ffcfe Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Wed, 13 Apr 2022 17:15:52 -0700 Subject: [PATCH 1/2] NH-11236 sampler creates Service Entry Internal KVs --- opentelemetry_distro_solarwinds/sampler.py | 35 +++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/opentelemetry_distro_solarwinds/sampler.py b/opentelemetry_distro_solarwinds/sampler.py index dd8e89b3..6e32cc47 100644 --- a/opentelemetry_distro_solarwinds/sampler.py +++ b/opentelemetry_distro_solarwinds/sampler.py @@ -29,6 +29,10 @@ class _SwSampler(Sampler): """SolarWinds custom opentelemetry sampler which obeys configuration options provided by the NH/AO Backend.""" + _INTERNAL_BUCKET_CAPACITY = "BucketCapacity" + _INTERNAL_BUCKET_RATE = "BucketRate" + _INTERNAL_SAMPLE_RATE = "SampleRate" + _INTERNAL_SAMPLE_SOURCE = "SampleSource" _SW_TRACESTATE_CAPTURE_KEY = "sw.w3c.tracestate" _SW_TRACESTATE_ROOT_KEY = "sw.tracestate_parent_id" _XTRACEOPTIONS_RESP_AUTH = "auth" @@ -283,25 +287,38 @@ def calculate_attributes( if self.otel_decision_from_liboboe(decision) == Decision.DROP: logger.debug("Trace decision is to drop - not setting attributes") return None + + new_attributes = {} + # Trace's root span has no valid traceparent nor tracestate - # so we don't set additional attributes + # so set service entry internal KVs here and only here if not parent_span_context.is_valid or not trace_state: - logger.debug("No valid traceparent or no tracestate - not setting attributes") - return None + new_attributes = { + self._INTERNAL_BUCKET_CAPACITY: "{}".format(decision["bucket_cap"]), + self._INTERNAL_BUCKET_RATE: "{}".format(decision["bucket_rate"]), + self._INTERNAL_SAMPLE_RATE: decision["rate"], + self._INTERNAL_SAMPLE_SOURCE: decision["source"] + } + logger.debug( + "No valid traceparent or no tracestate - only setting " + "service entry internal KVs attributes: {}".format(new_attributes) + ) + # attributes must be immutable for SamplingResult + return MappingProxyType(new_attributes) # Set attributes with self._SW_TRACESTATE_ROOT_KEY and self._SW_TRACESTATE_CAPTURE_KEY if not attributes: trace_state_no_response = self.remove_response_from_sw(trace_state) - attributes = { + new_attributes = { self._SW_TRACESTATE_CAPTURE_KEY: trace_state_no_response.to_header() } # Only set self._SW_TRACESTATE_ROOT_KEY on the entry (root) span for this service sw_value = parent_span_context.trace_state.get(SW_TRACESTATE_KEY, None) if sw_value: - attributes[self._SW_TRACESTATE_ROOT_KEY] \ + new_attributes[self._SW_TRACESTATE_ROOT_KEY] \ = W3CTransformer.span_id_from_sw(sw_value) - logger.debug("Created new attributes: {}".format(attributes)) + logger.debug("Created new attributes: {}".format(new_attributes)) else: # Copy existing MappingProxyType KV into new_attributes for modification # attributes may have other vendor info etc @@ -327,12 +344,10 @@ def calculate_attributes( ) trace_state_no_response = self.remove_response_from_sw(attr_trace_state) new_attributes[self._SW_TRACESTATE_CAPTURE_KEY] = trace_state_no_response.to_header() + logger.debug("Set updated attributes: {}".format(new_attributes)) - attributes = new_attributes - logger.debug("Set updated attributes: {}".format(attributes)) - # attributes must be immutable for SamplingResult - return MappingProxyType(attributes) + return MappingProxyType(new_attributes) def should_sample( self, From 23a12a255f8d0e159b5726cc3da7bb710756b216 Mon Sep 17 00:00:00 2001 From: tammy-baylis-swi Date: Mon, 18 Apr 2022 15:12:20 -0700 Subject: [PATCH 2/2] Sampler sets bucket/sample KVs if liboboe decision not cont'd --- opentelemetry_distro_solarwinds/sampler.py | 50 ++++++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/opentelemetry_distro_solarwinds/sampler.py b/opentelemetry_distro_solarwinds/sampler.py index 6e32cc47..8238034d 100644 --- a/opentelemetry_distro_solarwinds/sampler.py +++ b/opentelemetry_distro_solarwinds/sampler.py @@ -33,6 +33,7 @@ class _SwSampler(Sampler): _INTERNAL_BUCKET_RATE = "BucketRate" _INTERNAL_SAMPLE_RATE = "SampleRate" _INTERNAL_SAMPLE_SOURCE = "SampleSource" + _LIBOBOE_CONTINUED = -1 _SW_TRACESTATE_CAPTURE_KEY = "sw.w3c.tracestate" _SW_TRACESTATE_ROOT_KEY = "sw.tracestate_parent_id" _XTRACEOPTIONS_RESP_AUTH = "auth" @@ -124,6 +125,21 @@ def calculate_liboboe_decision( logger.debug("Got liboboe decision outputs: {}".format(decision)) return decision + def is_decision_continued( + self, + liboboe_decision: Dict + ) -> bool: + """Returns True if liboboe decision is a continued one, else False""" + for val in [ + liboboe_decision["rate"], + liboboe_decision["source"], + liboboe_decision["bucket_rate"], + liboboe_decision["bucket_cap"] + ]: + if val != self._LIBOBOE_CONTINUED: + return False + return True + def otel_decision_from_liboboe( self, liboboe_decision: Dict @@ -290,28 +306,35 @@ def calculate_attributes( new_attributes = {} + # If not a liboboe continued trace, set service entry internal KVs + if not self.is_decision_continued(decision): + new_attributes[self._INTERNAL_BUCKET_CAPACITY] = "{}".format(decision["bucket_cap"]) + new_attributes[self._INTERNAL_BUCKET_RATE] = "{}".format(decision["bucket_rate"]) + new_attributes[self._INTERNAL_SAMPLE_RATE] = decision["rate"] + new_attributes[self._INTERNAL_SAMPLE_SOURCE] = decision["source"] + logger.debug( + "Set attributes with service entry internal KVs: {}".format(new_attributes) + ) + # Trace's root span has no valid traceparent nor tracestate - # so set service entry internal KVs here and only here + # so we can't calculate remaining attributes if not parent_span_context.is_valid or not trace_state: - new_attributes = { - self._INTERNAL_BUCKET_CAPACITY: "{}".format(decision["bucket_cap"]), - self._INTERNAL_BUCKET_RATE: "{}".format(decision["bucket_rate"]), - self._INTERNAL_SAMPLE_RATE: decision["rate"], - self._INTERNAL_SAMPLE_SOURCE: decision["source"] - } logger.debug( - "No valid traceparent or no tracestate - only setting " - "service entry internal KVs attributes: {}".format(new_attributes) + "No valid traceparent or no tracestate - returning attributes: {}" + .format(new_attributes) ) - # attributes must be immutable for SamplingResult - return MappingProxyType(new_attributes) + if new_attributes: + # attributes must be immutable for SamplingResult + return MappingProxyType(new_attributes) + else: + return None # Set attributes with self._SW_TRACESTATE_ROOT_KEY and self._SW_TRACESTATE_CAPTURE_KEY if not attributes: trace_state_no_response = self.remove_response_from_sw(trace_state) - new_attributes = { + new_attributes.update({ self._SW_TRACESTATE_CAPTURE_KEY: trace_state_no_response.to_header() - } + }) # Only set self._SW_TRACESTATE_ROOT_KEY on the entry (root) span for this service sw_value = parent_span_context.trace_state.get(SW_TRACESTATE_KEY, None) if sw_value: @@ -322,7 +345,6 @@ def calculate_attributes( else: # Copy existing MappingProxyType KV into new_attributes for modification # attributes may have other vendor info etc - new_attributes = {} for k,v in attributes.items(): new_attributes[k] = v