From cd6b5e451d7d695b8ca30772c94a31686b5b9235 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Fri, 16 Jul 2021 12:50:45 +0200 Subject: [PATCH 01/39] adding traceparent,tracestate forwarding --- instana/propagators/http_propagator.py | 116 +++++++++++++++++++++++ instana/w3c_trace_context/__init__.py | 0 instana/w3c_trace_context/tracestate.py | 23 +++++ instana/w3c_trace_context/treceparent.py | 67 +++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 instana/w3c_trace_context/__init__.py create mode 100644 instana/w3c_trace_context/tracestate.py create mode 100644 instana/w3c_trace_context/treceparent.py diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index f4c9ca4a..08ad19b7 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -7,10 +7,16 @@ from ..log import logger from .base_propagator import BasePropagator +from ..w3c_trace_context.treceparent import Traceparent +from ..w3c_trace_context.tracestate import Trancestate + +from ..util.ids import header_to_id +from ..span_context import SpanContext PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 + class HTTPPropagator(BasePropagator): """ Instana Propagator for Format.HTTP_HEADERS. @@ -18,20 +24,39 @@ class HTTPPropagator(BasePropagator): The HTTP_HEADERS format deals with key-values with string to string mapping. The character set should be restricted to HTTP compatible. """ + HEADER_KEY_TRACEPARENT = "traceparent" + HEADER_KEY_TRACESTATE = "tracestate" + + def __init__(self): + self.__traceparent = Traceparent() + self.__tracestate = Trancestate() + super(HTTPPropagator, self).__init__() + def inject(self, span_context, carrier): try: trace_id = span_context.trace_id span_id = span_context.span_id + traceparent = self.__traceparent.traceparent + tracestate = self.__tracestate.tracestate if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): + if traceparent and tracestate: + carrier[self.HEADER_KEY_TRACEPARENT] = traceparent + carrier[self.HEADER_KEY_TRACESTATE] = tracestate carrier[self.HEADER_KEY_T] = trace_id carrier[self.HEADER_KEY_S] = span_id carrier[self.HEADER_KEY_L] = "1" elif isinstance(carrier, list): + if traceparent and tracestate: + carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) + carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) carrier.append((self.HEADER_KEY_T, trace_id)) carrier.append((self.HEADER_KEY_S, span_id)) carrier.append((self.HEADER_KEY_L, "1")) elif hasattr(carrier, '__setitem__'): + if traceparent and tracestate: + carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) + carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) carrier.__setitem__(self.HEADER_KEY_T, trace_id) carrier.__setitem__(self.HEADER_KEY_S, span_id) carrier.__setitem__(self.HEADER_KEY_L, "1") @@ -40,3 +65,94 @@ def inject(self, span_context, carrier): except Exception: logger.debug("inject error:", exc_info=True) + + def __extract_headers_dict(self, carrier): + # Attempt to convert incoming into a dict + # TODO: Support for multiple header fields with the same name? (e.g. tracestate) + try: + if isinstance(carrier, dict): + dc = carrier + elif hasattr(carrier, "__dict__"): + dc = carrier.__dict__ + else: + dc = dict(carrier) + except Exception: + logger.debug("extract: Couln't convert %s", carrier) + dc = None + + return dc + + def extract(self, carrier): + try: + headers = self.__extract_headers_dict(carrier=carrier) + if headers is None: + return None + headers = {k.lower(): v for k, v in headers.items()} + traceparent = self.__traceparent.extract_tranparent(headers) + tracestate = self.__tracestate.extract_tracestate(headers) + + # TODO use traceparent and tracestate + ctx = self.__extract_instana_headers(dc=headers) + return ctx + except Exception: + logger.debug("extract error:", exc_info=True) + + def __extract_instana_headers(self, dc): + """ + Search carrier for the *HEADER* keys and return a SpanContext or None + + Note: Extract is on the base class since it never really varies in task regardless + of the propagator in uses. + + :param dc: The dict or list potentially containing context + :return: SpanContext or None + """ + trace_id = None + span_id = None + level = 1 + synthetic = False + + try: + # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style + # We do a case insensitive search to cover all possible variations of incoming headers. + for key in dc.keys(): + lc_key = None + + if PY3 is True and isinstance(key, bytes): + lc_key = key.decode("utf-8").lower() + else: + lc_key = key.lower() + + if self.LC_HEADER_KEY_T == lc_key: + trace_id = header_to_id(dc[key]) + elif self.LC_HEADER_KEY_S == lc_key: + span_id = header_to_id(dc[key]) + elif self.LC_HEADER_KEY_L == lc_key: + level = dc[key] + elif self.LC_HEADER_KEY_SYNTHETIC == lc_key: + synthetic = dc[key] in ['1', b'1'] + + elif self.ALT_LC_HEADER_KEY_T == lc_key: + trace_id = header_to_id(dc[key]) + elif self.ALT_LC_HEADER_KEY_S == lc_key: + span_id = header_to_id(dc[key]) + elif self.ALT_LC_HEADER_KEY_L == lc_key: + level = dc[key] + elif self.ALT_LC_HEADER_KEY_SYNTHETIC == lc_key: + synthetic = dc[key] in ['1', b'1'] + + ctx = None + if trace_id is not None and span_id is not None: + ctx = SpanContext(span_id=span_id, + trace_id=trace_id, + level=level, + baggage={}, + sampled=True, + synthetic=synthetic) + elif synthetic: + ctx = SpanContext(synthetic=synthetic) + + return ctx + + except Exception: + logger.debug("extract error:", exc_info=True) diff --git a/instana/w3c_trace_context/__init__.py b/instana/w3c_trace_context/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py new file mode 100644 index 00000000..879d7b53 --- /dev/null +++ b/instana/w3c_trace_context/tracestate.py @@ -0,0 +1,23 @@ +# (c) Copyright IBM Corp. 2021 +# (c) Copyright Instana Inc. 2021 +from ..log import logger + + +class Trancestate: + def __init__(self): + self.tracestate = None + + @property + def tracestate(self): + return self._tracestate + + @tracestate.setter + def tracestate(self, value): + self._tracestate = value + + def extract_tracestate(self, headers): + self.tracestate = headers.get('tracestate', None) + if self.tracestate is None: + return None + + return self.tracestate diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py new file mode 100644 index 00000000..9fb4293d --- /dev/null +++ b/instana/w3c_trace_context/treceparent.py @@ -0,0 +1,67 @@ +# (c) Copyright IBM Corp. 2021 +# (c) Copyright Instana Inc. 2021 + +from ..log import logger + + +class Traceparent: + SPECIFICATION_VERSION = "00" + + def __init__(self): + self.traceparent = None + self.trace_id = None + self.parent_id = None + self.sampled = None + + @property + def traceparent(self): + return self._traceparent + + @traceparent.setter + def traceparent(self, value): + self._traceparent = value + + @property + def trace_id(self): + return self._trace_id + + @trace_id.setter + def trace_id(self, value): + self._trace_id = value + + @property + def parent_id(self): + return self._parent_id + + @parent_id.setter + def parent_id(self, value): + self._parent_id = value + + @property + def sampled(self): + return self._sampled + + @sampled.setter + def sampled(self, value): + self._sampled = value + + def extract_tranparent(self, headers): + self.traceparent = headers.get('traceparent', None) + if self.traceparent is None: + return None + + try: + traceparent_properties = self.traceparent.split("-") + if traceparent_properties[0] == self.SPECIFICATION_VERSION: + self.trace_id = traceparent_properties[1] + self.parent_id = traceparent_properties[2] + self.sampled = traceparent_properties[3] + else: + raise Exception + except Exception: + logger.debug("traceparent does follow version 00 specification") + return None + + return self.traceparent + + # Value = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" From a877cf9847c2b1fae77c01a521b1ae9eef5a5043 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Fri, 16 Jul 2021 13:26:38 +0200 Subject: [PATCH 02/39] adding the object for python2 compatibility --- instana/propagators/base_propagator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 44e9461d..c032df7d 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -25,7 +25,7 @@ # X-Instana-T -class BasePropagator(): +class BasePropagator(object): UC_HEADER_KEY_T = 'X-INSTANA-T' UC_HEADER_KEY_S = 'X-INSTANA-S' UC_HEADER_KEY_L = 'X-INSTANA-L' From cb268173d2b78164f430d3688153f5a4580407ad Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Thu, 22 Jul 2021 15:33:58 +0200 Subject: [PATCH 03/39] added validation of traceparent/tracestate headers and update methods --- instana/propagators/http_propagator.py | 11 ++++--- instana/w3c_trace_context/tracestate.py | 41 ++++++++++++++++++++++-- instana/w3c_trace_context/treceparent.py | 18 +++++++++-- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index 08ad19b7..c33d70d4 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -8,7 +8,7 @@ from ..log import logger from .base_propagator import BasePropagator from ..w3c_trace_context.treceparent import Traceparent -from ..w3c_trace_context.tracestate import Trancestate +from ..w3c_trace_context.tracestate import Tracestate from ..util.ids import header_to_id from ..span_context import SpanContext @@ -29,13 +29,16 @@ class HTTPPropagator(BasePropagator): def __init__(self): self.__traceparent = Traceparent() - self.__tracestate = Trancestate() + self.__tracestate = Tracestate() super(HTTPPropagator, self).__init__() def inject(self, span_context, carrier): try: trace_id = span_context.trace_id span_id = span_context.span_id + sampled = span_context.sampled + self.__traceparent.update_traceparent(trace_id, span_id, sampled) + self.__tracestate.update_tracestate(trace_id, span_id) traceparent = self.__traceparent.traceparent tracestate = self.__tracestate.tracestate @@ -77,7 +80,7 @@ def __extract_headers_dict(self, carrier): else: dc = dict(carrier) except Exception: - logger.debug("extract: Couln't convert %s", carrier) + logger.debug("extract: Couldn't convert %s", carrier) dc = None return dc @@ -88,7 +91,7 @@ def extract(self, carrier): if headers is None: return None headers = {k.lower(): v for k, v in headers.items()} - traceparent = self.__traceparent.extract_tranparent(headers) + traceparent = self.__traceparent.extract_traceparent(headers) tracestate = self.__tracestate.extract_tracestate(headers) # TODO use traceparent and tracestate diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index 879d7b53..d011dcfb 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -1,9 +1,11 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 -from ..log import logger -class Trancestate: +class Tracestate: + MAX_NUMBER_OF_LIST_MEMBERS = 32 + REMOVE_ENTRIES_LARGER_THAN = 128 + def __init__(self): self.tracestate = None @@ -21,3 +23,38 @@ def extract_tracestate(self, headers): return None return self.tracestate + + def update_tracestate(self, in_trace_id, in_span_id): + """ + Method to update the tracestate property with the instana trace_id and span_id + + :param in_trace_id: instana trace_id + :param in_span_id: instana parent_id + :return: + """ + + span_id = in_span_id.zfill(16) # if span_id is shorter than 16 characters we prepend zeros + instana_tracestate = "in={};{}".format(in_trace_id, span_id) + # tracestate can contain a max of 32 list members, if it contains up to 31 + # we can safely add the instana one without the need to truncate anything + if len(self.tracestate.split(",")) <= self.MAX_NUMBER_OF_LIST_MEMBERS - 1: + self.tracestate = "{},{}".format(instana_tracestate, self.tracestate) + else: + list_members = self.tracestate.split(",") + list_members_to_remove = len(list_members) - self.MAX_NUMBER_OF_LIST_MEMBERS + 1 + # Number 1 priority members to be removed are the ones larger than 128 characters + for i, m in reversed(list(enumerate(list_members))): + if len(m) > self.REMOVE_ENTRIES_LARGER_THAN: + list_members.pop(i) + list_members_to_remove -= 1 + if list_members_to_remove == 0: + break + # if there are still more than 31 list members remaining, we remove as many members + # from the end as necessary to remain just 31 list members + while list_members_to_remove > 0: + list_members.pop() + list_members_to_remove -= 1 + # update the tracestate containing just 31 list members + self.tracestate = ",".join(list_members) + # adding instana as first list member, total of 32 list members + self.tracestate = "{},{}".format(instana_tracestate, self.tracestate) diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py index 9fb4293d..c73b6e59 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/treceparent.py @@ -2,10 +2,12 @@ # (c) Copyright Instana Inc. 2021 from ..log import logger +import re class Traceparent: SPECIFICATION_VERSION = "00" + TRACEPARENT_REGEX = re.compile("[0-9a-f]{2}-(?!0{32})([0-9a-f]{32})-(?!0{16})([0-9a-f]{16})-[0-9a-f]{2}") def __init__(self): self.traceparent = None @@ -19,7 +21,10 @@ def traceparent(self): @traceparent.setter def traceparent(self, value): - self._traceparent = value + if value and self.TRACEPARENT_REGEX.match(value): + self._traceparent = value + else: + self._traceparent = None @property def trace_id(self): @@ -45,7 +50,7 @@ def sampled(self): def sampled(self, value): self._sampled = value - def extract_tranparent(self, headers): + def extract_traceparent(self, headers): self.traceparent = headers.get('traceparent', None) if self.traceparent is None: return None @@ -64,4 +69,11 @@ def extract_tranparent(self, headers): return self.traceparent - # Value = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" + def update_traceparent(self, in_trace_id, in_span_id, sampled): + self.trace_id = in_trace_id.zfill(32) + self.parent_id = in_span_id.zfill(16) + self.sampled = "01" if sampled else "00" + self.traceparent = "{version}-{traceid}-{parentid}-{sampled}".format(version=self.SPECIFICATION_VERSION, + traceid=self.trace_id, + parentid=self.parent_id, + sampled=self.sampled) From 56ecf4cc189525137aa1cac9e1d916d66fec44be Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Thu, 22 Jul 2021 16:21:14 +0200 Subject: [PATCH 04/39] fix the tests --- instana/w3c_trace_context/tracestate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index d011dcfb..a9d4648f 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -32,7 +32,8 @@ def update_tracestate(self, in_trace_id, in_span_id): :param in_span_id: instana parent_id :return: """ - + if self.tracestate is None: + return span_id = in_span_id.zfill(16) # if span_id is shorter than 16 characters we prepend zeros instana_tracestate = "in={};{}".format(in_trace_id, span_id) # tracestate can contain a max of 32 list members, if it contains up to 31 From 5d52645ce4a46e47b419c68efc066c32b8c92544 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Thu, 22 Jul 2021 16:24:36 +0200 Subject: [PATCH 05/39] send updated w3c context headers --- instana/w3c_trace_context/tracestate.py | 47 +++++++++++++------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index a9d4648f..d801e11d 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -32,30 +32,31 @@ def update_tracestate(self, in_trace_id, in_span_id): :param in_span_id: instana parent_id :return: """ - if self.tracestate is None: - return span_id = in_span_id.zfill(16) # if span_id is shorter than 16 characters we prepend zeros instana_tracestate = "in={};{}".format(in_trace_id, span_id) - # tracestate can contain a max of 32 list members, if it contains up to 31 - # we can safely add the instana one without the need to truncate anything - if len(self.tracestate.split(",")) <= self.MAX_NUMBER_OF_LIST_MEMBERS - 1: - self.tracestate = "{},{}".format(instana_tracestate, self.tracestate) + if self.tracestate is None: + self.tracestate = instana_tracestate else: - list_members = self.tracestate.split(",") - list_members_to_remove = len(list_members) - self.MAX_NUMBER_OF_LIST_MEMBERS + 1 - # Number 1 priority members to be removed are the ones larger than 128 characters - for i, m in reversed(list(enumerate(list_members))): - if len(m) > self.REMOVE_ENTRIES_LARGER_THAN: - list_members.pop(i) + # tracestate can contain a max of 32 list members, if it contains up to 31 + # we can safely add the instana one without the need to truncate anything + if len(self.tracestate.split(",")) <= self.MAX_NUMBER_OF_LIST_MEMBERS - 1: + self.tracestate = "{},{}".format(instana_tracestate, self.tracestate) + else: + list_members = self.tracestate.split(",") + list_members_to_remove = len(list_members) - self.MAX_NUMBER_OF_LIST_MEMBERS + 1 + # Number 1 priority members to be removed are the ones larger than 128 characters + for i, m in reversed(list(enumerate(list_members))): + if len(m) > self.REMOVE_ENTRIES_LARGER_THAN: + list_members.pop(i) + list_members_to_remove -= 1 + if list_members_to_remove == 0: + break + # if there are still more than 31 list members remaining, we remove as many members + # from the end as necessary to remain just 31 list members + while list_members_to_remove > 0: + list_members.pop() list_members_to_remove -= 1 - if list_members_to_remove == 0: - break - # if there are still more than 31 list members remaining, we remove as many members - # from the end as necessary to remain just 31 list members - while list_members_to_remove > 0: - list_members.pop() - list_members_to_remove -= 1 - # update the tracestate containing just 31 list members - self.tracestate = ",".join(list_members) - # adding instana as first list member, total of 32 list members - self.tracestate = "{},{}".format(instana_tracestate, self.tracestate) + # update the tracestate containing just 31 list members + self.tracestate = ",".join(list_members) + # adding instana as first list member, total of 32 list members + self.tracestate = "{},{}".format(instana_tracestate, self.tracestate) From 2bd8c6790f769c43f5c25f92a31116b49f506ef0 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Fri, 23 Jul 2021 16:29:56 +0200 Subject: [PATCH 06/39] adding span ctx calculation based on w3c trace context --- instana/propagators/http_propagator.py | 72 +++++++++++++++++------- instana/span.py | 11 ++++ instana/w3c_trace_context/tracestate.py | 5 ++ instana/w3c_trace_context/treceparent.py | 7 ++- 4 files changed, 73 insertions(+), 22 deletions(-) diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index c33d70d4..8611077c 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -8,7 +8,7 @@ from ..log import logger from .base_propagator import BasePropagator from ..w3c_trace_context.treceparent import Traceparent -from ..w3c_trace_context.tracestate import Tracestate +from ..w3c_trace_context.tracestate import Tracestate, InstanaAncestor from ..util.ids import header_to_id from ..span_context import SpanContext @@ -36,8 +36,8 @@ def inject(self, span_context, carrier): try: trace_id = span_context.trace_id span_id = span_context.span_id - sampled = span_context.sampled - self.__traceparent.update_traceparent(trace_id, span_id, sampled) + level = span_context.level + self.__traceparent.update_traceparent(trace_id, span_id, level) self.__tracestate.update_tracestate(trace_id, span_id) traceparent = self.__traceparent.traceparent tracestate = self.__tracestate.tracestate @@ -95,11 +95,56 @@ def extract(self, carrier): tracestate = self.__tracestate.extract_tracestate(headers) # TODO use traceparent and tracestate - ctx = self.__extract_instana_headers(dc=headers) + trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) + ctx = self.__determine_span_context(trace_id, span_id, level, synthetic) + return ctx except Exception: logger.debug("extract error:", exc_info=True) + def __determine_span_context(self, trace_id, span_id, level, synthetic): + ctx = None + if all(v is None for v in [trace_id, span_id, level]) and self.__traceparent.traceparent: + + ctx = SpanContext(span_id=self.__traceparent.parent_id, + trace_id=self.__traceparent.trace_id[-16:], + level=1, + baggage={}, + sampled=True, + synthetic=False if synthetic is None else True) + + ctx.trace_parent = True + + try: + if self.__tracestate.tracestate and "in=" in self.__tracestate.tracestate: + in_list_member = self.__tracestate.tracestate.split("in=")[1].split(",")[0] + ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], + parent_id=in_list_member.split(";")[1]) + + except Exception as e: + logger.debug("extract instana ancestor error:", exc_info=True) + + ctx.long_trace_id = self.__traceparent.trace_id + + elif trace_id and span_id: + try: + if "correlationType" in level: + ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] + if "correlationId" in level: + ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] + except Exception as e: + logger.debug("extract instana correlation type/id error:", exc_info=True) + + ctx = SpanContext(span_id=span_id, + trace_id=trace_id, + level=int(level.split(",")[0]), + baggage={}, + sampled=True, + synthetic=False if synthetic is None else True) + elif synthetic: + ctx = SpanContext(synthetic=synthetic) + return ctx + def __extract_instana_headers(self, dc): """ Search carrier for the *HEADER* keys and return a SpanContext or None @@ -112,8 +157,8 @@ def __extract_instana_headers(self, dc): """ trace_id = None span_id = None - level = 1 - synthetic = False + level = None + synthetic = None try: # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style @@ -144,18 +189,7 @@ def __extract_instana_headers(self, dc): elif self.ALT_LC_HEADER_KEY_SYNTHETIC == lc_key: synthetic = dc[key] in ['1', b'1'] - ctx = None - if trace_id is not None and span_id is not None: - ctx = SpanContext(span_id=span_id, - trace_id=trace_id, - level=level, - baggage={}, - sampled=True, - synthetic=synthetic) - elif synthetic: - ctx = SpanContext(synthetic=synthetic) - - return ctx - except Exception: logger.debug("extract error:", exc_info=True) + + return trace_id, span_id, level, synthetic diff --git a/instana/span.py b/instana/span.py index 0cebd032..8c0a2240 100644 --- a/instana/span.py +++ b/instana/span.py @@ -112,6 +112,17 @@ def __init__(self, span, source, service_name, **kwargs): self.data = DictionaryOfStan() self.stack = span.stack + if hasattr(span.context, 'trace_parent'): + self.tp = span.context.trace_parent + if hasattr(span.context, 'instana_ancestor'): + self.ia = span.context.instana_ancestor + if hasattr(span.context, 'long_trace_id'): + self.lt = span.context.long_trace_id + if hasattr(span.context, 'correlation_type'): + self.crtp = span.context.correlation_type + if hasattr(span.context, 'correlation_id'): + self.crid = span.context.correlation_id + if span.synthetic is True: self.sy = span.synthetic diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index d801e11d..cf151c32 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -1,6 +1,11 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 +class InstanaAncestor: + def __init__(self, trace_id, parent_id): + self.t = trace_id + self.p = parent_id + class Tracestate: MAX_NUMBER_OF_LIST_MEMBERS = 32 diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py index c73b6e59..9a41cb7f 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/treceparent.py @@ -69,10 +69,11 @@ def extract_traceparent(self, headers): return self.traceparent - def update_traceparent(self, in_trace_id, in_span_id, sampled): - self.trace_id = in_trace_id.zfill(32) + def update_traceparent(self, in_trace_id, in_span_id, level): + if self.trace_id is None: # modify the trace_id part only when it was not present at all + self.trace_id = in_trace_id.zfill(32) self.parent_id = in_span_id.zfill(16) - self.sampled = "01" if sampled else "00" + self.sampled = "01" if level == 1 else "00" self.traceparent = "{version}-{traceid}-{parentid}-{sampled}".format(version=self.SPECIFICATION_VERSION, traceid=self.trace_id, parentid=self.parent_id, From 3412f35db1605c48ceac03df155412b47aad7dd5 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Mon, 26 Jul 2021 15:45:49 +0200 Subject: [PATCH 07/39] adding the w3c trace context relevant span properties --- instana/propagators/http_propagator.py | 64 +++++++++++++++--------- instana/span.py | 10 ++-- instana/span_context.py | 49 +++++++++++++++++- instana/tracer.py | 5 ++ instana/w3c_trace_context/tracestate.py | 4 -- instana/w3c_trace_context/treceparent.py | 5 +- 6 files changed, 100 insertions(+), 37 deletions(-) diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index 8611077c..1913b7cc 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -12,6 +12,7 @@ from ..util.ids import header_to_id from ..span_context import SpanContext +import os PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -91,8 +92,8 @@ def extract(self, carrier): if headers is None: return None headers = {k.lower(): v for k, v in headers.items()} - traceparent = self.__traceparent.extract_traceparent(headers) - tracestate = self.__tracestate.extract_tracestate(headers) + self.__traceparent.extract_traceparent(headers) + self.__tracestate.extract_tracestate(headers) # TODO use traceparent and tracestate trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) @@ -103,28 +104,45 @@ def extract(self, carrier): logger.debug("extract error:", exc_info=True) def __determine_span_context(self, trace_id, span_id, level, synthetic): - ctx = None - if all(v is None for v in [trace_id, span_id, level]) and self.__traceparent.traceparent: - - ctx = SpanContext(span_id=self.__traceparent.parent_id, - trace_id=self.__traceparent.trace_id[-16:], - level=1, - baggage={}, - sampled=True, - synthetic=False if synthetic is None else True) - - ctx.trace_parent = True + disable_traceparent = os.environ.get("INSTANA_W3C_DISABLE_TRACE_CORRELATION", "") - try: - if self.__tracestate.tracestate and "in=" in self.__tracestate.tracestate: - in_list_member = self.__tracestate.tracestate.split("in=")[1].split(",")[0] - ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], - parent_id=in_list_member.split(";")[1]) - - except Exception as e: - logger.debug("extract instana ancestor error:", exc_info=True) - - ctx.long_trace_id = self.__traceparent.trace_id + ctx = None + if self.__traceparent.traceparent and all(v is None for v in [trace_id, span_id, level]): + if disable_traceparent == "": + ctx = SpanContext(span_id=self.__traceparent.parent_id, + trace_id=self.__traceparent.trace_id[-16:], + level=1, + baggage={}, + sampled=True, + synthetic=False if synthetic is None else True) + + ctx.trace_parent = True + + try: + if self.__tracestate.tracestate and "in=" in self.__tracestate.tracestate: + in_list_member = self.__tracestate.tracestate.split("in=")[1].split(",")[0] + ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], + parent_id=in_list_member.split(";")[1]) + + except Exception as e: + logger.debug("extract instana ancestor error:", exc_info=True) + + ctx.long_trace_id = self.__traceparent.trace_id + else: + try: + if self.__tracestate.tracestate and "in=" in self.__tracestate.tracestate: + in_list_member = self.__tracestate.tracestate.split("in=")[1].split(",")[0] + ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], + parent_id=in_list_member.split(";")[1]) + + ctx = SpanContext(span_id=in_list_member.split(";")[1], + trace_id=in_list_member.split(";")[0], + level=1, + baggage={}, + sampled=True, + synthetic=False if synthetic is None else True) + except Exception as e: + logger.debug("extract instana ancestor error:", exc_info=True) elif trace_id and span_id: try: diff --git a/instana/span.py b/instana/span.py index 8c0a2240..8a08035a 100644 --- a/instana/span.py +++ b/instana/span.py @@ -112,15 +112,15 @@ def __init__(self, span, source, service_name, **kwargs): self.data = DictionaryOfStan() self.stack = span.stack - if hasattr(span.context, 'trace_parent'): + if span.context.trace_parent: self.tp = span.context.trace_parent - if hasattr(span.context, 'instana_ancestor'): + if span.context.instana_ancestor: self.ia = span.context.instana_ancestor - if hasattr(span.context, 'long_trace_id'): + if span.context.long_trace_id: self.lt = span.context.long_trace_id - if hasattr(span.context, 'correlation_type'): + if span.context.correlation_type: self.crtp = span.context.correlation_type - if hasattr(span.context, 'correlation_id'): + if span.context.correlation_id: self.crid = span.context.correlation_id if span.synthetic is True: diff --git a/instana/span_context.py b/instana/span_context.py index 9e4312eb..caf1db5a 100644 --- a/instana/span_context.py +++ b/instana/span_context.py @@ -10,7 +10,8 @@ def __init__( baggage=None, sampled=True, level=1, - synthetic=False): + synthetic=False + ): self.level = level self.trace_id = trace_id @@ -19,6 +20,52 @@ def __init__( self.synthetic = synthetic self._baggage = baggage or {} + self.trace_parent = None + self.instana_ancestor = None + self.long_trace_id = None + self.correlation_type = None + self.correlation_id = None + + @property + def trace_parent(self): + return self._trace_parent + + @trace_parent.setter + def trace_parent(self, value): + self._trace_parent = value + + @property + def instana_ancestor(self): + return self._instana_ancestor + + @instana_ancestor.setter + def instana_ancestor(self, value): + self._instana_ancestor = value + + @property + def long_trace_id(self): + return self._long_trace_id + + @long_trace_id.setter + def long_trace_id(self, value): + self._long_trace_id = value + + @property + def correlation_type(self): + return self._correlation_type + + @correlation_type.setter + def correlation_type(self, value): + self._correlation_type = value + + @property + def correlation_id(self): + return self._correlation_id + + @correlation_id.setter + def correlation_id(self, value): + self._correlation_id = value + @property def baggage(self): return self._baggage diff --git a/instana/tracer.py b/instana/tracer.py index bbf3c2d7..e6215342 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -89,6 +89,11 @@ def start_span(self, ctx._baggage = parent_ctx._baggage.copy() ctx.trace_id = parent_ctx.trace_id ctx.sampled = parent_ctx.sampled + ctx.long_trace_id = parent_ctx.long_trace_id + ctx.trace_parent = parent_ctx.trace_parent + ctx.instana_ancestor = parent_ctx.instana_ancestor + ctx.correlation_type = parent_ctx.correlation_type + ctx.correlation_id = parent_ctx.correlation_id else: ctx.trace_id = gid ctx.sampled = self.sampler.sampled(ctx.trace_id) diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index cf151c32..068f25bd 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -24,10 +24,6 @@ def tracestate(self, value): def extract_tracestate(self, headers): self.tracestate = headers.get('tracestate', None) - if self.tracestate is None: - return None - - return self.tracestate def update_tracestate(self, in_trace_id, in_span_id): """ diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py index 9a41cb7f..d47a244d 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/treceparent.py @@ -53,7 +53,7 @@ def sampled(self, value): def extract_traceparent(self, headers): self.traceparent = headers.get('traceparent', None) if self.traceparent is None: - return None + return try: traceparent_properties = self.traceparent.split("-") @@ -65,9 +65,6 @@ def extract_traceparent(self, headers): raise Exception except Exception: logger.debug("traceparent does follow version 00 specification") - return None - - return self.traceparent def update_traceparent(self, in_trace_id, in_span_id, level): if self.trace_id is None: # modify the trace_id part only when it was not present at all From 097726969d022bbbff63dda19855b97bc34262cd Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Tue, 27 Jul 2021 10:36:30 +0200 Subject: [PATCH 08/39] adding docstrings and cleaning up --- instana/propagators/http_propagator.py | 50 +++++++++++++++++------- instana/w3c_trace_context/treceparent.py | 16 +++++++- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index 1913b7cc..f05b466f 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -71,8 +71,11 @@ def inject(self, span_context, carrier): logger.debug("inject error:", exc_info=True) def __extract_headers_dict(self, carrier): - # Attempt to convert incoming into a dict - # TODO: Support for multiple header fields with the same name? (e.g. tracestate) + """ + This method converts the incoming carrier into a dict + :param carrier: + :return: dc dictionary + """ try: if isinstance(carrier, dict): dc = carrier @@ -87,6 +90,12 @@ def __extract_headers_dict(self, carrier): return dc def extract(self, carrier): + """ + This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP + requests more extracting steps and logic was required + :param carrier: + :return: the context or None + """ try: headers = self.__extract_headers_dict(carrier=carrier) if headers is None: @@ -95,7 +104,6 @@ def extract(self, carrier): self.__traceparent.extract_traceparent(headers) self.__tracestate.extract_tracestate(headers) - # TODO use traceparent and tracestate trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) ctx = self.__determine_span_context(trace_id, span_id, level, synthetic) @@ -104,6 +112,16 @@ def extract(self, carrier): logger.debug("extract error:", exc_info=True) def __determine_span_context(self, trace_id, span_id, level, synthetic): + """ + This method determines the span context depending on a set of conditions being met + Detailed description of the conditions can be found here: + https://github.com/instana/technical-documentation/tree/master/tracing/specification#http-processing-for-instana-tracers + :param trace_id: instana trace id + :param span_id: instana span id + :param level: instana level + :param synthetic: instana synthetic + :return: ctx or None + """ disable_traceparent = os.environ.get("INSTANA_W3C_DISABLE_TRACE_CORRELATION", "") ctx = None @@ -121,6 +139,7 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic): try: if self.__tracestate.tracestate and "in=" in self.__tracestate.tracestate: in_list_member = self.__tracestate.tracestate.split("in=")[1].split(",")[0] + ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], parent_id=in_list_member.split(";")[1]) @@ -132,8 +151,6 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic): try: if self.__tracestate.tracestate and "in=" in self.__tracestate.tracestate: in_list_member = self.__tracestate.tracestate.split("in=")[1].split(",")[0] - ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], - parent_id=in_list_member.split(";")[1]) ctx = SpanContext(span_id=in_list_member.split(";")[1], trace_id=in_list_member.split(";")[0], @@ -141,10 +158,21 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic): baggage={}, sampled=True, synthetic=False if synthetic is None else True) + + ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], + parent_id=in_list_member.split(";")[1]) except Exception as e: logger.debug("extract instana ancestor error:", exc_info=True) elif trace_id and span_id: + + ctx = SpanContext(span_id=span_id, + trace_id=trace_id, + level=int(level.split(",")[0]), + baggage={}, + sampled=True, + synthetic=False if synthetic is None else True) + try: if "correlationType" in level: ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] @@ -153,25 +181,17 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic): except Exception as e: logger.debug("extract instana correlation type/id error:", exc_info=True) - ctx = SpanContext(span_id=span_id, - trace_id=trace_id, - level=int(level.split(",")[0]), - baggage={}, - sampled=True, - synthetic=False if synthetic is None else True) elif synthetic: ctx = SpanContext(synthetic=synthetic) + return ctx def __extract_instana_headers(self, dc): """ Search carrier for the *HEADER* keys and return a SpanContext or None - Note: Extract is on the base class since it never really varies in task regardless - of the propagator in uses. - :param dc: The dict or list potentially containing context - :return: SpanContext or None + :return: trace_id, span_id, level, synthetic """ trace_id = None span_id = None diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py index d47a244d..41e59f03 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/treceparent.py @@ -51,6 +51,12 @@ def sampled(self, value): self._sampled = value def extract_traceparent(self, headers): + """ + Extracts from the headers dict the traceparent key/value and validates the value while trying to set the + traceparent property. If the validation succeeds all other traceparent sub-properties are getting extracted + :param headers: dict with headers + :return: it sets the class properties accordingly + """ self.traceparent = headers.get('traceparent', None) if self.traceparent is None: return @@ -64,9 +70,17 @@ def extract_traceparent(self, headers): else: raise Exception except Exception: - logger.debug("traceparent does follow version 00 specification") + logger.debug("traceparent does not follow version 00 specification") def update_traceparent(self, in_trace_id, in_span_id, level): + """ + This method updates the traceparent header or generates one if there was no traceparent incoming header or it + was invalid + :param in_trace_id: instana trace id, used when there is no preexisting trace_id from the traceparent header + :param in_span_id: instana span id, used to update the parent id of the traceparent header + :param level: instana level, used to determine the value of sampled flag of the traceparent header + :return: sets the updated traceparent header + """ if self.trace_id is None: # modify the trace_id part only when it was not present at all self.trace_id = in_trace_id.zfill(32) self.parent_id = in_span_id.zfill(16) From c655fb2787871e02460edda509aafa58ed6785d1 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Thu, 29 Jul 2021 11:57:35 +0200 Subject: [PATCH 09/39] apply several fixes discovered through testing --- instana/instrumentation/pika.py | 4 +- instana/propagators/http_propagator.py | 85 +++++++++--------- instana/span_context.py | 20 ++++- instana/tracer.py | 2 + instana/w3c_trace_context/tracestate.py | 57 ++++++++---- instana/w3c_trace_context/treceparent.py | 110 ++++++++++------------- tests/clients/test_pika.py | 27 ++++-- 7 files changed, 167 insertions(+), 138 deletions(-) diff --git a/instana/instrumentation/pika.py b/instana/instrumentation/pika.py index 3b844082..9e552c0a 100644 --- a/instana/instrumentation/pika.py +++ b/instana/instrumentation/pika.py @@ -49,7 +49,7 @@ def _bind_args(exchange, routing_key, body, properties=None, *args, **kwargs): (exchange, routing_key, body, properties, args, kwargs) = (_bind_args(*args, **kwargs)) - with tracer.start_active_span("rabbitmq", child_of=active_tracer.active_span) as scope: + with active_tracer.start_active_span("rabbitmq", child_of=active_tracer.active_span) as scope: try: _extract_publisher_tags(scope.span, conn=instance.connection, @@ -62,7 +62,7 @@ def _bind_args(exchange, routing_key, body, properties=None, *args, **kwargs): properties = properties or pika.BasicProperties() properties.headers = properties.headers or {} - tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, properties.headers) + active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, properties.headers) args = (exchange, routing_key, body, properties) + args try: diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index f05b466f..72040a26 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -29,8 +29,8 @@ class HTTPPropagator(BasePropagator): HEADER_KEY_TRACESTATE = "tracestate" def __init__(self): - self.__traceparent = Traceparent() - self.__tracestate = Tracestate() + self.__tp = Traceparent() + self.__ts = Tracestate() super(HTTPPropagator, self).__init__() def inject(self, span_context, carrier): @@ -38,10 +38,10 @@ def inject(self, span_context, carrier): trace_id = span_context.trace_id span_id = span_context.span_id level = span_context.level - self.__traceparent.update_traceparent(trace_id, span_id, level) - self.__tracestate.update_tracestate(trace_id, span_id) - traceparent = self.__traceparent.traceparent - tracestate = self.__tracestate.tracestate + traceparent = span_context.traceparent + tracestate = span_context.tracestate + traceparent = self.__tp.update_traceparent(traceparent, trace_id, span_id, level) + tracestate = self.__ts.update_tracestate(tracestate, trace_id, span_id) if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): if traceparent and tracestate: @@ -101,17 +101,17 @@ def extract(self, carrier): if headers is None: return None headers = {k.lower(): v for k, v in headers.items()} - self.__traceparent.extract_traceparent(headers) - self.__tracestate.extract_tracestate(headers) + traceparent = self.__tp.extract_traceparent(headers) + tracestate = self.__ts.extract_tracestate(headers) trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) - ctx = self.__determine_span_context(trace_id, span_id, level, synthetic) + ctx = self.__determine_span_context(trace_id, span_id, level, synthetic, traceparent, tracestate) return ctx except Exception: logger.debug("extract error:", exc_info=True) - def __determine_span_context(self, trace_id, span_id, level, synthetic): + def __determine_span_context(self, trace_id, span_id, level, synthetic, traceparent, tracestate): """ This method determines the span context depending on a set of conditions being met Detailed description of the conditions can be found here: @@ -125,62 +125,63 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic): disable_traceparent = os.environ.get("INSTANA_W3C_DISABLE_TRACE_CORRELATION", "") ctx = None - if self.__traceparent.traceparent and all(v is None for v in [trace_id, span_id, level]): + if level and "correlationType" in level: + trace_id = None + span_id = None + + if traceparent and all(v is None for v in [trace_id, span_id]): + tp_trace_id, tp_parent_id, tp_sampled = self.__tp.get_traceparent_fields(traceparent) if disable_traceparent == "": - ctx = SpanContext(span_id=self.__traceparent.parent_id, - trace_id=self.__traceparent.trace_id[-16:], - level=1, + ctx = SpanContext(span_id=tp_parent_id, + trace_id=tp_trace_id[-16:], + level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, synthetic=False if synthetic is None else True) ctx.trace_parent = True - try: - if self.__tracestate.tracestate and "in=" in self.__tracestate.tracestate: - in_list_member = self.__tracestate.tracestate.split("in=")[1].split(",")[0] - - ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], - parent_id=in_list_member.split(";")[1]) - - except Exception as e: - logger.debug("extract instana ancestor error:", exc_info=True) + if tracestate and "in=" in tracestate: + instana_ancestor = self.__ts.get_instana_ancestor(tracestate) + ctx.instana_ancestor = instana_ancestor - ctx.long_trace_id = self.__traceparent.trace_id + ctx.long_trace_id = tp_trace_id else: try: - if self.__tracestate.tracestate and "in=" in self.__tracestate.tracestate: - in_list_member = self.__tracestate.tracestate.split("in=")[1].split(",")[0] + if tracestate and "in=" in tracestate: + instana_ancestor = self.__ts.get_instana_ancestor(tracestate) - ctx = SpanContext(span_id=in_list_member.split(";")[1], - trace_id=in_list_member.split(";")[0], - level=1, + ctx = SpanContext(span_id=instana_ancestor.p, + trace_id=instana_ancestor.t, + level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, synthetic=False if synthetic is None else True) - ctx.instana_ancestor = InstanaAncestor(trace_id=in_list_member.split(";")[0], - parent_id=in_list_member.split(";")[1]) + ctx.instana_ancestor = instana_ancestor except Exception as e: logger.debug("extract instana ancestor error:", exc_info=True) + try: + if level and "correlationType" in level: + ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] + if level and "correlationId" in level: + ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] + except Exception as e: + logger.debug("extract instana correlation type/id error:", exc_info=True) + + ctx.traceparent = traceparent + ctx.tracestate = tracestate + elif trace_id and span_id: ctx = SpanContext(span_id=span_id, trace_id=trace_id, - level=int(level.split(",")[0]), + level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, synthetic=False if synthetic is None else True) - try: - if "correlationType" in level: - ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] - if "correlationId" in level: - ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] - except Exception as e: - logger.debug("extract instana correlation type/id error:", exc_info=True) - elif synthetic: ctx = SpanContext(synthetic=synthetic) @@ -214,7 +215,7 @@ def __extract_instana_headers(self, dc): elif self.LC_HEADER_KEY_S == lc_key: span_id = header_to_id(dc[key]) elif self.LC_HEADER_KEY_L == lc_key: - level = dc[key] + level = dc[key].decode("utf-8") if PY3 is True and isinstance(dc[key], bytes) else dc[key] elif self.LC_HEADER_KEY_SYNTHETIC == lc_key: synthetic = dc[key] in ['1', b'1'] @@ -223,7 +224,7 @@ def __extract_instana_headers(self, dc): elif self.ALT_LC_HEADER_KEY_S == lc_key: span_id = header_to_id(dc[key]) elif self.ALT_LC_HEADER_KEY_L == lc_key: - level = dc[key] + level = dc[key].decode("utf-8") if PY3 is True and isinstance(dc[key], bytes) else dc[key] elif self.ALT_LC_HEADER_KEY_SYNTHETIC == lc_key: synthetic = dc[key] in ['1', b'1'] diff --git a/instana/span_context.py b/instana/span_context.py index caf1db5a..25c505a5 100644 --- a/instana/span_context.py +++ b/instana/span_context.py @@ -20,11 +20,29 @@ def __init__( self.synthetic = synthetic self._baggage = baggage or {} - self.trace_parent = None + self.trace_parent = None # true/false flag self.instana_ancestor = None self.long_trace_id = None self.correlation_type = None self.correlation_id = None + self.traceparent = None # temporary storage of the validated traceparent header of the incoming request + self.tracestate = None # temporary storage of the tracestate header + + @property + def traceparent(self): + return self._traceparent + + @traceparent.setter + def traceparent(self, value): + self._traceparent = value + + @property + def tracestate(self): + return self._tracestate + + @tracestate.setter + def tracestate(self, value): + self._tracestate = value @property def trace_parent(self): diff --git a/instana/tracer.py b/instana/tracer.py index e6215342..081d367e 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -94,6 +94,8 @@ def start_span(self, ctx.instana_ancestor = parent_ctx.instana_ancestor ctx.correlation_type = parent_ctx.correlation_type ctx.correlation_id = parent_ctx.correlation_id + ctx.traceparent = parent_ctx.traceparent + ctx.tracestate = parent_ctx.tracestate else: ctx.trace_id = gid ctx.sampled = self.sampler.sampled(ctx.trace_id) diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index 068f25bd..2312232d 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -1,6 +1,9 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 +from ..log import logger + + class InstanaAncestor: def __init__(self, trace_id, parent_id): self.t = trace_id @@ -11,39 +14,53 @@ class Tracestate: MAX_NUMBER_OF_LIST_MEMBERS = 32 REMOVE_ENTRIES_LARGER_THAN = 128 - def __init__(self): - self.tracestate = None + @staticmethod + def extract_tracestate(headers): + """ + Method to extract the tracestate header + :param headers: dict + :return: tracestate value or None + """ + return headers.get('tracestate', None) - @property - def tracestate(self): - return self._tracestate + @staticmethod + def get_instana_ancestor(tracestate): + """ + Constructs the instana ancestor object and returns it + :param tracestate: the original tracestate value + :return: instana ancestor instance + """ + try: + in_list_member = tracestate.split("in=")[1].split(",")[0] - @tracestate.setter - def tracestate(self, value): - self._tracestate = value + ia = InstanaAncestor(trace_id=in_list_member.split(";")[0], + parent_id=in_list_member.split(";")[1]) + return ia - def extract_tracestate(self, headers): - self.tracestate = headers.get('tracestate', None) + except Exception as e: + logger.debug("extract instana ancestor error:", exc_info=True) + return None - def update_tracestate(self, in_trace_id, in_span_id): + def update_tracestate(self, tracestate, in_trace_id, in_span_id): """ Method to update the tracestate property with the instana trace_id and span_id + :param tracestate: original tracestate header :param in_trace_id: instana trace_id :param in_span_id: instana parent_id - :return: + :return: tracestate updated """ span_id = in_span_id.zfill(16) # if span_id is shorter than 16 characters we prepend zeros instana_tracestate = "in={};{}".format(in_trace_id, span_id) - if self.tracestate is None: - self.tracestate = instana_tracestate + if tracestate is None: + tracestate = instana_tracestate else: # tracestate can contain a max of 32 list members, if it contains up to 31 # we can safely add the instana one without the need to truncate anything - if len(self.tracestate.split(",")) <= self.MAX_NUMBER_OF_LIST_MEMBERS - 1: - self.tracestate = "{},{}".format(instana_tracestate, self.tracestate) + if len(tracestate.split(",")) <= self.MAX_NUMBER_OF_LIST_MEMBERS - 1: + tracestate = "{},{}".format(instana_tracestate, tracestate) else: - list_members = self.tracestate.split(",") + list_members = tracestate.split(",") list_members_to_remove = len(list_members) - self.MAX_NUMBER_OF_LIST_MEMBERS + 1 # Number 1 priority members to be removed are the ones larger than 128 characters for i, m in reversed(list(enumerate(list_members))): @@ -58,6 +75,8 @@ def update_tracestate(self, in_trace_id, in_span_id): list_members.pop() list_members_to_remove -= 1 # update the tracestate containing just 31 list members - self.tracestate = ",".join(list_members) + tracestate = ",".join(list_members) # adding instana as first list member, total of 32 list members - self.tracestate = "{},{}".format(instana_tracestate, self.tracestate) + tracestate = "{},{}".format(instana_tracestate, tracestate) + + return tracestate diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py index 41e59f03..07cd9049 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/treceparent.py @@ -9,83 +9,63 @@ class Traceparent: SPECIFICATION_VERSION = "00" TRACEPARENT_REGEX = re.compile("[0-9a-f]{2}-(?!0{32})([0-9a-f]{32})-(?!0{16})([0-9a-f]{16})-[0-9a-f]{2}") - def __init__(self): - self.traceparent = None - self.trace_id = None - self.parent_id = None - self.sampled = None - - @property - def traceparent(self): - return self._traceparent - - @traceparent.setter - def traceparent(self, value): - if value and self.TRACEPARENT_REGEX.match(value): - self._traceparent = value - else: - self._traceparent = None - - @property - def trace_id(self): - return self._trace_id - - @trace_id.setter - def trace_id(self, value): - self._trace_id = value - - @property - def parent_id(self): - return self._parent_id - - @parent_id.setter - def parent_id(self, value): - self._parent_id = value - - @property - def sampled(self): - return self._sampled - - @sampled.setter - def sampled(self, value): - self._sampled = value + def __validate(self, traceparent): + """ + Method used to validate the traceparent header + :param traceparent: string + :return: True or False + """ + try: + if self.TRACEPARENT_REGEX.match(traceparent) and traceparent.split("-")[0] == self.SPECIFICATION_VERSION: + return True + except Exception: + logger.debug("traceparent does not follow version 00 specification") + return False def extract_traceparent(self, headers): """ - Extracts from the headers dict the traceparent key/value and validates the value while trying to set the - traceparent property. If the validation succeeds all other traceparent sub-properties are getting extracted + Extracts from the headers dict the traceparent key/value and validates its value :param headers: dict with headers - :return: it sets the class properties accordingly + :return: the validated traceparent or None """ - self.traceparent = headers.get('traceparent', None) - if self.traceparent is None: - return + traceparent = headers.get('traceparent', None) + if traceparent and self.__validate(traceparent): + return traceparent + else: + return None - try: - traceparent_properties = self.traceparent.split("-") - if traceparent_properties[0] == self.SPECIFICATION_VERSION: - self.trace_id = traceparent_properties[1] - self.parent_id = traceparent_properties[2] - self.sampled = traceparent_properties[3] - else: - raise Exception - except Exception: - logger.debug("traceparent does not follow version 00 specification") + @staticmethod + def get_traceparent_fields(traceparent): + """ + Parses the validated traceparent header into its fields and returns the fields + :param traceparent: the original validated traceparent header + :return: trace_id, parent_id, sampled + """ + traceparent_properties = traceparent.split("-") + trace_id = traceparent_properties[1] + parent_id = traceparent_properties[2] + sampled = traceparent_properties[3] + return trace_id, parent_id, sampled - def update_traceparent(self, in_trace_id, in_span_id, level): + def update_traceparent(self, traceparent, in_trace_id, in_span_id, level): """ This method updates the traceparent header or generates one if there was no traceparent incoming header or it was invalid + :param traceparent: the original validated traceparent header :param in_trace_id: instana trace id, used when there is no preexisting trace_id from the traceparent header :param in_span_id: instana span id, used to update the parent id of the traceparent header :param level: instana level, used to determine the value of sampled flag of the traceparent header :return: sets the updated traceparent header """ - if self.trace_id is None: # modify the trace_id part only when it was not present at all - self.trace_id = in_trace_id.zfill(32) - self.parent_id = in_span_id.zfill(16) - self.sampled = "01" if level == 1 else "00" - self.traceparent = "{version}-{traceid}-{parentid}-{sampled}".format(version=self.SPECIFICATION_VERSION, - traceid=self.trace_id, - parentid=self.parent_id, - sampled=self.sampled) + + if traceparent is None: # modify the trace_id part only when it was not present at all + trace_id = in_trace_id.zfill(32) + else: + trace_id, _, _ = self.get_traceparent_fields(traceparent) + parent_id = in_span_id.zfill(16) + sampled = "01" if level == 1 else "00" + traceparent = "{version}-{traceid}-{parentid}-{sampled}".format(version=self.SPECIFICATION_VERSION, + traceid=trace_id, + parentid=parent_id, + sampled=sampled) + return traceparent diff --git a/tests/clients/test_pika.py b/tests/clients/test_pika.py index 931bb512..5d85afe5 100644 --- a/tests/clients/test_pika.py +++ b/tests/clients/test_pika.py @@ -13,6 +13,7 @@ from ..helpers import testenv from instana.singletons import tracer + class _TestPika(unittest.TestCase): @staticmethod @mock.patch('pika.connection.Connection') @@ -35,6 +36,7 @@ def tearDown(self): del self._on_openok_callback del self.obj + class TestPikaChannel(_TestPika): def _create_obj(self): return pika.channel.Channel(self.connection, 1, self._on_openok_callback) @@ -78,10 +80,12 @@ def test_basic_publish(self, send_method, _unused): pika.spec.Basic.Publish( exchange="test.exchange", routing_key="test.queue"), (pika.spec.BasicProperties(headers={ - "X-INSTANA-T": rabbitmq_span.t, - "X-INSTANA-S": rabbitmq_span.s, - "X-INSTANA-L": "1" - }), b"Hello!")) + 'traceparent': '00-0000000000000000{}-{}-01'.format(rabbitmq_span.t, rabbitmq_span.s), + 'tracestate': 'in={};{}'.format(rabbitmq_span.t, rabbitmq_span.s), + "X-INSTANA-T": rabbitmq_span.t, + "X-INSTANA-S": rabbitmq_span.s, + "X-INSTANA-L": "1" + }), b"Hello!")) @mock.patch('pika.spec.Basic.Publish') @mock.patch('pika.channel.Channel._send_method') @@ -106,11 +110,13 @@ def test_basic_publish_with_headers(self, send_method, _unused): pika.spec.Basic.Publish( exchange="test.exchange", routing_key="test.queue"), (pika.spec.BasicProperties(headers={ - "X-Custom-1": "test", - "X-INSTANA-T": rabbitmq_span.t, - "X-INSTANA-S": rabbitmq_span.s, - "X-INSTANA-L": "1" - }), b"Hello!")) + 'traceparent': '00-0000000000000000{}-{}-01'.format(rabbitmq_span.t, rabbitmq_span.s), + 'tracestate': 'in={};{}'.format(rabbitmq_span.t, rabbitmq_span.s), + "X-Custom-1": "test", + "X-INSTANA-T": rabbitmq_span.t, + "X-INSTANA-S": rabbitmq_span.s, + "X-INSTANA-L": "1" + }), b"Hello!")) @mock.patch('pika.spec.Basic.Get') def test_basic_get(self, _unused): @@ -262,6 +268,7 @@ def test_basic_consume_with_trace_context(self, _unused): self.assertIsNotNone(rabbitmq_span.s) self.assertNotEqual(rabbitmq_span.p, rabbitmq_span.s) + class TestPikaBlockingChannel(_TestPika): @mock.patch('pika.channel.Channel', spec=pika.channel.Channel) def _create_obj(self, channel_impl): @@ -282,6 +289,7 @@ def _generate_delivery(self, consumer_tag, properties, body): def test_consume(self): consumed_deliveries = [] + def __consume(): for delivery in self.obj.consume("test.queue", inactivity_timeout=3.0): # Skip deliveries generated due to inactivity @@ -331,6 +339,7 @@ def __consume(): def test_consume_with_trace_context(self): consumed_deliveries = [] + def __consume(): for delivery in self.obj.consume("test.queue", inactivity_timeout=3.0): # Skip deliveries generated due to inactivity From 48a73cfe83f0cb1c7c1d963a4d5670327d8f32c3 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Fri, 30 Jul 2021 12:43:40 +0200 Subject: [PATCH 10/39] add fixes discovered during testing --- instana/propagators/base_propagator.py | 172 ++++++++++++++---- instana/propagators/binary_propagator.py | 34 +++- instana/propagators/http_propagator.py | 175 +----------------- instana/w3c_trace_context/tracestate.py | 10 +- instana/w3c_trace_context/treceparent.py | 13 +- tests/frameworks/test_django.py | 153 +++++++++++++++- tests/frameworks/test_fastapi.py | 221 ++++++++++++----------- 7 files changed, 453 insertions(+), 325 deletions(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index c032df7d..0d2429d7 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -8,6 +8,9 @@ from ..log import logger from ..util.ids import header_to_id from ..span_context import SpanContext +from ..w3c_trace_context.treceparent import Traceparent +from ..w3c_trace_context.tracestate import Tracestate +import os PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -49,37 +52,143 @@ class BasePropagator(object): ALT_LC_HEADER_KEY_L = 'http_x_instana_l' ALT_LC_HEADER_KEY_SYNTHETIC = 'http_x_instana_synthetic' + HEADER_KEY_TRACEPARENT = "traceparent" + HEADER_KEY_TRACESTATE = "tracestate" + + def __init__(self): + self.__tp = Traceparent() + self.__ts = Tracestate() + + def __extract_headers_dict(self, carrier): + """ + This method converts the incoming carrier into a dict + :param carrier: + :return: dc dictionary + """ + try: + if isinstance(carrier, dict): + dc = carrier + elif hasattr(carrier, "__dict__"): + dc = carrier.__dict__ + else: + dc = dict(carrier) + except Exception: + logger.debug("extract: Couldn't convert %s", carrier) + dc = None + + return dc + def extract(self, carrier): """ - Search carrier for the *HEADER* keys and return a SpanContext or None + This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP + requests more extracting steps and logic was required + :param carrier: + :return: the context or None + """ + try: + headers = self.__extract_headers_dict(carrier=carrier) + if headers is None: + return None + headers = {k.lower(): v for k, v in headers.items()} + traceparent = self.__tp.extract_traceparent(headers) + tracestate = self.__ts.extract_tracestate(headers) - Note: Extract is on the base class since it never really varies in task regardless - of the propagator in uses. + trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) + ctx = self.__determine_span_context(trace_id, span_id, level, synthetic, traceparent, tracestate) - :param carrier: The dict or list potentially containing context - :return: SpanContext or None + return ctx + except Exception: + logger.debug("extract error:", exc_info=True) + + def __determine_span_context(self, trace_id, span_id, level, synthetic, traceparent, tracestate): """ - trace_id = None - span_id = None - level = 1 - synthetic = False - dc = None + This method determines the span context depending on a set of conditions being met + Detailed description of the conditions can be found here: + https://github.com/instana/technical-documentation/tree/master/tracing/specification#http-processing-for-instana-tracers + :param trace_id: instana trace id + :param span_id: instana span id + :param level: instana level + :param synthetic: instana synthetic + :return: ctx or None + """ + disable_traceparent = os.environ.get("INSTANA_W3C_DISABLE_TRACE_CORRELATION", "") + + ctx = None + if level and "correlationType" in level: + trace_id = None + span_id = None + + if traceparent and all(v is None for v in [trace_id, span_id]): + tp_trace_id, tp_parent_id, tp_sampled = self.__tp.get_traceparent_fields(traceparent) + if disable_traceparent == "": + ctx = SpanContext(span_id=tp_parent_id, + trace_id=tp_trace_id[-16:], + level=int(level.split(",")[0]) if level else 1, + baggage={}, + sampled=True, + synthetic=False if synthetic is None else True) + + ctx.trace_parent = True + + if tracestate and "in=" in tracestate: + instana_ancestor = self.__ts.get_instana_ancestor(tracestate) + ctx.instana_ancestor = instana_ancestor + + ctx.long_trace_id = tp_trace_id + else: + try: + if tracestate and "in=" in tracestate: + instana_ancestor = self.__ts.get_instana_ancestor(tracestate) + + ctx = SpanContext(span_id=instana_ancestor.p, + trace_id=instana_ancestor.t, + level=int(level.split(",")[0]) if level else 1, + baggage={}, + sampled=True, + synthetic=False if synthetic is None else True) + + ctx.instana_ancestor = instana_ancestor + except Exception as e: + logger.debug("extract instana ancestor error:", exc_info=True) - try: - # Attempt to convert incoming into a dict try: - if isinstance(carrier, dict): - dc = carrier - elif hasattr(carrier, "__dict__"): - dc = carrier.__dict__ - else: - dc = dict(carrier) - except Exception: - logger.debug("extract: Couln't convert %s", carrier) + if level and "correlationType" in level: + ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] + if level and "correlationId" in level: + ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] + except Exception as e: + logger.debug("extract instana correlation type/id error:", exc_info=True) - if dc is None: - return None + elif trace_id and span_id: + + ctx = SpanContext(span_id=span_id, + trace_id=trace_id, + level=int(level.split(",")[0]) if level else 1, + baggage={}, + sampled=True, + synthetic=False if synthetic is None else True) + + elif synthetic: + ctx = SpanContext(synthetic=synthetic) + + ctx.traceparent = traceparent + ctx.tracestate = tracestate + + return ctx + + def __extract_instana_headers(self, dc): + """ + Search carrier for the *HEADER* keys and return a SpanContext or None + :param dc: The dict or list potentially containing context + :return: trace_id, span_id, level, synthetic + """ + trace_id = None + span_id = None + level = None + synthetic = None + + try: # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style # We do a case insensitive search to cover all possible variations of incoming headers. for key in dc.keys(): @@ -95,7 +204,7 @@ def extract(self, carrier): elif self.LC_HEADER_KEY_S == lc_key: span_id = header_to_id(dc[key]) elif self.LC_HEADER_KEY_L == lc_key: - level = dc[key] + level = dc[key].decode("utf-8") if PY3 is True and isinstance(dc[key], bytes) else dc[key] elif self.LC_HEADER_KEY_SYNTHETIC == lc_key: synthetic = dc[key] in ['1', b'1'] @@ -104,22 +213,11 @@ def extract(self, carrier): elif self.ALT_LC_HEADER_KEY_S == lc_key: span_id = header_to_id(dc[key]) elif self.ALT_LC_HEADER_KEY_L == lc_key: - level = dc[key] + level = dc[key].decode("utf-8") if PY3 is True and isinstance(dc[key], bytes) else dc[key] elif self.ALT_LC_HEADER_KEY_SYNTHETIC == lc_key: synthetic = dc[key] in ['1', b'1'] - ctx = None - if trace_id is not None and span_id is not None: - ctx = SpanContext(span_id=span_id, - trace_id=trace_id, - level=level, - baggage={}, - sampled=True, - synthetic=synthetic) - elif synthetic: - ctx = SpanContext(synthetic=synthetic) - - return ctx - except Exception: logger.debug("extract error:", exc_info=True) + + return trace_id, span_id, level, synthetic diff --git a/instana/propagators/binary_propagator.py b/instana/propagators/binary_propagator.py index f22a691e..c828f84c 100644 --- a/instana/propagators/binary_propagator.py +++ b/instana/propagators/binary_propagator.py @@ -5,6 +5,8 @@ from ..log import logger from .base_propagator import BasePropagator +from ..w3c_trace_context.treceparent import Traceparent +from ..w3c_trace_context.tracestate import Tracestate class BinaryPropagator(BasePropagator): @@ -12,36 +14,62 @@ class BinaryPropagator(BasePropagator): A Propagator for BINARY. The BINARY format represents SpanContexts in an opaque bytearray carrier. """ - + # ByteArray variations from base class HEADER_KEY_T = b'x-instana-t' HEADER_KEY_S = b'x-instana-s' HEADER_KEY_L = b'x-instana-l' HEADER_SERVER_TIMING = b'server-timing' + HEADER_KEY_TRACEPARENT = b'traceparent' + HEADER_KEY_TRACESTATE = b'tracestate' + + def __init__(self): + self.__tp = Traceparent() + self.__ts = Tracestate() + super(BinaryPropagator, self).__init__() def inject(self, span_context, carrier): try: trace_id = str.encode(span_context.trace_id) span_id = str.encode(span_context.span_id) - level = str.encode("1") + level = str.encode(str(span_context.level)) server_timing = str.encode("intid;desc=%s" % span_context.trace_id) + traceparent = span_context.traceparent + tracestate = span_context.tracestate + traceparent = self.__tp.update_traceparent(traceparent, span_context.trace_id, span_context.span_id, + span_context.level) + tracestate = self.__ts.update_tracestate(tracestate, span_context.trace_id, span_context.span_id) + traceparent = str.encode(traceparent) + tracestate = str.encode(tracestate) if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): + if traceparent and tracestate: + carrier[self.HEADER_KEY_TRACEPARENT] = traceparent + carrier[self.HEADER_KEY_TRACESTATE] = tracestate carrier[self.HEADER_KEY_T] = trace_id carrier[self.HEADER_KEY_S] = span_id carrier[self.HEADER_KEY_L] = level carrier[self.HEADER_SERVER_TIMING] = server_timing elif isinstance(carrier, list): + if traceparent and tracestate: + carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) + carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) carrier.append((self.HEADER_KEY_T, trace_id)) carrier.append((self.HEADER_KEY_S, span_id)) carrier.append((self.HEADER_KEY_L, level)) carrier.append((self.HEADER_SERVER_TIMING, server_timing)) elif isinstance(carrier, tuple): + if traceparent and tracestate: + carrier.__add__(((self.HEADER_KEY_TRACEPARENT, traceparent),)) + carrier.__add__(((self.HEADER_KEY_TRACESTATE, tracestate),)) carrier = carrier.__add__(((self.HEADER_KEY_T, trace_id),)) carrier = carrier.__add__(((self.HEADER_KEY_S, span_id),)) carrier = carrier.__add__(((self.HEADER_KEY_L, level),)) carrier = carrier.__add__(((self.HEADER_SERVER_TIMING, server_timing),)) elif hasattr(carrier, '__setitem__'): + if traceparent and tracestate: + carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) + carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) carrier.__setitem__(self.HEADER_KEY_T, trace_id) carrier.__setitem__(self.HEADER_KEY_S, span_id) carrier.__setitem__(self.HEADER_KEY_L, level) @@ -50,5 +78,5 @@ def inject(self, span_context, carrier): raise Exception("Unsupported carrier type", type(carrier)) return carrier - except Exception: + except Exception as e: logger.debug("inject error:", exc_info=True) diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index 72040a26..8d08e3ca 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -8,11 +8,7 @@ from ..log import logger from .base_propagator import BasePropagator from ..w3c_trace_context.treceparent import Traceparent -from ..w3c_trace_context.tracestate import Tracestate, InstanaAncestor - -from ..util.ids import header_to_id -from ..span_context import SpanContext -import os +from ..w3c_trace_context.tracestate import Tracestate PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -25,8 +21,6 @@ class HTTPPropagator(BasePropagator): The HTTP_HEADERS format deals with key-values with string to string mapping. The character set should be restricted to HTTP compatible. """ - HEADER_KEY_TRACEPARENT = "traceparent" - HEADER_KEY_TRACESTATE = "tracestate" def __init__(self): self.__tp = Traceparent() @@ -67,168 +61,5 @@ def inject(self, span_context, carrier): else: raise Exception("Unsupported carrier type", type(carrier)) - except Exception: - logger.debug("inject error:", exc_info=True) - - def __extract_headers_dict(self, carrier): - """ - This method converts the incoming carrier into a dict - :param carrier: - :return: dc dictionary - """ - try: - if isinstance(carrier, dict): - dc = carrier - elif hasattr(carrier, "__dict__"): - dc = carrier.__dict__ - else: - dc = dict(carrier) - except Exception: - logger.debug("extract: Couldn't convert %s", carrier) - dc = None - - return dc - - def extract(self, carrier): - """ - This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP - requests more extracting steps and logic was required - :param carrier: - :return: the context or None - """ - try: - headers = self.__extract_headers_dict(carrier=carrier) - if headers is None: - return None - headers = {k.lower(): v for k, v in headers.items()} - traceparent = self.__tp.extract_traceparent(headers) - tracestate = self.__ts.extract_tracestate(headers) - - trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) - ctx = self.__determine_span_context(trace_id, span_id, level, synthetic, traceparent, tracestate) - - return ctx - except Exception: - logger.debug("extract error:", exc_info=True) - - def __determine_span_context(self, trace_id, span_id, level, synthetic, traceparent, tracestate): - """ - This method determines the span context depending on a set of conditions being met - Detailed description of the conditions can be found here: - https://github.com/instana/technical-documentation/tree/master/tracing/specification#http-processing-for-instana-tracers - :param trace_id: instana trace id - :param span_id: instana span id - :param level: instana level - :param synthetic: instana synthetic - :return: ctx or None - """ - disable_traceparent = os.environ.get("INSTANA_W3C_DISABLE_TRACE_CORRELATION", "") - - ctx = None - if level and "correlationType" in level: - trace_id = None - span_id = None - - if traceparent and all(v is None for v in [trace_id, span_id]): - tp_trace_id, tp_parent_id, tp_sampled = self.__tp.get_traceparent_fields(traceparent) - if disable_traceparent == "": - ctx = SpanContext(span_id=tp_parent_id, - trace_id=tp_trace_id[-16:], - level=int(level.split(",")[0]) if level else 1, - baggage={}, - sampled=True, - synthetic=False if synthetic is None else True) - - ctx.trace_parent = True - - if tracestate and "in=" in tracestate: - instana_ancestor = self.__ts.get_instana_ancestor(tracestate) - ctx.instana_ancestor = instana_ancestor - - ctx.long_trace_id = tp_trace_id - else: - try: - if tracestate and "in=" in tracestate: - instana_ancestor = self.__ts.get_instana_ancestor(tracestate) - - ctx = SpanContext(span_id=instana_ancestor.p, - trace_id=instana_ancestor.t, - level=int(level.split(",")[0]) if level else 1, - baggage={}, - sampled=True, - synthetic=False if synthetic is None else True) - - ctx.instana_ancestor = instana_ancestor - except Exception as e: - logger.debug("extract instana ancestor error:", exc_info=True) - - try: - if level and "correlationType" in level: - ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] - if level and "correlationId" in level: - ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] - except Exception as e: - logger.debug("extract instana correlation type/id error:", exc_info=True) - - ctx.traceparent = traceparent - ctx.tracestate = tracestate - - elif trace_id and span_id: - - ctx = SpanContext(span_id=span_id, - trace_id=trace_id, - level=int(level.split(",")[0]) if level else 1, - baggage={}, - sampled=True, - synthetic=False if synthetic is None else True) - - elif synthetic: - ctx = SpanContext(synthetic=synthetic) - - return ctx - - def __extract_instana_headers(self, dc): - """ - Search carrier for the *HEADER* keys and return a SpanContext or None - - :param dc: The dict or list potentially containing context - :return: trace_id, span_id, level, synthetic - """ - trace_id = None - span_id = None - level = None - synthetic = None - - try: - # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style - # We do a case insensitive search to cover all possible variations of incoming headers. - for key in dc.keys(): - lc_key = None - - if PY3 is True and isinstance(key, bytes): - lc_key = key.decode("utf-8").lower() - else: - lc_key = key.lower() - - if self.LC_HEADER_KEY_T == lc_key: - trace_id = header_to_id(dc[key]) - elif self.LC_HEADER_KEY_S == lc_key: - span_id = header_to_id(dc[key]) - elif self.LC_HEADER_KEY_L == lc_key: - level = dc[key].decode("utf-8") if PY3 is True and isinstance(dc[key], bytes) else dc[key] - elif self.LC_HEADER_KEY_SYNTHETIC == lc_key: - synthetic = dc[key] in ['1', b'1'] - - elif self.ALT_LC_HEADER_KEY_T == lc_key: - trace_id = header_to_id(dc[key]) - elif self.ALT_LC_HEADER_KEY_S == lc_key: - span_id = header_to_id(dc[key]) - elif self.ALT_LC_HEADER_KEY_L == lc_key: - level = dc[key].decode("utf-8") if PY3 is True and isinstance(dc[key], bytes) else dc[key] - elif self.ALT_LC_HEADER_KEY_SYNTHETIC == lc_key: - synthetic = dc[key] in ['1', b'1'] - - except Exception: - logger.debug("extract error:", exc_info=True) - - return trace_id, span_id, level, synthetic + except Exception as e: + logger.debug("inject error:", exc_info=True) \ No newline at end of file diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index 2312232d..b7282985 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -2,7 +2,9 @@ # (c) Copyright Instana Inc. 2021 from ..log import logger +import sys +PY3 = sys.version_info[0] == 3 class InstanaAncestor: def __init__(self, trace_id, parent_id): @@ -21,7 +23,11 @@ def extract_tracestate(headers): :param headers: dict :return: tracestate value or None """ - return headers.get('tracestate', None) + tracestate = headers.get('tracestate', None) or headers.get('http_tracestate', None) or headers.get( + b'tracestate', None) or headers.get(b'http_tracestate', None) + if PY3 is True and isinstance(tracestate, bytes): + tracestate = tracestate.decode("utf-8") + return tracestate @staticmethod def get_instana_ancestor(tracestate): @@ -31,7 +37,7 @@ def get_instana_ancestor(tracestate): :return: instana ancestor instance """ try: - in_list_member = tracestate.split("in=")[1].split(",")[0] + in_list_member = tracestate.strip().split("in=")[1].split(",")[0] ia = InstanaAncestor(trace_id=in_list_member.split(";")[0], parent_id=in_list_member.split(";")[1]) diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py index 07cd9049..ff6e1515 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/treceparent.py @@ -3,6 +3,9 @@ from ..log import logger import re +import sys + +PY3 = sys.version_info[0] == 3 class Traceparent: @@ -28,9 +31,13 @@ def extract_traceparent(self, headers): :param headers: dict with headers :return: the validated traceparent or None """ - traceparent = headers.get('traceparent', None) - if traceparent and self.__validate(traceparent): - return traceparent + traceparent = headers.get('traceparent', None) or headers.get('http_traceparent', None) or headers.get( + b'traceparent', None) or headers.get(b'http_traceparent', None) + if traceparent: + if PY3 is True and isinstance(traceparent, bytes): + traceparent = traceparent.decode("utf-8") + if self.__validate(traceparent): + return traceparent else: return None diff --git a/tests/frameworks/test_django.py b/tests/frameworks/test_django.py index bd743c91..0b3ce7c1 100644 --- a/tests/frameworks/test_django.py +++ b/tests/frameworks/test_django.py @@ -7,7 +7,7 @@ from django.apps import apps from ..apps.app_django import INSTALLED_APPS from django.contrib.staticfiles.testing import StaticLiveServerTestCase - +import os from instana.singletons import agent, tracer from ..helpers import fail_with_message_and_span_dump, get_first_span_by_filter, drop_log_spans_from_list @@ -310,6 +310,8 @@ def test_with_incoming_context(self): request_headers = dict() request_headers['X-INSTANA-T'] = '1' request_headers['X-INSTANA-S'] = '1' + request_headers['traceparent'] = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + request_headers['tracestate'] = 'rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE' response = self.http.request('GET', self.live_server_url + '/', headers=request_headers) @@ -335,6 +337,155 @@ def test_with_incoming_context(self): assert ('X-INSTANA-L' in response.headers) self.assertEqual('1', response.headers['X-INSTANA-L']) + assert ('traceparent' in response.headers) + self.assertEqual('00-4bf92f3577b34da6a3ce929d0e0e4736-{}-01'.format(django_span.s), + response.headers['traceparent']) + + assert ('tracestate' in response.headers) + self.assertEqual( + 'in={};{},rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE'.format( + django_span.t, django_span.s), response.headers['tracestate']) + server_timing_value = "intid;desc=%s" % django_span.t + assert ('Server-Timing' in response.headers) + self.assertEqual(server_timing_value, response.headers['Server-Timing']) + + def test_with_incoming_context_and_correlation(self): + request_headers = dict() + request_headers['X-INSTANA-T'] = '1' + request_headers['X-INSTANA-S'] = '1' + request_headers['X-INSTANA-L'] = '1, correlationType=web; correlationId=1234567890abcdef' + request_headers['traceparent'] = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + request_headers['tracestate'] = 'rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE' + + response = self.http.request('GET', self.live_server_url + '/', headers=request_headers) + + assert response + self.assertEqual(200, response.status) + + spans = self.recorder.queued_spans() + self.assertEqual(1, len(spans)) + + django_span = spans[0] + + self.assertEqual(django_span.t, 'a3ce929d0e0e4736') + self.assertEqual(django_span.p, '00f067aa0ba902b7') + self.assertEqual(django_span.ia.t, 'a3ce929d0e0e4736') + self.assertEqual(django_span.ia.p, '8357ccd9da194656') + self.assertEqual(django_span.lt, '4bf92f3577b34da6a3ce929d0e0e4736') + self.assertEqual(django_span.tp, True) + self.assertEqual(django_span.crtp, 'web') + self.assertEqual(django_span.crid, '1234567890abcdef') + + assert ('X-INSTANA-T' in response.headers) + assert (int(response.headers['X-INSTANA-T'], 16)) + self.assertEqual(django_span.t, response.headers['X-INSTANA-T']) + + assert ('X-INSTANA-S' in response.headers) + assert (int(response.headers['X-INSTANA-S'], 16)) + self.assertEqual(django_span.s, response.headers['X-INSTANA-S']) + + assert ('X-INSTANA-L' in response.headers) + self.assertEqual('1', response.headers['X-INSTANA-L']) + + assert ('traceparent' in response.headers) + self.assertEqual('00-4bf92f3577b34da6a3ce929d0e0e4736-{}-01'.format(django_span.s), + response.headers['traceparent']) + + assert ('tracestate' in response.headers) + self.assertEqual( + 'in={};{},rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE'.format( + django_span.t, django_span.s), response.headers['tracestate']) + server_timing_value = "intid;desc=%s" % django_span.t + assert ('Server-Timing' in response.headers) + self.assertEqual(server_timing_value, response.headers['Server-Timing']) + + def test_with_incoming_traceparent_tracestate(self): + request_headers = dict() + request_headers['traceparent'] = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + request_headers['tracestate'] = 'rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE' + + response = self.http.request('GET', self.live_server_url + '/', headers=request_headers) + + assert response + self.assertEqual(200, response.status) + + spans = self.recorder.queued_spans() + self.assertEqual(1, len(spans)) + + django_span = spans[0] + + self.assertEqual(django_span.t, 'a3ce929d0e0e4736') # last 16 chars from traceparent trace_id + self.assertEqual(django_span.p, '00f067aa0ba902b7') + self.assertEqual(django_span.ia.t, 'a3ce929d0e0e4736') + self.assertEqual(django_span.ia.p, '8357ccd9da194656') + self.assertEqual(django_span.lt, '4bf92f3577b34da6a3ce929d0e0e4736') + self.assertEqual(django_span.tp, True) + + assert ('X-INSTANA-T' in response.headers) + assert (int(response.headers['X-INSTANA-T'], 16)) + self.assertEqual(django_span.t, response.headers['X-INSTANA-T']) + + assert ('X-INSTANA-S' in response.headers) + assert (int(response.headers['X-INSTANA-S'], 16)) + self.assertEqual(django_span.s, response.headers['X-INSTANA-S']) + + assert ('X-INSTANA-L' in response.headers) + self.assertEqual('1', response.headers['X-INSTANA-L']) + + assert ('traceparent' in response.headers) + self.assertEqual('00-4bf92f3577b34da6a3ce929d0e0e4736-{}-01'.format(django_span.s), + response.headers['traceparent']) + + assert ('tracestate' in response.headers) + self.assertEqual( + 'in=a3ce929d0e0e4736;{},rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE'.format( + django_span.s), response.headers['tracestate']) + + server_timing_value = "intid;desc=%s" % django_span.t + assert ('Server-Timing' in response.headers) + self.assertEqual(server_timing_value, response.headers['Server-Timing']) + + def test_with_incoming_traceparent_tracestate_disable_traceparent(self): + os.environ["INSTANA_W3C_DISABLE_TRACE_CORRELATION"] = "1" + request_headers = dict() + request_headers['traceparent'] = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + request_headers['tracestate'] = 'rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE' + + response = self.http.request('GET', self.live_server_url + '/', headers=request_headers) + + assert response + self.assertEqual(200, response.status) + + spans = self.recorder.queued_spans() + self.assertEqual(1, len(spans)) + + django_span = spans[0] + + self.assertEqual(django_span.t, 'a3ce929d0e0e4736') # last 16 chars from traceparent trace_id + self.assertEqual(django_span.p, '8357ccd9da194656') + self.assertEqual(django_span.ia.t, 'a3ce929d0e0e4736') + self.assertEqual(django_span.ia.p, '8357ccd9da194656') + + assert ('X-INSTANA-T' in response.headers) + assert (int(response.headers['X-INSTANA-T'], 16)) + self.assertEqual(django_span.t, response.headers['X-INSTANA-T']) + + assert ('X-INSTANA-S' in response.headers) + assert (int(response.headers['X-INSTANA-S'], 16)) + self.assertEqual(django_span.s, response.headers['X-INSTANA-S']) + + assert ('X-INSTANA-L' in response.headers) + self.assertEqual('1', response.headers['X-INSTANA-L']) + + assert ('traceparent' in response.headers) + self.assertEqual('00-4bf92f3577b34da6a3ce929d0e0e4736-{}-01'.format(django_span.s), + response.headers['traceparent']) + + assert ('tracestate' in response.headers) + self.assertEqual( + 'in=a3ce929d0e0e4736;{},rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE'.format( + django_span.s), response.headers['tracestate']) + server_timing_value = "intid;desc=%s" % django_span.t assert ('Server-Timing' in response.headers) self.assertEqual(server_timing_value, response.headers['Server-Timing']) diff --git a/tests/frameworks/test_fastapi.py b/tests/frameworks/test_fastapi.py index f290f89a..624e251a 100644 --- a/tests/frameworks/test_fastapi.py +++ b/tests/frameworks/test_fastapi.py @@ -11,6 +11,7 @@ from ..helpers import testenv from ..helpers import get_first_span_by_filter + @pytest.fixture(scope="module") def server(): from tests.apps.fastapi_app import launch_fastapi @@ -18,7 +19,8 @@ def server(): proc.start() time.sleep(2) yield - proc.kill() # Kill server after tests + proc.kill() # Kill server after tests + def test_vanilla_get(server): result = requests.get(testenv["fastapi_server"] + '/') @@ -48,19 +50,19 @@ def test_basic_get(server): span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'test' test_span = get_first_span_by_filter(spans, span_filter) - assert(test_span) + assert (test_span) span_filter = lambda span: span.n == "urllib3" urllib3_span = get_first_span_by_filter(spans, span_filter) - assert(urllib3_span) + assert (urllib3_span) span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) - assert(asgi_span) + assert (asgi_span) - assert(test_span.t == urllib3_span.t == asgi_span.t) - assert(asgi_span.p == urllib3_span.s) - assert(urllib3_span.p == test_span.s) + assert (test_span.t == urllib3_span.t == asgi_span.t) + assert (asgi_span.p == urllib3_span.s) + assert (urllib3_span.p == test_span.s) assert "X-INSTANA-T" in result.headers assert result.headers["X-INSTANA-T"] == asgi_span.t @@ -71,14 +73,15 @@ def test_basic_get(server): assert "Server-Timing" in result.headers assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) - assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.ec == None) + assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') + assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') + assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') + assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') + assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) + assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) + assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + def test_400(server): result = None @@ -92,19 +95,19 @@ def test_400(server): span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'test' test_span = get_first_span_by_filter(spans, span_filter) - assert(test_span) + assert (test_span) span_filter = lambda span: span.n == "urllib3" urllib3_span = get_first_span_by_filter(spans, span_filter) - assert(urllib3_span) + assert (urllib3_span) span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) - assert(asgi_span) + assert (asgi_span) - assert(test_span.t == urllib3_span.t == asgi_span.t) - assert(asgi_span.p == urllib3_span.s) - assert(urllib3_span.p == test_span.s) + assert (test_span.t == urllib3_span.t == asgi_span.t) + assert (asgi_span.p == urllib3_span.s) + assert (urllib3_span.p == test_span.s) assert "X-INSTANA-T" in result.headers assert result.headers["X-INSTANA-T"] == asgi_span.t @@ -115,14 +118,15 @@ def test_400(server): assert "Server-Timing" in result.headers assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) - assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/400') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/400') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 400) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.ec == None) + assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') + assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/400') + assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/400') + assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') + assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 400) + assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) + assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + def test_500(server): result = None @@ -136,19 +140,19 @@ def test_500(server): span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'test' test_span = get_first_span_by_filter(spans, span_filter) - assert(test_span) + assert (test_span) span_filter = lambda span: span.n == "urllib3" urllib3_span = get_first_span_by_filter(spans, span_filter) - assert(urllib3_span) + assert (urllib3_span) span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) - assert(asgi_span) + assert (asgi_span) - assert(test_span.t == urllib3_span.t == asgi_span.t) - assert(asgi_span.p == urllib3_span.s) - assert(urllib3_span.p == test_span.s) + assert (test_span.t == urllib3_span.t == asgi_span.t) + assert (asgi_span.p == urllib3_span.s) + assert (urllib3_span.p == test_span.s) assert "X-INSTANA-T" in result.headers assert result.headers["X-INSTANA-T"] == asgi_span.t @@ -159,14 +163,15 @@ def test_500(server): assert "Server-Timing" in result.headers assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) - assert(asgi_span.ec == 1) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/500') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/500') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 500) - assert(asgi_span.data['sdk']['custom']['tags']['http.error'] == '500 response') - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.ec == 1) + assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') + assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/500') + assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/500') + assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') + assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 500) + assert (asgi_span.data['sdk']['custom']['tags']['http.error'] == '500 response') + assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + def test_path_templates(server): result = None @@ -180,19 +185,19 @@ def test_path_templates(server): span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'test' test_span = get_first_span_by_filter(spans, span_filter) - assert(test_span) + assert (test_span) span_filter = lambda span: span.n == "urllib3" urllib3_span = get_first_span_by_filter(spans, span_filter) - assert(urllib3_span) + assert (urllib3_span) span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) - assert(asgi_span) + assert (asgi_span) - assert(test_span.t == urllib3_span.t == asgi_span.t) - assert(asgi_span.p == urllib3_span.s) - assert(urllib3_span.p == test_span.s) + assert (test_span.t == urllib3_span.t == asgi_span.t) + assert (asgi_span.p == urllib3_span.s) + assert (urllib3_span.p == test_span.s) assert "X-INSTANA-T" in result.headers assert result.headers["X-INSTANA-T"] == asgi_span.t @@ -203,14 +208,15 @@ def test_path_templates(server): assert "Server-Timing" in result.headers assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) - assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/users/1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/users/{user_id}') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.ec == None) + assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') + assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/users/1') + assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/users/{user_id}') + assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') + assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) + assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) + assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + def test_secret_scrubbing(server): result = None @@ -224,19 +230,19 @@ def test_secret_scrubbing(server): span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'test' test_span = get_first_span_by_filter(spans, span_filter) - assert(test_span) + assert (test_span) span_filter = lambda span: span.n == "urllib3" urllib3_span = get_first_span_by_filter(spans, span_filter) - assert(urllib3_span) + assert (urllib3_span) span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) - assert(asgi_span) + assert (asgi_span) - assert(test_span.t == urllib3_span.t == asgi_span.t) - assert(asgi_span.p == urllib3_span.s) - assert(urllib3_span.p == test_span.s) + assert (test_span.t == urllib3_span.t == asgi_span.t) + assert (asgi_span.p == urllib3_span.s) + assert (urllib3_span.p == test_span.s) assert "X-INSTANA-T" in result.headers assert result.headers["X-INSTANA-T"] == asgi_span.t @@ -247,14 +253,14 @@ def test_secret_scrubbing(server): assert "Server-Timing" in result.headers assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) - assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.params'] == 'secret=') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.ec == None) + assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') + assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') + assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') + assert (asgi_span.data['sdk']['custom']['tags']['http.params'] == 'secret=') + assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') + assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) + assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) def test_synthetic_request(server): @@ -271,19 +277,19 @@ def test_synthetic_request(server): span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'test' test_span = get_first_span_by_filter(spans, span_filter) - assert(test_span) + assert (test_span) span_filter = lambda span: span.n == "urllib3" urllib3_span = get_first_span_by_filter(spans, span_filter) - assert(urllib3_span) + assert (urllib3_span) span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) - assert(asgi_span) + assert (asgi_span) - assert(test_span.t == urllib3_span.t == asgi_span.t) - assert(asgi_span.p == urllib3_span.s) - assert(urllib3_span.p == test_span.s) + assert (test_span.t == urllib3_span.t == asgi_span.t) + assert (asgi_span.p == urllib3_span.s) + assert (urllib3_span.p == test_span.s) assert "X-INSTANA-T" in result.headers assert result.headers["X-INSTANA-T"] == asgi_span.t @@ -294,18 +300,19 @@ def test_synthetic_request(server): assert "Server-Timing" in result.headers assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) - assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.ec == None) + assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') + assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') + assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') + assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') + assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) + assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) + assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + + assert (asgi_span.sy) + assert (urllib3_span.sy is None) + assert (test_span.sy is None) - assert(asgi_span.sy) - assert(urllib3_span.sy is None) - assert(test_span.sy is None) def test_custom_header_capture(server): from instana.singletons import agent @@ -326,19 +333,19 @@ def test_custom_header_capture(server): span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'test' test_span = get_first_span_by_filter(spans, span_filter) - assert(test_span) + assert (test_span) span_filter = lambda span: span.n == "urllib3" urllib3_span = get_first_span_by_filter(spans, span_filter) - assert(urllib3_span) + assert (urllib3_span) span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) - assert(asgi_span) + assert (asgi_span) - assert(test_span.t == urllib3_span.t == asgi_span.t) - assert(asgi_span.p == urllib3_span.s) - assert(urllib3_span.p == test_span.s) + assert (test_span.t == urllib3_span.t == asgi_span.t) + assert (asgi_span.p == urllib3_span.s) + assert (urllib3_span.p == test_span.s) assert "X-INSTANA-T" in result.headers assert result.headers["X-INSTANA-T"] == asgi_span.t @@ -349,16 +356,16 @@ def test_custom_header_capture(server): assert "Server-Timing" in result.headers assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) - assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) - - assert("http.header.X-Capture-This" in asgi_span.data["sdk"]["custom"]['tags']) - assert("this" == asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-This"]) - assert("http.header.X-Capture-That" in asgi_span.data["sdk"]["custom"]['tags']) - assert("that" == asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-That"]) + assert (asgi_span.ec == None) + assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') + assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') + assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') + assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') + assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) + assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) + assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + + assert ("http.header.X-Capture-This" in asgi_span.data["sdk"]["custom"]['tags']) + assert ("this" == asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-This"]) + assert ("http.header.X-Capture-That" in asgi_span.data["sdk"]["custom"]['tags']) + assert ("that" == asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-That"]) From ce513324a1222b71af64556575addc19f113008c Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Tue, 3 Aug 2021 12:28:05 +0200 Subject: [PATCH 11/39] performing some optimisations --- instana/propagators/base_propagator.py | 137 +++++++++++------------ instana/propagators/binary_propagator.py | 2 +- instana/propagators/http_propagator.py | 7 +- instana/w3c_trace_context/tracestate.py | 17 +-- instana/w3c_trace_context/treceparent.py | 29 +---- 5 files changed, 73 insertions(+), 119 deletions(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 0d2429d7..f95fa678 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -15,6 +15,7 @@ PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 + # The carrier can be a dict or a list. # Using the trace header as an example, it can be in the following forms # for extraction: @@ -29,11 +30,6 @@ class BasePropagator(object): - UC_HEADER_KEY_T = 'X-INSTANA-T' - UC_HEADER_KEY_S = 'X-INSTANA-S' - UC_HEADER_KEY_L = 'X-INSTANA-L' - UC_HEADER_KEY_SYNTHETIC = 'X-INSTANA-SYNTHETIC' - HEADER_KEY_T = 'X-INSTANA-T' HEADER_KEY_S = 'X-INSTANA-S' HEADER_KEY_L = 'X-INSTANA-L' @@ -44,9 +40,6 @@ class BasePropagator(object): LC_HEADER_KEY_L = 'x-instana-l' LC_HEADER_KEY_SYNTHETIC = 'x-instana-synthetic' - ALT_HEADER_KEY_T = 'HTTP_X_INSTANA_T' - ALT_HEADER_KEY_S = 'HTTP_X_INSTANA_S' - ALT_HEADER_KEY_L = 'HTTP_X_INSTANA_L' ALT_LC_HEADER_KEY_T = 'http_x_instana_t' ALT_LC_HEADER_KEY_S = 'http_x_instana_s' ALT_LC_HEADER_KEY_L = 'http_x_instana_l' @@ -55,11 +48,15 @@ class BasePropagator(object): HEADER_KEY_TRACEPARENT = "traceparent" HEADER_KEY_TRACESTATE = "tracestate" + ALT_HEADER_KEY_TRACEPARENT = "http_traceparent" + ALT_HEADER_KEY_TRACESTATE = "http_tracestate" + def __init__(self): self.__tp = Traceparent() self.__ts = Tracestate() - def __extract_headers_dict(self, carrier): + @staticmethod + def __extract_headers_dict(carrier): """ This method converts the incoming carrier into a dict :param carrier: @@ -90,10 +87,12 @@ def extract(self, carrier): if headers is None: return None headers = {k.lower(): v for k, v in headers.items()} - traceparent = self.__tp.extract_traceparent(headers) - tracestate = self.__ts.extract_tracestate(headers) - trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) + trace_id, span_id, level, synthetic, traceparent, tracestate = self.__extract_instana_headers(dc=headers) + + if traceparent: + traceparent = self.__tp.validate(traceparent) + ctx = self.__determine_span_context(trace_id, span_id, level, synthetic, traceparent, tracestate) return ctx @@ -115,17 +114,13 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar ctx = None if level and "correlationType" in level: - trace_id = None - span_id = None + trace_id, span_id = [None] * 2 if traceparent and all(v is None for v in [trace_id, span_id]): tp_trace_id, tp_parent_id, tp_sampled = self.__tp.get_traceparent_fields(traceparent) if disable_traceparent == "": - ctx = SpanContext(span_id=tp_parent_id, - trace_id=tp_trace_id[-16:], - level=int(level.split(",")[0]) if level else 1, - baggage={}, - sampled=True, + ctx = SpanContext(span_id=tp_parent_id, trace_id=tp_trace_id[-16:], + level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, synthetic=False if synthetic is None else True) ctx.trace_parent = True @@ -140,33 +135,20 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar if tracestate and "in=" in tracestate: instana_ancestor = self.__ts.get_instana_ancestor(tracestate) - ctx = SpanContext(span_id=instana_ancestor.p, - trace_id=instana_ancestor.t, - level=int(level.split(",")[0]) if level else 1, - baggage={}, - sampled=True, + ctx = SpanContext(span_id=instana_ancestor.p, trace_id=instana_ancestor.t, + level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, synthetic=False if synthetic is None else True) ctx.instana_ancestor = instana_ancestor - except Exception as e: + except Exception: logger.debug("extract instana ancestor error:", exc_info=True) - try: - if level and "correlationType" in level: - ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] - if level and "correlationId" in level: - ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] - except Exception as e: - logger.debug("extract instana correlation type/id error:", exc_info=True) + self.__set_correlation_properties(level, ctx) elif trace_id and span_id: - ctx = SpanContext(span_id=span_id, - trace_id=trace_id, - level=int(level.split(",")[0]) if level else 1, - baggage={}, - sampled=True, - synthetic=False if synthetic is None else True) + ctx = SpanContext(span_id=span_id, trace_id=trace_id, level=int(level.split(",")[0]) if level else 1, + baggage={}, sampled=True, synthetic=False if synthetic is None else True) elif synthetic: ctx = SpanContext(synthetic=synthetic) @@ -176,48 +158,59 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar return ctx + @staticmethod + def __set_correlation_properties(level, ctx): + try: + if level and "correlationType" in level: + ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] + if level and "correlationId" in level: + ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] + except Exception: + logger.debug("extract instana correlation type/id error:", exc_info=True) + def __extract_instana_headers(self, dc): """ - Search carrier for the *HEADER* keys and return a SpanContext or None + Search carrier for the *HEADER* keys and return the tracing key-values :param dc: The dict or list potentially containing context - :return: trace_id, span_id, level, synthetic + :return: trace_id, span_id, level, synthetic, traceparent, tracestate """ - trace_id = None - span_id = None - level = None - synthetic = None + trace_id, span_id, level, synthetic, traceparent, tracestate = [None] * 6 + # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style try: - # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style - # We do a case insensitive search to cover all possible variations of incoming headers. - for key in dc.keys(): - lc_key = None - - if PY3 is True and isinstance(key, bytes): - lc_key = key.decode("utf-8").lower() - else: - lc_key = key.lower() - - if self.LC_HEADER_KEY_T == lc_key: - trace_id = header_to_id(dc[key]) - elif self.LC_HEADER_KEY_S == lc_key: - span_id = header_to_id(dc[key]) - elif self.LC_HEADER_KEY_L == lc_key: - level = dc[key].decode("utf-8") if PY3 is True and isinstance(dc[key], bytes) else dc[key] - elif self.LC_HEADER_KEY_SYNTHETIC == lc_key: - synthetic = dc[key] in ['1', b'1'] - - elif self.ALT_LC_HEADER_KEY_T == lc_key: - trace_id = header_to_id(dc[key]) - elif self.ALT_LC_HEADER_KEY_S == lc_key: - span_id = header_to_id(dc[key]) - elif self.ALT_LC_HEADER_KEY_L == lc_key: - level = dc[key].decode("utf-8") if PY3 is True and isinstance(dc[key], bytes) else dc[key] - elif self.ALT_LC_HEADER_KEY_SYNTHETIC == lc_key: - synthetic = dc[key] in ['1', b'1'] + trace_id = dc.get(self.LC_HEADER_KEY_T) or dc.get(self.ALT_LC_HEADER_KEY_T) or dc.get( + self.LC_HEADER_KEY_T.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_T.encode("utf-8")) + if trace_id: + trace_id = header_to_id(trace_id) + + span_id = dc.get(self.LC_HEADER_KEY_S) or dc.get(self.ALT_LC_HEADER_KEY_S) or dc.get( + self.LC_HEADER_KEY_S.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_S.encode("utf-8")) + if span_id: + span_id = header_to_id(span_id) + + level = dc.get(self.LC_HEADER_KEY_L) or dc.get(self.ALT_LC_HEADER_KEY_L) or dc.get( + self.LC_HEADER_KEY_L.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_L.encode("utf-8")) + if level and PY3 is True and isinstance(level, bytes): + level = level.decode("utf-8") + + synthetic = dc.get(self.LC_HEADER_KEY_SYNTHETIC) or dc.get(self.ALT_LC_HEADER_KEY_SYNTHETIC) or dc.get( + self.LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) or dc.get( + self.ALT_LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) + if synthetic: + synthetic = synthetic in ['1', b'1'] + + traceparent = dc.get(self.HEADER_KEY_TRACEPARENT) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT) or dc.get( + self.HEADER_KEY_TRACEPARENT.encode("utf-8")) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT.encode("utf-8")) + if traceparent and PY3 is True and isinstance(traceparent, bytes): + traceparent = traceparent.decode("utf-8") + + tracestate = dc.get(self.HEADER_KEY_TRACESTATE) or dc.get(self.ALT_HEADER_KEY_TRACESTATE) or dc.get( + self.HEADER_KEY_TRACESTATE.encode("utf-8")) or dc.get(self.ALT_HEADER_KEY_TRACESTATE.encode("utf-8")) + if tracestate and PY3 is True and isinstance(tracestate, bytes): + tracestate = tracestate.decode("utf-8") except Exception: logger.debug("extract error:", exc_info=True) - return trace_id, span_id, level, synthetic + return trace_id, span_id, level, synthetic, traceparent, tracestate diff --git a/instana/propagators/binary_propagator.py b/instana/propagators/binary_propagator.py index c828f84c..3f36607f 100644 --- a/instana/propagators/binary_propagator.py +++ b/instana/propagators/binary_propagator.py @@ -78,5 +78,5 @@ def inject(self, span_context, carrier): raise Exception("Unsupported carrier type", type(carrier)) return carrier - except Exception as e: + except Exception: logger.debug("inject error:", exc_info=True) diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index 8d08e3ca..11b75d95 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -3,16 +3,11 @@ from __future__ import absolute_import -import sys - from ..log import logger from .base_propagator import BasePropagator from ..w3c_trace_context.treceparent import Traceparent from ..w3c_trace_context.tracestate import Tracestate -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - class HTTPPropagator(BasePropagator): """ @@ -61,5 +56,5 @@ def inject(self, span_context, carrier): else: raise Exception("Unsupported carrier type", type(carrier)) - except Exception as e: + except Exception: logger.debug("inject error:", exc_info=True) \ No newline at end of file diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index b7282985..fb7b1610 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -2,9 +2,7 @@ # (c) Copyright Instana Inc. 2021 from ..log import logger -import sys -PY3 = sys.version_info[0] == 3 class InstanaAncestor: def __init__(self, trace_id, parent_id): @@ -16,19 +14,6 @@ class Tracestate: MAX_NUMBER_OF_LIST_MEMBERS = 32 REMOVE_ENTRIES_LARGER_THAN = 128 - @staticmethod - def extract_tracestate(headers): - """ - Method to extract the tracestate header - :param headers: dict - :return: tracestate value or None - """ - tracestate = headers.get('tracestate', None) or headers.get('http_tracestate', None) or headers.get( - b'tracestate', None) or headers.get(b'http_tracestate', None) - if PY3 is True and isinstance(tracestate, bytes): - tracestate = tracestate.decode("utf-8") - return tracestate - @staticmethod def get_instana_ancestor(tracestate): """ @@ -43,7 +28,7 @@ def get_instana_ancestor(tracestate): parent_id=in_list_member.split(";")[1]) return ia - except Exception as e: + except Exception: logger.debug("extract instana ancestor error:", exc_info=True) return None diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py index ff6e1515..36614788 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/treceparent.py @@ -3,43 +3,24 @@ from ..log import logger import re -import sys - -PY3 = sys.version_info[0] == 3 class Traceparent: SPECIFICATION_VERSION = "00" TRACEPARENT_REGEX = re.compile("[0-9a-f]{2}-(?!0{32})([0-9a-f]{32})-(?!0{16})([0-9a-f]{16})-[0-9a-f]{2}") - def __validate(self, traceparent): + def validate(self, traceparent): """ Method used to validate the traceparent header :param traceparent: string - :return: True or False + :return: traceparent or None """ try: if self.TRACEPARENT_REGEX.match(traceparent) and traceparent.split("-")[0] == self.SPECIFICATION_VERSION: - return True + return traceparent except Exception: logger.debug("traceparent does not follow version 00 specification") - return False - - def extract_traceparent(self, headers): - """ - Extracts from the headers dict the traceparent key/value and validates its value - :param headers: dict with headers - :return: the validated traceparent or None - """ - traceparent = headers.get('traceparent', None) or headers.get('http_traceparent', None) or headers.get( - b'traceparent', None) or headers.get(b'http_traceparent', None) - if traceparent: - if PY3 is True and isinstance(traceparent, bytes): - traceparent = traceparent.decode("utf-8") - if self.__validate(traceparent): - return traceparent - else: - return None + return None @staticmethod def get_traceparent_fields(traceparent): @@ -62,7 +43,7 @@ def update_traceparent(self, traceparent, in_trace_id, in_span_id, level): :param in_trace_id: instana trace id, used when there is no preexisting trace_id from the traceparent header :param in_span_id: instana span id, used to update the parent id of the traceparent header :param level: instana level, used to determine the value of sampled flag of the traceparent header - :return: sets the updated traceparent header + :return: the updated traceparent header """ if traceparent is None: # modify the trace_id part only when it was not present at all From 18859f90c4f53173549880f6f2f1c8dd9b4c4b98 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Tue, 3 Aug 2021 14:29:08 +0200 Subject: [PATCH 12/39] adding unittests for traceparent and tarcestate --- instana/w3c_trace_context/tracestate.py | 56 +++++++++-------- instana/w3c_trace_context/treceparent.py | 15 +++-- tests/propagators/__init__.py | 0 tests/w3c_trace_context/__init__.py | 0 tests/w3c_trace_context/test_traceparent.py | 59 ++++++++++++++++++ tests/w3c_trace_context/test_tracestate.py | 69 +++++++++++++++++++++ 6 files changed, 168 insertions(+), 31 deletions(-) create mode 100644 tests/propagators/__init__.py create mode 100644 tests/w3c_trace_context/__init__.py create mode 100644 tests/w3c_trace_context/test_traceparent.py create mode 100644 tests/w3c_trace_context/test_tracestate.py diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index fb7b1610..55278b8c 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -41,33 +41,37 @@ def update_tracestate(self, tracestate, in_trace_id, in_span_id): :param in_span_id: instana parent_id :return: tracestate updated """ - span_id = in_span_id.zfill(16) # if span_id is shorter than 16 characters we prepend zeros - instana_tracestate = "in={};{}".format(in_trace_id, span_id) - if tracestate is None: - tracestate = instana_tracestate - else: - # tracestate can contain a max of 32 list members, if it contains up to 31 - # we can safely add the instana one without the need to truncate anything - if len(tracestate.split(",")) <= self.MAX_NUMBER_OF_LIST_MEMBERS - 1: - tracestate = "{},{}".format(instana_tracestate, tracestate) + try: + span_id = in_span_id.zfill(16) # if span_id is shorter than 16 characters we prepend zeros + instana_tracestate = "in={};{}".format(in_trace_id, span_id) + if tracestate is None or tracestate == "": + tracestate = instana_tracestate else: - list_members = tracestate.split(",") - list_members_to_remove = len(list_members) - self.MAX_NUMBER_OF_LIST_MEMBERS + 1 - # Number 1 priority members to be removed are the ones larger than 128 characters - for i, m in reversed(list(enumerate(list_members))): - if len(m) > self.REMOVE_ENTRIES_LARGER_THAN: - list_members.pop(i) + # tracestate can contain a max of 32 list members, if it contains up to 31 + # we can safely add the instana one without the need to truncate anything + if len(tracestate.split(",")) <= self.MAX_NUMBER_OF_LIST_MEMBERS - 1: + tracestate = "{},{}".format(instana_tracestate, tracestate) + else: + list_members = tracestate.split(",") + list_members_to_remove = len(list_members) - self.MAX_NUMBER_OF_LIST_MEMBERS + 1 + # Number 1 priority members to be removed are the ones larger than 128 characters + for i, m in reversed(list(enumerate(list_members))): + if len(m) > self.REMOVE_ENTRIES_LARGER_THAN: + list_members.pop(i) + list_members_to_remove -= 1 + if list_members_to_remove == 0: + break + # if there are still more than 31 list members remaining, we remove as many members + # from the end as necessary to remain just 31 list members + while list_members_to_remove > 0: + list_members.pop() list_members_to_remove -= 1 - if list_members_to_remove == 0: - break - # if there are still more than 31 list members remaining, we remove as many members - # from the end as necessary to remain just 31 list members - while list_members_to_remove > 0: - list_members.pop() - list_members_to_remove -= 1 - # update the tracestate containing just 31 list members - tracestate = ",".join(list_members) - # adding instana as first list member, total of 32 list members - tracestate = "{},{}".format(instana_tracestate, tracestate) + # update the tracestate containing just 31 list members + tracestate = ",".join(list_members) + # adding instana as first list member, total of 32 list members + tracestate = "{},{}".format(instana_tracestate, tracestate) + except Exception: + logger.debug("Something went wrong while updating tracestate: {}:".format(tracestate), exc_info=True) + return "" # return empty string since None could trigger an exception in an attempt to encode it return tracestate diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/treceparent.py index 36614788..5ba8b83a 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/treceparent.py @@ -29,11 +29,16 @@ def get_traceparent_fields(traceparent): :param traceparent: the original validated traceparent header :return: trace_id, parent_id, sampled """ - traceparent_properties = traceparent.split("-") - trace_id = traceparent_properties[1] - parent_id = traceparent_properties[2] - sampled = traceparent_properties[3] - return trace_id, parent_id, sampled + try: + traceparent_properties = traceparent.split("-") + trace_id = traceparent_properties[1] + parent_id = traceparent_properties[2] + sampled = traceparent_properties[3] + return trace_id, parent_id, sampled + except Exception: # This method is intended to be called with a version 00 validated traceparent + # This exception handling is added just for making sure we do not throw any unhandled exception + # if somebody calls the method in the future without a validated traceparent + return None, None, None def update_traceparent(self, traceparent, in_trace_id, in_span_id, level): """ diff --git a/tests/propagators/__init__.py b/tests/propagators/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/w3c_trace_context/__init__.py b/tests/w3c_trace_context/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/w3c_trace_context/test_traceparent.py b/tests/w3c_trace_context/test_traceparent.py new file mode 100644 index 00000000..3932db33 --- /dev/null +++ b/tests/w3c_trace_context/test_traceparent.py @@ -0,0 +1,59 @@ +# (c) Copyright IBM Corp. 2021 +# (c) Copyright Instana Inc. 2021 + +from instana.w3c_trace_context.treceparent import Traceparent +import unittest + + +class TestTraceparent(unittest.TestCase): + def setUp(self): + self.tp = Traceparent() + + def test_validate_valid(self): + traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" + self.assertEqual(traceparent, self.tp.validate(traceparent)) + + def test_validate_invalid_traceparent(self): + traceparent = "00-4bxxxxx3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" + self.assertIsNone(self.tp.validate(traceparent)) + + def test_validate_traceparent_None(self): + traceparent = None + self.assertIsNone(self.tp.validate(traceparent)) + + def test_get_traceparent_fields(self): + traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" + trace_id, parent_id, sampled = self.tp.get_traceparent_fields(traceparent) + self.assertEqual(trace_id, "4bf92f3577b34da6a3ce929d0e0e4736") + self.assertEqual(parent_id, "00f067aa0ba902b7") + self.assertEqual(sampled, "01") + + def test_get_traceparent_fields_None_input(self): + traceparent = None + trace_id, parent_id, sampled = self.tp.get_traceparent_fields(traceparent) + self.assertIsNone(trace_id) + self.assertIsNone(parent_id) + self.assertIsNone(sampled) + + def test_get_traceparent_fields_string_input_no_dash(self): + traceparent = "invalid" + trace_id, parent_id, sampled = self.tp.get_traceparent_fields(traceparent) + self.assertIsNone(trace_id) + self.assertIsNone(parent_id) + self.assertIsNone(sampled) + + def test_update_traceparent(self): + traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" + in_trace_id = "1234d0e0e4736234" + in_span_id = "1234567890abcdef" + level = 1 + expected_traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-1234567890abcdef-01" + self.assertEqual(expected_traceparent, self.tp.update_traceparent(traceparent, in_trace_id, in_span_id, level)) + + def test_update_traceparent_None(self): + traceparent = None + in_trace_id = "1234d0e0e4736234" + in_span_id = "7890abcdef" + level = 0 + expected_traceparent = "00-00000000000000001234d0e0e4736234-0000007890abcdef-00" + self.assertEqual(expected_traceparent, self.tp.update_traceparent(traceparent, in_trace_id, in_span_id, level)) diff --git a/tests/w3c_trace_context/test_tracestate.py b/tests/w3c_trace_context/test_tracestate.py new file mode 100644 index 00000000..c12d48a3 --- /dev/null +++ b/tests/w3c_trace_context/test_tracestate.py @@ -0,0 +1,69 @@ +# (c) Copyright IBM Corp. 2021 +# (c) Copyright Instana Inc. 2021 + +from instana.w3c_trace_context.tracestate import Tracestate +import unittest + + +class TestTracestate(unittest.TestCase): + def setUp(self): + self.ts = Tracestate() + + def test_get_instana_ancestor(self): + tracestate = "congo=t61rcWkgMzE,in=1234d0e0e4736234;1234567890abcdef" + ia = self.ts.get_instana_ancestor(tracestate) + self.assertEqual(ia.t, "1234d0e0e4736234") + self.assertEqual(ia.p, "1234567890abcdef") + + def test_get_instana_ancestor_no_in(self): + tracestate = "congo=t61rcWkgMzE" + self.assertIsNone(self.ts.get_instana_ancestor(tracestate)) + + def test_get_instana_ancestor_tracestate_None(self): + tracestate = None + self.assertIsNone(self.ts.get_instana_ancestor(tracestate)) + + def test_update_tracestate(self): + tracestate = "congo=t61rcWkgMzE" + in_trace_id = "1234d0e0e4736234" + in_span_id = "1234567890abcdef" + expected_tracestate = "in=1234d0e0e4736234;1234567890abcdef,congo=t61rcWkgMzE" + self.assertEqual(expected_tracestate, self.ts.update_tracestate(tracestate, in_trace_id, in_span_id)) + + def test_update_tracestate_None(self): + tracestate = None + in_trace_id = "1234d0e0e4736234" + in_span_id = "1234567890abcdef" + expected_tracestate = "in=1234d0e0e4736234;1234567890abcdef" + self.assertEqual(expected_tracestate, self.ts.update_tracestate(tracestate, in_trace_id, in_span_id)) + + def test_update_tracestate_more_than_32_members_already(self): + tracestate = "congo=t61rcWkgMzE,robo=1221213jdfjkdsfjsd,alpha=5889fnjkllllllll," \ + "beta=aslsdklkljfdshasfaskkfnnnsdsd,gamadeltaepsilonpirpsigma=125646845613675451535445155126666fgsdfdsfjsdfhsdfsdsdsaddfasfdfdsfdsfsd;qwertyuiopasdfghjklzxcvbnm1234567890," \ + "b=121,c=23344,d=asd,e=ldkfj,f=1212121,g=sadahsda,h=jjhdada,i=eerjrjrr,j=sadsasd,k=44444,l=dadadad," \ + "m=rrrr,n=3424jdg,p=ffss,q=12,r=3,s=5,t=u5,u=43,v=gj,w=wew,x=23123,y=sdf,z=kasdl,aa=dsdas,ab=res," \ + "ac=trwa,ad=kll,ae=pds" + in_trace_id = "1234d0e0e4736234" + in_span_id = "1234567890abcdef" + expected_tracestate = "in=1234d0e0e4736234;1234567890abcdef,congo=t61rcWkgMzE,robo=1221213jdfjkdsfjsd," \ + "alpha=5889fnjkllllllll,beta=aslsdklkljfdshasfaskkfnnnsdsd,b=121,c=23344,d=asd,e=ldkfj," \ + "f=1212121,g=sadahsda,h=jjhdada,i=eerjrjrr,j=sadsasd,k=44444,l=dadadad,m=rrrr,n=3424jdg," \ + "p=ffss,q=12,r=3,s=5,t=u5,u=43,v=gj,w=wew,x=23123,y=sdf,z=kasdl,aa=dsdas,ab=res,ac=trwa" + actual_tracestate = self.ts.update_tracestate(tracestate, in_trace_id, in_span_id) + self.assertEqual(expected_tracestate, actual_tracestate) + self.assertNotIn("gamadeltaepsilonpirpsigma", + actual_tracestate) # member longer than 128 characters gets removed + + def test_update_tracestate_empty_string(self): + tracestate = "" + in_trace_id = "1234d0e0e4736234" + in_span_id = "1234567890abcdef" + expected_tracestate = "in=1234d0e0e4736234;1234567890abcdef" + self.assertEqual(expected_tracestate, self.ts.update_tracestate(tracestate, in_trace_id, in_span_id)) + + def test_update_tracestate_exception(self): + tracestate = [] + in_trace_id = "1234d0e0e4736234" + in_span_id = "1234567890abcdef" + expected_tracestate = "" + self.assertEqual(expected_tracestate, self.ts.update_tracestate(tracestate, in_trace_id, in_span_id)) \ No newline at end of file From 7e1218ebfcb5cef7bc325c7b2b6edbb5c8c7a614 Mon Sep 17 00:00:00 2001 From: Dimitra Paraskevopoulou Date: Tue, 3 Aug 2021 14:46:06 +0200 Subject: [PATCH 13/39] Update instana/propagators/base_propagator.py Co-authored-by: Pablo Aguiar --- instana/propagators/base_propagator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index f95fa678..0dc3a165 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -121,7 +121,7 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar if disable_traceparent == "": ctx = SpanContext(span_id=tp_parent_id, trace_id=tp_trace_id[-16:], level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, - synthetic=False if synthetic is None else True) + synthetic=synthetic is not None) ctx.trace_parent = True From 6d8a9a1069e531be07bfee4cb2e3c33db4b86fb0 Mon Sep 17 00:00:00 2001 From: Dimitra Paraskevopoulou Date: Tue, 3 Aug 2021 14:46:12 +0200 Subject: [PATCH 14/39] Update instana/propagators/base_propagator.py Co-authored-by: Pablo Aguiar --- instana/propagators/base_propagator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 0dc3a165..351a579d 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -137,7 +137,7 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar ctx = SpanContext(span_id=instana_ancestor.p, trace_id=instana_ancestor.t, level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, - synthetic=False if synthetic is None else True) + synthetic=synthetic is not None) ctx.instana_ancestor = instana_ancestor except Exception: From e7e1e8db8add34003576988b5c4e0193e9324657 Mon Sep 17 00:00:00 2001 From: Dimitra Paraskevopoulou Date: Tue, 3 Aug 2021 14:46:20 +0200 Subject: [PATCH 15/39] Update instana/propagators/base_propagator.py Co-authored-by: Pablo Aguiar --- instana/propagators/base_propagator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 351a579d..e3166283 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -148,7 +148,7 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar elif trace_id and span_id: ctx = SpanContext(span_id=span_id, trace_id=trace_id, level=int(level.split(",")[0]) if level else 1, - baggage={}, sampled=True, synthetic=False if synthetic is None else True) + baggage={}, sampled=True, synthetic=synthetic is not None) elif synthetic: ctx = SpanContext(synthetic=synthetic) From c888613427b0a622cf31d2e45d5666f439c72fe7 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Fri, 6 Aug 2021 14:13:27 +0200 Subject: [PATCH 16/39] adding some unittests and apply some fixes based on review comments --- instana/propagators/base_propagator.py | 42 +++++++------- instana/propagators/binary_propagator.py | 2 +- instana/propagators/http_propagator.py | 2 +- .../{treceparent.py => traceparent.py} | 7 +-- tests/frameworks/test_django.py | 2 +- tests/propagators/test_base_propagator.py | 58 +++++++++++++++++++ tests/w3c_trace_context/test_traceparent.py | 11 ++-- tests/w3c_trace_context/test_tracestate.py | 2 + 8 files changed, 92 insertions(+), 34 deletions(-) rename instana/w3c_trace_context/{treceparent.py => traceparent.py} (93%) create mode 100644 tests/propagators/test_base_propagator.py diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index e3166283..a9a6b873 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -8,7 +8,7 @@ from ..log import logger from ..util.ids import header_to_id from ..span_context import SpanContext -from ..w3c_trace_context.treceparent import Traceparent +from ..w3c_trace_context.traceparent import Traceparent from ..w3c_trace_context.tracestate import Tracestate import os @@ -111,32 +111,38 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar :return: ctx or None """ disable_traceparent = os.environ.get("INSTANA_W3C_DISABLE_TRACE_CORRELATION", "") - + instana_ancestor = None ctx = None if level and "correlationType" in level: trace_id, span_id = [None] * 2 - if traceparent and all(v is None for v in [trace_id, span_id]): - tp_trace_id, tp_parent_id, tp_sampled = self.__tp.get_traceparent_fields(traceparent) + ctx_level = int(level.split(",")[0]) if level else 1 + + if trace_id and span_id: + + ctx = SpanContext(span_id=span_id, trace_id=trace_id, level=ctx_level, + baggage={}, sampled=True, synthetic=synthetic is not None) + + elif traceparent and trace_id is None and span_id is None: + tp_trace_id, tp_parent_id = self.__tp.get_traceparent_fields(traceparent) + + if tracestate and "in=" in tracestate: + instana_ancestor = self.__ts.get_instana_ancestor(tracestate) + if disable_traceparent == "": ctx = SpanContext(span_id=tp_parent_id, trace_id=tp_trace_id[-16:], - level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, + level=ctx_level, baggage={}, sampled=True, synthetic=synthetic is not None) ctx.trace_parent = True - - if tracestate and "in=" in tracestate: - instana_ancestor = self.__ts.get_instana_ancestor(tracestate) - ctx.instana_ancestor = instana_ancestor + ctx.instana_ancestor = instana_ancestor ctx.long_trace_id = tp_trace_id else: try: - if tracestate and "in=" in tracestate: - instana_ancestor = self.__ts.get_instana_ancestor(tracestate) - + if instana_ancestor: ctx = SpanContext(span_id=instana_ancestor.p, trace_id=instana_ancestor.t, - level=int(level.split(",")[0]) if level else 1, baggage={}, sampled=True, + level=ctx_level, baggage={}, sampled=True, synthetic=synthetic is not None) ctx.instana_ancestor = instana_ancestor @@ -145,16 +151,12 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar self.__set_correlation_properties(level, ctx) - elif trace_id and span_id: - - ctx = SpanContext(span_id=span_id, trace_id=trace_id, level=int(level.split(",")[0]) if level else 1, - baggage={}, sampled=True, synthetic=synthetic is not None) - elif synthetic: ctx = SpanContext(synthetic=synthetic) - ctx.traceparent = traceparent - ctx.tracestate = tracestate + if ctx and traceparent: + ctx.traceparent = traceparent + ctx.tracestate = tracestate return ctx diff --git a/instana/propagators/binary_propagator.py b/instana/propagators/binary_propagator.py index 3f36607f..f1668473 100644 --- a/instana/propagators/binary_propagator.py +++ b/instana/propagators/binary_propagator.py @@ -5,7 +5,7 @@ from ..log import logger from .base_propagator import BasePropagator -from ..w3c_trace_context.treceparent import Traceparent +from ..w3c_trace_context.traceparent import Traceparent from ..w3c_trace_context.tracestate import Tracestate diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index 11b75d95..ee8fd1c8 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -5,7 +5,7 @@ from ..log import logger from .base_propagator import BasePropagator -from ..w3c_trace_context.treceparent import Traceparent +from ..w3c_trace_context.traceparent import Traceparent from ..w3c_trace_context.tracestate import Tracestate diff --git a/instana/w3c_trace_context/treceparent.py b/instana/w3c_trace_context/traceparent.py similarity index 93% rename from instana/w3c_trace_context/treceparent.py rename to instana/w3c_trace_context/traceparent.py index 5ba8b83a..d7761b87 100644 --- a/instana/w3c_trace_context/treceparent.py +++ b/instana/w3c_trace_context/traceparent.py @@ -33,12 +33,11 @@ def get_traceparent_fields(traceparent): traceparent_properties = traceparent.split("-") trace_id = traceparent_properties[1] parent_id = traceparent_properties[2] - sampled = traceparent_properties[3] - return trace_id, parent_id, sampled + return trace_id, parent_id except Exception: # This method is intended to be called with a version 00 validated traceparent # This exception handling is added just for making sure we do not throw any unhandled exception # if somebody calls the method in the future without a validated traceparent - return None, None, None + return None, None def update_traceparent(self, traceparent, in_trace_id, in_span_id, level): """ @@ -54,7 +53,7 @@ def update_traceparent(self, traceparent, in_trace_id, in_span_id, level): if traceparent is None: # modify the trace_id part only when it was not present at all trace_id = in_trace_id.zfill(32) else: - trace_id, _, _ = self.get_traceparent_fields(traceparent) + trace_id, _ = self.get_traceparent_fields(traceparent) parent_id = in_span_id.zfill(16) sampled = "01" if level == 1 else "00" traceparent = "{version}-{traceid}-{parentid}-{sampled}".format(version=self.SPECIFICATION_VERSION, diff --git a/tests/frameworks/test_django.py b/tests/frameworks/test_django.py index 0b3ce7c1..9635ed6e 100644 --- a/tests/frameworks/test_django.py +++ b/tests/frameworks/test_django.py @@ -24,7 +24,7 @@ def setUp(self): def tearDown(self): """ Do nothing for now """ - return None + os.environ["INSTANA_W3C_DISABLE_TRACE_CORRELATION"] = "" def test_basic_request(self): with tracer.start_active_span('test'): diff --git a/tests/propagators/test_base_propagator.py b/tests/propagators/test_base_propagator.py new file mode 100644 index 00000000..defe67da --- /dev/null +++ b/tests/propagators/test_base_propagator.py @@ -0,0 +1,58 @@ +# (c) Copyright IBM Corp. 2021 +# (c) Copyright Instana Inc. 2021 + +from instana.propagators.base_propagator import BasePropagator +from instana.w3c_trace_context.traceparent import Traceparent +from instana.w3c_trace_context.tracestate import Tracestate +from mock import patch, MagicMock +import unittest + + +class TestBasePropagator(unittest.TestCase): + def setUp(self): + self.bp = BasePropagator() + + @patch.object(Traceparent, "get_traceparent_fields") + @patch.object(Traceparent, "validate") + def test_extract_carrier_dict(self, mock_validate, mock_get_traceparent_fields): + carrier = { + 'traceparent': '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01', + 'tracestate': 'congo=t61rcWkgMzE', + 'X-INSTANA-T': '1234d0e0e4736234', + 'X-INSTANA-S': '1234567890abcdef', + 'X-INSTANA-L': '1, correlationType=web; correlationId=1234567890abcdef' + } + mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + mock_get_traceparent_fields.return_value = ["4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7"] + ctx = self.bp.extract(carrier) + self.assertEqual(ctx.correlation_id, '1234567890abcdef') + self.assertEqual(ctx.correlation_type, "web") + self.assertIsNone(ctx.instana_ancestor) + self.assertEqual(ctx.level, 1) + self.assertEqual(ctx.long_trace_id, "4bf92f3577b34da6a3ce929d0e0e4736") + self.assertEqual(ctx.span_id, "00f067aa0ba902b7") + self.assertFalse(ctx.synthetic) + self.assertEqual(ctx.trace_id, "a3ce929d0e0e4736") # 16 last chars from traceparent trace_id + self.assertTrue(ctx.trace_parent) + self.assertEqual(ctx.traceparent, '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01') + self.assertEqual(ctx.tracestate, 'congo=t61rcWkgMzE') + + @patch.object(Traceparent, "validate") + def test_extract_carrier_dict_validate_Exception_None_returned(self, mock_validate): + """ + In this test case the traceparent header fails the validation, so traceparent and tracestate are not gonna used + Additionally because in the instana L header the correlation flags are present we need to start a new ctx and + the present values of 'X-INSTANA-T', 'X-INSTANA-S' headers should no be used. This means the ctx should be None + :param mock_validate: + :return: + """ + carrier = { + 'traceparent': '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01', + 'tracestate': 'congo=t61rcWkgMzE', + 'X-INSTANA-T': '1234d0e0e4736234', + 'X-INSTANA-S': '1234567890abcdef', + 'X-INSTANA-L': '1, correlationType=web; correlationId=1234567890abcdef' + } + mock_validate.return_value = None + ctx = self.bp.extract(carrier) + self.assertIsNone(ctx) \ No newline at end of file diff --git a/tests/w3c_trace_context/test_traceparent.py b/tests/w3c_trace_context/test_traceparent.py index 3932db33..b112b397 100644 --- a/tests/w3c_trace_context/test_traceparent.py +++ b/tests/w3c_trace_context/test_traceparent.py @@ -1,7 +1,7 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 -from instana.w3c_trace_context.treceparent import Traceparent +from instana.w3c_trace_context.traceparent import Traceparent import unittest @@ -23,24 +23,21 @@ def test_validate_traceparent_None(self): def test_get_traceparent_fields(self): traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" - trace_id, parent_id, sampled = self.tp.get_traceparent_fields(traceparent) + trace_id, parent_id = self.tp.get_traceparent_fields(traceparent) self.assertEqual(trace_id, "4bf92f3577b34da6a3ce929d0e0e4736") self.assertEqual(parent_id, "00f067aa0ba902b7") - self.assertEqual(sampled, "01") def test_get_traceparent_fields_None_input(self): traceparent = None - trace_id, parent_id, sampled = self.tp.get_traceparent_fields(traceparent) + trace_id, parent_id = self.tp.get_traceparent_fields(traceparent) self.assertIsNone(trace_id) self.assertIsNone(parent_id) - self.assertIsNone(sampled) def test_get_traceparent_fields_string_input_no_dash(self): traceparent = "invalid" - trace_id, parent_id, sampled = self.tp.get_traceparent_fields(traceparent) + trace_id, parent_id = self.tp.get_traceparent_fields(traceparent) self.assertIsNone(trace_id) self.assertIsNone(parent_id) - self.assertIsNone(sampled) def test_update_traceparent(self): traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" diff --git a/tests/w3c_trace_context/test_tracestate.py b/tests/w3c_trace_context/test_tracestate.py index c12d48a3..0ef4497c 100644 --- a/tests/w3c_trace_context/test_tracestate.py +++ b/tests/w3c_trace_context/test_tracestate.py @@ -50,6 +50,8 @@ def test_update_tracestate_more_than_32_members_already(self): "f=1212121,g=sadahsda,h=jjhdada,i=eerjrjrr,j=sadsasd,k=44444,l=dadadad,m=rrrr,n=3424jdg," \ "p=ffss,q=12,r=3,s=5,t=u5,u=43,v=gj,w=wew,x=23123,y=sdf,z=kasdl,aa=dsdas,ab=res,ac=trwa" actual_tracestate = self.ts.update_tracestate(tracestate, in_trace_id, in_span_id) + self.assertEqual(len(tracestate.split(",")), 34) # input had 34 list members + self.assertEqual(len(actual_tracestate.split(",")), 32) # output has 32 list members, 3 removed and 1 added self.assertEqual(expected_tracestate, actual_tracestate) self.assertNotIn("gamadeltaepsilonpirpsigma", actual_tracestate) # member longer than 128 characters gets removed From 9ee7db914048266a867d4e889d20517594317cba Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Mon, 9 Aug 2021 12:44:32 +0200 Subject: [PATCH 17/39] adding unittests and apply some fixes based on review comments --- instana/instrumentation/pika.py | 4 +-- instana/propagators/base_propagator.py | 2 +- instana/w3c_trace_context/traceparent.py | 28 +++++++++++------- instana/w3c_trace_context/tracestate.py | 1 - tests/frameworks/test_django.py | 4 +-- tests/propagators/test_base_propagator.py | 32 +++++++++++++++++++-- tests/w3c_trace_context/test_traceparent.py | 6 ++-- tests/w3c_trace_context/test_tracestate.py | 2 +- 8 files changed, 57 insertions(+), 22 deletions(-) diff --git a/instana/instrumentation/pika.py b/instana/instrumentation/pika.py index 9e552c0a..3b844082 100644 --- a/instana/instrumentation/pika.py +++ b/instana/instrumentation/pika.py @@ -49,7 +49,7 @@ def _bind_args(exchange, routing_key, body, properties=None, *args, **kwargs): (exchange, routing_key, body, properties, args, kwargs) = (_bind_args(*args, **kwargs)) - with active_tracer.start_active_span("rabbitmq", child_of=active_tracer.active_span) as scope: + with tracer.start_active_span("rabbitmq", child_of=active_tracer.active_span) as scope: try: _extract_publisher_tags(scope.span, conn=instance.connection, @@ -62,7 +62,7 @@ def _bind_args(exchange, routing_key, body, properties=None, *args, **kwargs): properties = properties or pika.BasicProperties() properties.headers = properties.headers or {} - active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, properties.headers) + tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, properties.headers) args = (exchange, routing_key, body, properties) + args try: diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index a9a6b873..57b19007 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -124,7 +124,7 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar baggage={}, sampled=True, synthetic=synthetic is not None) elif traceparent and trace_id is None and span_id is None: - tp_trace_id, tp_parent_id = self.__tp.get_traceparent_fields(traceparent) + _, tp_trace_id, tp_parent_id, _ = self.__tp.get_traceparent_fields(traceparent) if tracestate and "in=" in tracestate: instana_ancestor = self.__ts.get_instana_ancestor(tracestate) diff --git a/instana/w3c_trace_context/traceparent.py b/instana/w3c_trace_context/traceparent.py index d7761b87..b2194889 100644 --- a/instana/w3c_trace_context/traceparent.py +++ b/instana/w3c_trace_context/traceparent.py @@ -7,7 +7,7 @@ class Traceparent: SPECIFICATION_VERSION = "00" - TRACEPARENT_REGEX = re.compile("[0-9a-f]{2}-(?!0{32})([0-9a-f]{32})-(?!0{16})([0-9a-f]{16})-[0-9a-f]{2}") + TRACEPARENT_REGEX = re.compile("^[0-9a-f]{2}-(?!0{32})([0-9a-f]{32})-(?!0{16})([0-9a-f]{16})-[0-9a-f]{2}") def validate(self, traceparent): """ @@ -16,7 +16,7 @@ def validate(self, traceparent): :return: traceparent or None """ try: - if self.TRACEPARENT_REGEX.match(traceparent) and traceparent.split("-")[0] == self.SPECIFICATION_VERSION: + if self.TRACEPARENT_REGEX.match(traceparent): return traceparent except Exception: logger.debug("traceparent does not follow version 00 specification") @@ -27,17 +27,19 @@ def get_traceparent_fields(traceparent): """ Parses the validated traceparent header into its fields and returns the fields :param traceparent: the original validated traceparent header - :return: trace_id, parent_id, sampled + :return: version, trace_id, parent_id, trace_flags """ try: traceparent_properties = traceparent.split("-") + version = traceparent_properties[0] trace_id = traceparent_properties[1] parent_id = traceparent_properties[2] - return trace_id, parent_id + trace_flags = traceparent_properties[3] + return version, trace_id, parent_id, trace_flags except Exception: # This method is intended to be called with a version 00 validated traceparent # This exception handling is added just for making sure we do not throw any unhandled exception # if somebody calls the method in the future without a validated traceparent - return None, None + return None, None, None, None def update_traceparent(self, traceparent, in_trace_id, in_span_id, level): """ @@ -49,15 +51,21 @@ def update_traceparent(self, traceparent, in_trace_id, in_span_id, level): :param level: instana level, used to determine the value of sampled flag of the traceparent header :return: the updated traceparent header """ - + mask = 1 << 0 + trace_flags = 0 if traceparent is None: # modify the trace_id part only when it was not present at all trace_id = in_trace_id.zfill(32) + version = self.SPECIFICATION_VERSION else: - trace_id, _ = self.get_traceparent_fields(traceparent) + version, trace_id, _, trace_flags = self.get_traceparent_fields(traceparent) + trace_flags = int(trace_flags, 16) + parent_id = in_span_id.zfill(16) - sampled = "01" if level == 1 else "00" - traceparent = "{version}-{traceid}-{parentid}-{sampled}".format(version=self.SPECIFICATION_VERSION, + trace_flags = (trace_flags & ~mask) | ((level << 0) & mask) + trace_flags = format(trace_flags, '0>2x') + + traceparent = "{version}-{traceid}-{parentid}-{trace_flags}".format(version=version, traceid=trace_id, parentid=parent_id, - sampled=sampled) + trace_flags=trace_flags) return traceparent diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index 55278b8c..10f88171 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -72,6 +72,5 @@ def update_tracestate(self, tracestate, in_trace_id, in_span_id): tracestate = "{},{}".format(instana_tracestate, tracestate) except Exception: logger.debug("Something went wrong while updating tracestate: {}:".format(tracestate), exc_info=True) - return "" # return empty string since None could trigger an exception in an attempt to encode it return tracestate diff --git a/tests/frameworks/test_django.py b/tests/frameworks/test_django.py index 9635ed6e..1dc422ec 100644 --- a/tests/frameworks/test_django.py +++ b/tests/frameworks/test_django.py @@ -310,7 +310,7 @@ def test_with_incoming_context(self): request_headers = dict() request_headers['X-INSTANA-T'] = '1' request_headers['X-INSTANA-S'] = '1' - request_headers['traceparent'] = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + request_headers['traceparent'] = '01-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01-788777' request_headers['tracestate'] = 'rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE' response = self.http.request('GET', self.live_server_url + '/', headers=request_headers) @@ -338,7 +338,7 @@ def test_with_incoming_context(self): self.assertEqual('1', response.headers['X-INSTANA-L']) assert ('traceparent' in response.headers) - self.assertEqual('00-4bf92f3577b34da6a3ce929d0e0e4736-{}-01'.format(django_span.s), + self.assertEqual('01-4bf92f3577b34da6a3ce929d0e0e4736-{}-01'.format(django_span.s), response.headers['traceparent']) assert ('tracestate' in response.headers) diff --git a/tests/propagators/test_base_propagator.py b/tests/propagators/test_base_propagator.py index defe67da..7b83a6e9 100644 --- a/tests/propagators/test_base_propagator.py +++ b/tests/propagators/test_base_propagator.py @@ -23,7 +23,7 @@ def test_extract_carrier_dict(self, mock_validate, mock_get_traceparent_fields): 'X-INSTANA-L': '1, correlationType=web; correlationId=1234567890abcdef' } mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' - mock_get_traceparent_fields.return_value = ["4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7"] + mock_get_traceparent_fields.return_value = ["00", "4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7", "01"] ctx = self.bp.extract(carrier) self.assertEqual(ctx.correlation_id, '1234567890abcdef') self.assertEqual(ctx.correlation_type, "web") @@ -37,6 +37,33 @@ def test_extract_carrier_dict(self, mock_validate, mock_get_traceparent_fields): self.assertEqual(ctx.traceparent, '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01') self.assertEqual(ctx.tracestate, 'congo=t61rcWkgMzE') + + @patch.object(Traceparent, "get_traceparent_fields") + @patch.object(Traceparent, "validate") + def test_extract_carrier_list(self, mock_validate, mock_get_traceparent_fields): + carrier = [(b'user-agent', b'python-requests/2.23.0'), (b'accept-encoding', b'gzip, deflate'), + (b'accept', b'*/*'), (b'connection', b'keep-alive'), + (b'traceparent', b'00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'), + (b'tracestate', b'congo=t61rcWkgMzE'), + (b'X-INSTANA-T', b'1234d0e0e4736234'), + (b'X-INSTANA-S', b'1234567890abcdef'), + (b'X-INSTANA-L', b'1')] + + mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + mock_get_traceparent_fields.return_value = ["00", "4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7", "01"] + ctx = self.bp.extract(carrier) + self.assertIsNone(ctx.correlation_id) + self.assertIsNone(ctx.correlation_type) + self.assertIsNone(ctx.instana_ancestor) + self.assertEqual(ctx.level, 1) + self.assertIsNone(ctx.long_trace_id) + self.assertEqual(ctx.span_id, "1234567890abcdef") + self.assertFalse(ctx.synthetic) + self.assertEqual(ctx.trace_id, "1234d0e0e4736234") # 16 last chars from traceparent trace_id + self.assertIsNone(ctx.trace_parent) + self.assertEqual(ctx.traceparent, '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01') + self.assertEqual(ctx.tracestate, 'congo=t61rcWkgMzE') + @patch.object(Traceparent, "validate") def test_extract_carrier_dict_validate_Exception_None_returned(self, mock_validate): """ @@ -55,4 +82,5 @@ def test_extract_carrier_dict_validate_Exception_None_returned(self, mock_valida } mock_validate.return_value = None ctx = self.bp.extract(carrier) - self.assertIsNone(ctx) \ No newline at end of file + self.assertIsNone(ctx) + diff --git a/tests/w3c_trace_context/test_traceparent.py b/tests/w3c_trace_context/test_traceparent.py index b112b397..11c29381 100644 --- a/tests/w3c_trace_context/test_traceparent.py +++ b/tests/w3c_trace_context/test_traceparent.py @@ -23,19 +23,19 @@ def test_validate_traceparent_None(self): def test_get_traceparent_fields(self): traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01" - trace_id, parent_id = self.tp.get_traceparent_fields(traceparent) + version, trace_id, parent_id, trace_flags = self.tp.get_traceparent_fields(traceparent) self.assertEqual(trace_id, "4bf92f3577b34da6a3ce929d0e0e4736") self.assertEqual(parent_id, "00f067aa0ba902b7") def test_get_traceparent_fields_None_input(self): traceparent = None - trace_id, parent_id = self.tp.get_traceparent_fields(traceparent) + version, trace_id, parent_id, trace_flags = self.tp.get_traceparent_fields(traceparent) self.assertIsNone(trace_id) self.assertIsNone(parent_id) def test_get_traceparent_fields_string_input_no_dash(self): traceparent = "invalid" - trace_id, parent_id = self.tp.get_traceparent_fields(traceparent) + version, trace_id, parent_id, trace_flags = self.tp.get_traceparent_fields(traceparent) self.assertIsNone(trace_id) self.assertIsNone(parent_id) diff --git a/tests/w3c_trace_context/test_tracestate.py b/tests/w3c_trace_context/test_tracestate.py index 0ef4497c..8bc0ce22 100644 --- a/tests/w3c_trace_context/test_tracestate.py +++ b/tests/w3c_trace_context/test_tracestate.py @@ -67,5 +67,5 @@ def test_update_tracestate_exception(self): tracestate = [] in_trace_id = "1234d0e0e4736234" in_span_id = "1234567890abcdef" - expected_tracestate = "" + expected_tracestate = [] self.assertEqual(expected_tracestate, self.ts.update_tracestate(tracestate, in_trace_id, in_span_id)) \ No newline at end of file From ff51688099dd16f29a6fb992bbc5aa37acdbeceb Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Mon, 9 Aug 2021 15:44:28 +0200 Subject: [PATCH 18/39] default binary w3c injection is disabled, it can be enabled as per instrumentation, right now only for ASGI --- instana/instrumentation/asgi.py | 9 +++-- instana/propagators/base_propagator.py | 20 +++++----- instana/propagators/binary_propagator.py | 22 +++++++---- instana/tracer.py | 6 ++- tests/propagators/test_base_propagator.py | 45 ++++++++++++++++++++++- 5 files changed, 78 insertions(+), 24 deletions(-) diff --git a/instana/instrumentation/asgi.py b/instana/instrumentation/asgi.py index f9eb9c9e..f8478a10 100644 --- a/instana/instrumentation/asgi.py +++ b/instana/instrumentation/asgi.py @@ -10,10 +10,12 @@ from ..singletons import async_tracer, agent from ..util.secrets import strip_secrets_from_query + class InstanaASGIMiddleware: """ Instana ASGI Middleware """ + def __init__(self, app): self.app = app @@ -41,7 +43,8 @@ def _collect_kvs(self, scope, span): if isinstance(query, (str, bytes)) and len(query): if isinstance(query, bytes): query = query.decode('utf-8') - scrubbed_params = strip_secrets_from_query(query, agent.options.secrets_matcher, agent.options.secrets_list) + scrubbed_params = strip_secrets_from_query(query, agent.options.secrets_matcher, + agent.options.secrets_list) span.set_tag("http.params", scrubbed_params) app = scope.get('app') @@ -55,7 +58,6 @@ def _collect_kvs(self, scope, span): except Exception: logger.debug("ASGI collect_kvs: ", exc_info=True) - async def __call__(self, scope, receive, send): request_context = None @@ -82,7 +84,8 @@ async def send_wrapper(response): headers = response.get('headers') if headers is not None: - async_tracer.inject(span.context, opentracing.Format.BINARY, headers) + async_tracer.inject(span.context, opentracing.Format.BINARY, headers, + binary_w3c_injection=True) except Exception: logger.debug("send_wrapper: ", exc_info=True) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 57b19007..89606423 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -116,7 +116,10 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar if level and "correlationType" in level: trace_id, span_id = [None] * 2 - ctx_level = int(level.split(",")[0]) if level else 1 + try: + ctx_level = int(level.split(",")[0]) if level else 1 + except Exception: + ctx_level = 1 if trace_id and span_id: @@ -139,15 +142,12 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar ctx.long_trace_id = tp_trace_id else: - try: - if instana_ancestor: - ctx = SpanContext(span_id=instana_ancestor.p, trace_id=instana_ancestor.t, - level=ctx_level, baggage={}, sampled=True, - synthetic=synthetic is not None) - - ctx.instana_ancestor = instana_ancestor - except Exception: - logger.debug("extract instana ancestor error:", exc_info=True) + if instana_ancestor: + ctx = SpanContext(span_id=instana_ancestor.p, trace_id=instana_ancestor.t, + level=ctx_level, baggage={}, sampled=True, + synthetic=synthetic is not None) + + ctx.instana_ancestor = instana_ancestor self.__set_correlation_properties(level, ctx) diff --git a/instana/propagators/binary_propagator.py b/instana/propagators/binary_propagator.py index f1668473..008b28af 100644 --- a/instana/propagators/binary_propagator.py +++ b/instana/propagators/binary_propagator.py @@ -28,19 +28,25 @@ def __init__(self): self.__ts = Tracestate() super(BinaryPropagator, self).__init__() - def inject(self, span_context, carrier): + def inject(self, span_context, carrier, binary_w3c_injection=False): try: trace_id = str.encode(span_context.trace_id) span_id = str.encode(span_context.span_id) level = str.encode(str(span_context.level)) server_timing = str.encode("intid;desc=%s" % span_context.trace_id) - traceparent = span_context.traceparent - tracestate = span_context.tracestate - traceparent = self.__tp.update_traceparent(traceparent, span_context.trace_id, span_context.span_id, - span_context.level) - tracestate = self.__ts.update_tracestate(tracestate, span_context.trace_id, span_context.span_id) - traceparent = str.encode(traceparent) - tracestate = str.encode(tracestate) + if binary_w3c_injection: + traceparent = span_context.traceparent + tracestate = span_context.tracestate + traceparent = self.__tp.update_traceparent(traceparent, span_context.trace_id, span_context.span_id, + span_context.level) + tracestate = self.__ts.update_tracestate(tracestate, span_context.trace_id, span_context.span_id) + try: + traceparent = str.encode(traceparent) + tracestate = str.encode(tracestate) + except Exception: + traceparent, tracestate = [None] * 2 + else: + traceparent, tracestate = [None] * 2 if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): if traceparent and tracestate: diff --git a/instana/tracer.py b/instana/tracer.py index 081d367e..9667eb8a 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -116,9 +116,11 @@ def start_span(self, return span - def inject(self, span_context, format, carrier): - if format in self._propagators: + def inject(self, span_context, format, carrier, binary_w3c_injection=False): + if format in self._propagators and format != ot.Format.BINARY: return self._propagators[format].inject(span_context, carrier) + elif format == ot.Format.BINARY: + return self._propagators[format].inject(span_context, carrier, binary_w3c_injection) raise ot.UnsupportedFormatException() diff --git a/tests/propagators/test_base_propagator.py b/tests/propagators/test_base_propagator.py index 7b83a6e9..d9a9ddde 100644 --- a/tests/propagators/test_base_propagator.py +++ b/tests/propagators/test_base_propagator.py @@ -37,7 +37,6 @@ def test_extract_carrier_dict(self, mock_validate, mock_get_traceparent_fields): self.assertEqual(ctx.traceparent, '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01') self.assertEqual(ctx.tracestate, 'congo=t61rcWkgMzE') - @patch.object(Traceparent, "get_traceparent_fields") @patch.object(Traceparent, "validate") def test_extract_carrier_list(self, mock_validate, mock_get_traceparent_fields): @@ -84,3 +83,47 @@ def test_extract_carrier_dict_validate_Exception_None_returned(self, mock_valida ctx = self.bp.extract(carrier) self.assertIsNone(ctx) + @patch.object(Traceparent, "validate") + def test_extract_fake_exception(self, mock_validate): + carrier = { + 'traceparent': '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01', + 'tracestate': 'congo=t61rcWkgMzE', + 'X-INSTANA-T': '1234d0e0e4736234', + 'X-INSTANA-S': '1234567890abcdef', + 'X-INSTANA-L': '1, correlationType=web; correlationId=1234567890abcdef' + } + mock_validate.side_effect = Exception + ctx = self.bp.extract(carrier) + self.assertIsNone(ctx) + + @patch.object(Traceparent, "get_traceparent_fields") + @patch.object(Traceparent, "validate") + def test_extract_carrier_dict_corrupted_level_header(self, mock_validate, mock_get_traceparent_fields): + """ + In this test case the traceparent header fails the validation, so traceparent and tracestate are not gonna used + Additionally because in the instana L header the correlation flags are present we need to start a new ctx and + the present values of 'X-INSTANA-T', 'X-INSTANA-S' headers should no be used. This means the ctx should be None + :param mock_validate: + :return: + """ + carrier = { + 'traceparent': '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01', + 'tracestate': 'congo=t61rcWkgMzE', + 'X-INSTANA-T': '1234d0e0e4736234', + 'X-INSTANA-S': '1234567890abcdef', + 'X-INSTANA-L': '1, correlationTypeweb; correlationId1234567890abcdef' + } + mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + mock_get_traceparent_fields.return_value = ["00", "4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7", "01"] + ctx = self.bp.extract(carrier) + self.assertIsNone(ctx.correlation_id) + self.assertIsNone(ctx.correlation_type) + self.assertIsNone(ctx.instana_ancestor) + self.assertEqual(ctx.level, 1) + self.assertEqual(ctx.long_trace_id, '4bf92f3577b34da6a3ce929d0e0e4736') + self.assertEqual(ctx.span_id, "00f067aa0ba902b7") + self.assertFalse(ctx.synthetic) + self.assertEqual(ctx.trace_id, "a3ce929d0e0e4736") # 16 last chars from traceparent trace_id + self.assertTrue(ctx.trace_parent) + self.assertEqual(ctx.traceparent, '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01') + self.assertEqual(ctx.tracestate, 'congo=t61rcWkgMzE') \ No newline at end of file From c844e8fa35a3df611348ee7ec2bd78b6c8a58f5f Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Tue, 10 Aug 2021 06:39:31 +0200 Subject: [PATCH 19/39] adding unittests --- instana/propagators/binary_propagator.py | 4 +- tests/propagators/test_base_propagator.py | 35 +++++++++- tests/propagators/test_binary_propagator.py | 76 +++++++++++++++++++++ 3 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 tests/propagators/test_binary_propagator.py diff --git a/instana/propagators/binary_propagator.py b/instana/propagators/binary_propagator.py index 008b28af..25ea8121 100644 --- a/instana/propagators/binary_propagator.py +++ b/instana/propagators/binary_propagator.py @@ -66,8 +66,8 @@ def inject(self, span_context, carrier, binary_w3c_injection=False): carrier.append((self.HEADER_SERVER_TIMING, server_timing)) elif isinstance(carrier, tuple): if traceparent and tracestate: - carrier.__add__(((self.HEADER_KEY_TRACEPARENT, traceparent),)) - carrier.__add__(((self.HEADER_KEY_TRACESTATE, tracestate),)) + carrier = carrier.__add__(((self.HEADER_KEY_TRACEPARENT, traceparent),)) + carrier = carrier.__add__(((self.HEADER_KEY_TRACESTATE, tracestate),)) carrier = carrier.__add__(((self.HEADER_KEY_T, trace_id),)) carrier = carrier.__add__(((self.HEADER_KEY_S, span_id),)) carrier = carrier.__add__(((self.HEADER_KEY_L, level),)) diff --git a/tests/propagators/test_base_propagator.py b/tests/propagators/test_base_propagator.py index d9a9ddde..df5d11a4 100644 --- a/tests/propagators/test_base_propagator.py +++ b/tests/propagators/test_base_propagator.py @@ -3,8 +3,7 @@ from instana.propagators.base_propagator import BasePropagator from instana.w3c_trace_context.traceparent import Traceparent -from instana.w3c_trace_context.tracestate import Tracestate -from mock import patch, MagicMock +from mock import patch import unittest @@ -126,4 +125,36 @@ def test_extract_carrier_dict_corrupted_level_header(self, mock_validate, mock_g self.assertEqual(ctx.trace_id, "a3ce929d0e0e4736") # 16 last chars from traceparent trace_id self.assertTrue(ctx.trace_parent) self.assertEqual(ctx.traceparent, '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01') + self.assertEqual(ctx.tracestate, 'congo=t61rcWkgMzE') + + @patch.object(Traceparent, "get_traceparent_fields") + @patch.object(Traceparent, "validate") + def test_extract_carrier_dict_level_header_not_splitable(self, mock_validate, mock_get_traceparent_fields): + """ + In this test case the traceparent header fails the validation, so traceparent and tracestate are not gonna used + Additionally because in the instana L header the correlation flags are present we need to start a new ctx and + the present values of 'X-INSTANA-T', 'X-INSTANA-S' headers should no be used. This means the ctx should be None + :param mock_validate: + :return: + """ + carrier = { + 'traceparent': '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01', + 'tracestate': 'congo=t61rcWkgMzE', + 'X-INSTANA-T': '1234d0e0e4736234', + 'X-INSTANA-S': '1234567890abcdef', + 'X-INSTANA-L': ['1'] + } + mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' + mock_get_traceparent_fields.return_value = ["00", "4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7", "01"] + ctx = self.bp.extract(carrier) + self.assertIsNone(ctx.correlation_id) + self.assertIsNone(ctx.correlation_type) + self.assertIsNone(ctx.instana_ancestor) + self.assertEqual(ctx.level, 1) + self.assertIsNone(ctx.long_trace_id) + self.assertEqual(ctx.span_id, "1234567890abcdef") + self.assertFalse(ctx.synthetic) + self.assertEqual(ctx.trace_id, "1234d0e0e4736234") + self.assertIsNone(ctx.trace_parent) + self.assertEqual(ctx.traceparent, '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01') self.assertEqual(ctx.tracestate, 'congo=t61rcWkgMzE') \ No newline at end of file diff --git a/tests/propagators/test_binary_propagator.py b/tests/propagators/test_binary_propagator.py new file mode 100644 index 00000000..0e973d89 --- /dev/null +++ b/tests/propagators/test_binary_propagator.py @@ -0,0 +1,76 @@ +# (c) Copyright IBM Corp. 2021 +# (c) Copyright Instana Inc. 2021 + +from instana.propagators.binary_propagator import BinaryPropagator +from instana.span_context import SpanContext +from instana.w3c_trace_context.traceparent import Traceparent +from instana.w3c_trace_context.tracestate import Tracestate +from mock import patch, MagicMock +import unittest + + +class TestBinaryPropagator(unittest.TestCase): + def setUp(self): + self.bp = BinaryPropagator() + + def test_inject_carrier_dict(self): + carrier = {} + ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", + level=1, baggage={}, sampled=True, + synthetic=False) + carrier = self.bp.inject(ctx, carrier) + self.assertEqual(carrier[b'x-instana-t'], b"1234d0e0e4736234") + + def test_inject_carrier_dict_w3c_True(self): + carrier = {} + ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", + level=1, baggage={}, sampled=True, + synthetic=False) + carrier = self.bp.inject(ctx, carrier, binary_w3c_injection=True) + self.assertEqual(carrier[b'x-instana-t'], b"1234d0e0e4736234") + self.assertEqual(carrier[b'traceparent'], b'00-00000000000000001234d0e0e4736234-1234567890abcdef-01') + self.assertEqual(carrier[b'tracestate'], b'in=1234d0e0e4736234;1234567890abcdef') + + def test_inject_carrier_list(self): + carrier = [] + ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", + level=1, baggage={}, sampled=True, + synthetic=False) + carrier = self.bp.inject(ctx, carrier) + self.assertEqual(carrier[0], (b'x-instana-t', b'1234d0e0e4736234')) + + def test_inject_carrier_list_w3c_True(self): + carrier = [] + ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", + level=1, baggage={}, sampled=True, + synthetic=False) + carrier = self.bp.inject(ctx, carrier, binary_w3c_injection=True) + self.assertEqual(carrier[2], (b'x-instana-t', b'1234d0e0e4736234')) + self.assertEqual(carrier[0], (b'traceparent', b'00-00000000000000001234d0e0e4736234-1234567890abcdef-01')) + self.assertEqual(carrier[1], (b'tracestate', b'in=1234d0e0e4736234;1234567890abcdef')) + + def test_inject_carrier_tupple(self): + carrier = () + ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", + level=1, baggage={}, sampled=True, + synthetic=False) + carrier = self.bp.inject(ctx, carrier) + self.assertEqual(carrier[0], (b'x-instana-t', b'1234d0e0e4736234')) + + def test_inject_carrier_tupple_w3c_True(self): + carrier = () + ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", + level=1, baggage={}, sampled=True, + synthetic=False) + carrier = self.bp.inject(ctx, carrier, binary_w3c_injection=True) + self.assertEqual(carrier[2], (b'x-instana-t', b'1234d0e0e4736234')) + self.assertEqual(carrier[0], (b'traceparent', b'00-00000000000000001234d0e0e4736234-1234567890abcdef-01')) + self.assertEqual(carrier[1], (b'tracestate', b'in=1234d0e0e4736234;1234567890abcdef')) + + def test_inject_carrier_set_exception(self): + carrier = set() + ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", + level=1, baggage={}, sampled=True, + synthetic=False) + carrier = self.bp.inject(ctx, carrier) + self.assertIsNone(carrier) \ No newline at end of file From d419507281e030f54253a6d490a99428eee80d6b Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Tue, 10 Aug 2021 06:40:06 +0200 Subject: [PATCH 20/39] clean unused imports --- tests/propagators/test_binary_propagator.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/propagators/test_binary_propagator.py b/tests/propagators/test_binary_propagator.py index 0e973d89..3fd15cb1 100644 --- a/tests/propagators/test_binary_propagator.py +++ b/tests/propagators/test_binary_propagator.py @@ -3,9 +3,6 @@ from instana.propagators.binary_propagator import BinaryPropagator from instana.span_context import SpanContext -from instana.w3c_trace_context.traceparent import Traceparent -from instana.w3c_trace_context.tracestate import Tracestate -from mock import patch, MagicMock import unittest From a7f241391b1ef259e1b0c4f59c36277e419ac28c Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Tue, 10 Aug 2021 09:15:18 +0200 Subject: [PATCH 21/39] document the binary_w3c_injection parameter --- instana/tracer.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/instana/tracer.py b/instana/tracer.py index 9667eb8a..9c1ea708 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -117,6 +117,17 @@ def start_span(self, return span def inject(self, span_context, format, carrier, binary_w3c_injection=False): + """ + In this method the binary_w3c_injection parameter is to be used only for the binary injection call, + by default in the binary injection the w3c trace context parameters are not getting injected. if there is + a wish to inject them for a specific instrumentation the inject method here should be called with the + parameter set to True + :param span_context: + :param format: + :param carrier: + :param binary_w3c_injection: default False + :return: + """ if format in self._propagators and format != ot.Format.BINARY: return self._propagators[format].inject(span_context, carrier) elif format == ot.Format.BINARY: From ce333e17752969ed176b17a3953e1ddf72a16efe Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Tue, 10 Aug 2021 13:45:43 +0200 Subject: [PATCH 22/39] add fixes for adjusting behavior to the testing tracer suite --- instana/instrumentation/flask/vanilla.py | 3 +-- instana/instrumentation/flask/with_blinker.py | 3 +-- instana/propagators/base_propagator.py | 5 +++-- instana/propagators/http_propagator.py | 6 +++++- instana/util/ids.py | 6 +++--- instana/w3c_trace_context/tracestate.py | 6 ++++++ tests/apps/flask_app/app.py | 2 +- 7 files changed, 20 insertions(+), 11 deletions(-) diff --git a/instana/instrumentation/flask/vanilla.py b/instana/instrumentation/flask/vanilla.py index a9906247..734d3fa5 100644 --- a/instana/instrumentation/flask/vanilla.py +++ b/instana/instrumentation/flask/vanilla.py @@ -22,8 +22,7 @@ def before_request_with_instana(*argv, **kwargs): env = flask.request.environ ctx = None - if 'HTTP_X_INSTANA_T' in env and 'HTTP_X_INSTANA_S' in env: - ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env) + ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env) flask.g.scope = tracer.start_active_span('wsgi', child_of=ctx) span = flask.g.scope.span diff --git a/instana/instrumentation/flask/with_blinker.py b/instana/instrumentation/flask/with_blinker.py index 1cca6648..254a20b7 100644 --- a/instana/instrumentation/flask/with_blinker.py +++ b/instana/instrumentation/flask/with_blinker.py @@ -23,8 +23,7 @@ def request_started_with_instana(sender, **extra): env = flask.request.environ ctx = None - if 'HTTP_X_INSTANA_T' in env and 'HTTP_X_INSTANA_S' in env: - ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env) + ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env) flask.g.scope = tracer.start_active_span('wsgi', child_of=ctx) span = flask.g.scope.span diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 89606423..d606193d 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -123,9 +123,10 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar if trace_id and span_id: - ctx = SpanContext(span_id=span_id, trace_id=trace_id, level=ctx_level, + ctx = SpanContext(span_id=span_id, trace_id=trace_id[-16:], level=ctx_level, baggage={}, sampled=True, synthetic=synthetic is not None) - + if len(trace_id) > 16: + ctx.long_trace_id = trace_id elif traceparent and trace_id is None and span_id is None: _, tp_trace_id, tp_parent_id, _ = self.__tp.get_traceparent_fields(traceparent) diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index ee8fd1c8..6d346866 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -27,9 +27,13 @@ def inject(self, span_context, carrier): trace_id = span_context.trace_id span_id = span_context.span_id level = span_context.level + if span_context.long_trace_id and not span_context.trace_parent: + tp_trace_id = span_context.long_trace_id + else: + tp_trace_id = trace_id traceparent = span_context.traceparent tracestate = span_context.tracestate - traceparent = self.__tp.update_traceparent(traceparent, trace_id, span_id, level) + traceparent = self.__tp.update_traceparent(traceparent, tp_trace_id, span_id, level) tracestate = self.__ts.update_tracestate(tracestate, trace_id, span_id) if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): diff --git a/instana/util/ids.py b/instana/util/ids.py index 01749934..9c0462d0 100644 --- a/instana/util/ids.py +++ b/instana/util/ids.py @@ -58,9 +58,9 @@ def header_to_id(header): if length < 16: # Left pad ID with zeros header = header.zfill(16) - elif length > 16: - # Phase 0: Discard everything but the last 16byte - header = header[-16:] + # elif length > 16: + # # Phase 0: Discard everything but the last 16byte + # header = header[-16:] return header except ValueError: diff --git a/instana/w3c_trace_context/tracestate.py b/instana/w3c_trace_context/tracestate.py index 10f88171..b1d066ea 100644 --- a/instana/w3c_trace_context/tracestate.py +++ b/instana/w3c_trace_context/tracestate.py @@ -47,6 +47,12 @@ def update_tracestate(self, tracestate, in_trace_id, in_span_id): if tracestate is None or tracestate == "": tracestate = instana_tracestate else: + # remove the existing in= entry + if "in=" in tracestate: + splitted = tracestate.split("in=") + before_in = splitted[0] + after_in = splitted[1].split(",")[1:] + tracestate = '{}{}'.format(before_in, ",".join(after_in)) # tracestate can contain a max of 32 list members, if it contains up to 31 # we can safely add the instana one without the need to truncate anything if len(tracestate.split(",")) <= self.MAX_NUMBER_OF_LIST_MEMBERS - 1: diff --git a/tests/apps/flask_app/app.py b/tests/apps/flask_app/app.py index 2a043323..e6f18aeb 100755 --- a/tests/apps/flask_app/app.py +++ b/tests/apps/flask_app/app.py @@ -19,7 +19,7 @@ # in test sets that don't install/test for it. pass -from ...helpers import testenv +from tests.helpers import testenv from instana.singletons import tracer logging.basicConfig(level=logging.WARNING) From c526ed803d486dc1364511d1bc6777af4edb4f7f Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Tue, 10 Aug 2021 14:38:28 +0200 Subject: [PATCH 23/39] fixing failing tests --- instana/propagators/base_propagator.py | 2 +- tests/frameworks/test_django.py | 8 ++++---- tests/test_id_management.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index d606193d..7fc30132 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -123,7 +123,7 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar if trace_id and span_id: - ctx = SpanContext(span_id=span_id, trace_id=trace_id[-16:], level=ctx_level, + ctx = SpanContext(span_id=span_id[-16:], trace_id=trace_id[-16:], level=ctx_level, baggage={}, sampled=True, synthetic=synthetic is not None) if len(trace_id) > 16: ctx.long_trace_id = trace_id diff --git a/tests/frameworks/test_django.py b/tests/frameworks/test_django.py index 1dc422ec..82fdeddb 100644 --- a/tests/frameworks/test_django.py +++ b/tests/frameworks/test_django.py @@ -343,7 +343,7 @@ def test_with_incoming_context(self): assert ('tracestate' in response.headers) self.assertEqual( - 'in={};{},rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE'.format( + 'in={};{},rojo=00f067aa0ba902b7,congo=t61rcWkgMzE'.format( django_span.t, django_span.s), response.headers['tracestate']) server_timing_value = "intid;desc=%s" % django_span.t assert ('Server-Timing' in response.headers) @@ -393,7 +393,7 @@ def test_with_incoming_context_and_correlation(self): assert ('tracestate' in response.headers) self.assertEqual( - 'in={};{},rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE'.format( + 'in={};{},rojo=00f067aa0ba902b7,congo=t61rcWkgMzE'.format( django_span.t, django_span.s), response.headers['tracestate']) server_timing_value = "intid;desc=%s" % django_span.t assert ('Server-Timing' in response.headers) @@ -438,7 +438,7 @@ def test_with_incoming_traceparent_tracestate(self): assert ('tracestate' in response.headers) self.assertEqual( - 'in=a3ce929d0e0e4736;{},rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE'.format( + 'in=a3ce929d0e0e4736;{},rojo=00f067aa0ba902b7,congo=t61rcWkgMzE'.format( django_span.s), response.headers['tracestate']) server_timing_value = "intid;desc=%s" % django_span.t @@ -483,7 +483,7 @@ def test_with_incoming_traceparent_tracestate_disable_traceparent(self): assert ('tracestate' in response.headers) self.assertEqual( - 'in=a3ce929d0e0e4736;{},rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE'.format( + 'in=a3ce929d0e0e4736;{},rojo=00f067aa0ba902b7,congo=t61rcWkgMzE'.format( django_span.s), response.headers['tracestate']) server_timing_value = "intid;desc=%s" % django_span.t diff --git a/tests/test_id_management.py b/tests/test_id_management.py index 69529e1e..a2563fcc 100644 --- a/tests/test_id_management.py +++ b/tests/test_id_management.py @@ -37,7 +37,7 @@ def test_various_header_to_id_conversion(): # Very long incoming header should just return the rightmost 16 bytes result = instana.util.ids.header_to_id('0x0123456789abcdef0123456789abcdef') - assert('0123456789abcdef' == result) + assert('0x0123456789abcdef0123456789abcdef' == result) def test_header_to_id_conversion_with_bogus_header(): From e82afb18ae7fc0b7fef80067404d374da70d1062 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 05:50:29 +0200 Subject: [PATCH 24/39] moving the logic of the extra span attributes relative to w3c to only be applied for entry spans --- instana/span.py | 24 +++++++++++++----------- instana/version.py | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/instana/span.py b/instana/span.py index 8a08035a..942c39b0 100644 --- a/instana/span.py +++ b/instana/span.py @@ -112,17 +112,6 @@ def __init__(self, span, source, service_name, **kwargs): self.data = DictionaryOfStan() self.stack = span.stack - if span.context.trace_parent: - self.tp = span.context.trace_parent - if span.context.instana_ancestor: - self.ia = span.context.instana_ancestor - if span.context.long_trace_id: - self.lt = span.context.long_trace_id - if span.context.correlation_type: - self.crtp = span.context.correlation_type - if span.context.correlation_id: - self.crid = span.context.correlation_id - if span.synthetic is True: self.sy = span.synthetic @@ -264,6 +253,7 @@ def __init__(self, span, source, service_name, **kwargs): # entry self._populate_entry_span_data(span) self.data["service"] = service_name + self._populate_extra_span_attributes(span) elif span.operation_name in self.EXIT_SPANS: self.k = 2 # exit self._populate_exit_span_data(span) @@ -282,6 +272,18 @@ def __init__(self, span, source, service_name, **kwargs): if len(span.tags) > 0: self.data["custom"]["tags"] = self._validate_tags(span.tags) + def _populate_extra_span_attributes(self, span): + if span.context.trace_parent: + self.tp = span.context.trace_parent + if span.context.instana_ancestor: + self.ia = span.context.instana_ancestor + if span.context.long_trace_id: + self.lt = span.context.long_trace_id + if span.context.correlation_type: + self.crtp = span.context.correlation_type + if span.context.correlation_id: + self.crid = span.context.correlation_id + def _populate_entry_span_data(self, span): if span.operation_name in self.HTTP_SPANS: self._collect_http_tags(span) diff --git a/instana/version.py b/instana/version.py index 7cc1a6b0..19afcaaf 100644 --- a/instana/version.py +++ b/instana/version.py @@ -3,4 +3,4 @@ # Module version file. Used by setup.py and snapshot reporting. -VERSION = '1.34.1' +VERSION = '1.35.0' From 8dc635d4337122f09d9343de24231a372a54ffd6 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 06:20:27 +0200 Subject: [PATCH 25/39] apply correlation properties fix --- instana/propagators/base_propagator.py | 3 +++ instana/tracer.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 7fc30132..28fbe48c 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -154,6 +154,9 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar elif synthetic: ctx = SpanContext(synthetic=synthetic) + else: + ctx = SpanContext() + self.__set_correlation_properties(level, ctx) if ctx and traceparent: ctx.traceparent = traceparent diff --git a/instana/tracer.py b/instana/tracer.py index 9c1ea708..a88e2a34 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -99,6 +99,9 @@ def start_span(self, else: ctx.trace_id = gid ctx.sampled = self.sampler.sampled(ctx.trace_id) + if parent_ctx is not None and parent_ctx.correlation_id is not None: + ctx.correlation_type = parent_ctx.correlation_type + ctx.correlation_id = parent_ctx.correlation_id # Tie it all together span = InstanaSpan(self, From 94dbcc6cd68b985199a292b964cc2d9dce022f09 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 06:25:49 +0200 Subject: [PATCH 26/39] correct the env variable name --- instana/propagators/base_propagator.py | 2 +- tests/frameworks/test_django.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 28fbe48c..8b27696c 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -110,7 +110,7 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar :param synthetic: instana synthetic :return: ctx or None """ - disable_traceparent = os.environ.get("INSTANA_W3C_DISABLE_TRACE_CORRELATION", "") + disable_traceparent = os.environ.get("INSTANA_DISABLE_W3C_TRACE_CORRELATION", "") instana_ancestor = None ctx = None if level and "correlationType" in level: diff --git a/tests/frameworks/test_django.py b/tests/frameworks/test_django.py index 82fdeddb..7d020ef5 100644 --- a/tests/frameworks/test_django.py +++ b/tests/frameworks/test_django.py @@ -24,7 +24,7 @@ def setUp(self): def tearDown(self): """ Do nothing for now """ - os.environ["INSTANA_W3C_DISABLE_TRACE_CORRELATION"] = "" + os.environ["INSTANA_DISABLE_W3C_TRACE_CORRELATION"] = "" def test_basic_request(self): with tracer.start_active_span('test'): @@ -446,7 +446,7 @@ def test_with_incoming_traceparent_tracestate(self): self.assertEqual(server_timing_value, response.headers['Server-Timing']) def test_with_incoming_traceparent_tracestate_disable_traceparent(self): - os.environ["INSTANA_W3C_DISABLE_TRACE_CORRELATION"] = "1" + os.environ["INSTANA_DISABLE_W3C_TRACE_CORRELATION"] = "1" request_headers = dict() request_headers['traceparent'] = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' request_headers['tracestate'] = 'rojo=00f067aa0ba902b7,in=a3ce929d0e0e4736;8357ccd9da194656,congo=t61rcWkgMzE' From 8df08e786105281907b2c9b3f429918ce2bb5aeb Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 06:37:45 +0200 Subject: [PATCH 27/39] fixing tests --- tests/opentracing/test_ot_propagators.py | 22 +++++++++++++++------- tests/propagators/test_base_propagator.py | 8 +++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/opentracing/test_ot_propagators.py b/tests/opentracing/test_ot_propagators.py index f6949a7c..dc92839a 100644 --- a/tests/opentracing/test_ot_propagators.py +++ b/tests/opentracing/test_ot_propagators.py @@ -113,14 +113,16 @@ def test_http_extract_synthetic_only(): assert ctx.synthetic -def test_http_no_context_extract(): +def test_http_default_context_extract(): ot.tracer = InstanaTracer() carrier = {} ctx = ot.tracer.extract(ot.Format.HTTP_HEADERS, carrier) - assert ctx is None - + assert isinstance(ctx, SpanContext) + assert ctx.trace_id is None + assert ctx.span_id is None + assert ctx.synthetic is False def test_http_128bit_headers(): ot.tracer = InstanaTracer() @@ -195,13 +197,16 @@ def test_text_mixed_case_extract(): assert ctx.span_id == '0000000000000001' -def test_text_no_context_extract(): +def test_text_default_context_extract(): ot.tracer = InstanaTracer() carrier = {} ctx = ot.tracer.extract(ot.Format.TEXT_MAP, carrier) - assert ctx is None + assert isinstance(ctx, SpanContext) + assert ctx.trace_id is None + assert ctx.span_id is None + assert ctx.synthetic is False def test_text_128bit_headers(): @@ -279,13 +284,16 @@ def test_binary_mixed_case_extract(): assert ctx.synthetic -def test_binary_no_context_extract(): +def test_binary_default_context_extract(): ot.tracer = InstanaTracer() carrier = {} ctx = ot.tracer.extract(ot.Format.BINARY, carrier) - assert ctx is None + assert isinstance(ctx, SpanContext) + assert ctx.trace_id is None + assert ctx.span_id is None + assert ctx.synthetic is False def test_binary_128bit_headers(): diff --git a/tests/propagators/test_base_propagator.py b/tests/propagators/test_base_propagator.py index df5d11a4..d527465c 100644 --- a/tests/propagators/test_base_propagator.py +++ b/tests/propagators/test_base_propagator.py @@ -3,6 +3,7 @@ from instana.propagators.base_propagator import BasePropagator from instana.w3c_trace_context.traceparent import Traceparent +from instana.span_context import SpanContext from mock import patch import unittest @@ -80,7 +81,12 @@ def test_extract_carrier_dict_validate_Exception_None_returned(self, mock_valida } mock_validate.return_value = None ctx = self.bp.extract(carrier) - self.assertIsNone(ctx) + self.assertTrue(isinstance(ctx, SpanContext)) + assert ctx.trace_id is None + assert ctx.span_id is None + assert ctx.synthetic is False + self.assertEqual(ctx.correlation_id, "1234567890abcdef") + self.assertEqual(ctx.correlation_type, "web") @patch.object(Traceparent, "validate") def test_extract_fake_exception(self, mock_validate): From ee4ed47137f175e98ebfa428b8ba8e7eabad3ca9 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 06:54:09 +0200 Subject: [PATCH 28/39] apply fixes on w3c logic --- instana/propagators/base_propagator.py | 2 -- instana/tracer.py | 4 +++- tests/frameworks/test_django.py | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 8b27696c..4037b509 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -148,8 +148,6 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar level=ctx_level, baggage={}, sampled=True, synthetic=synthetic is not None) - ctx.instana_ancestor = instana_ancestor - self.__set_correlation_properties(level, ctx) elif synthetic: diff --git a/instana/tracer.py b/instana/tracer.py index a88e2a34..728f022c 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -99,9 +99,11 @@ def start_span(self, else: ctx.trace_id = gid ctx.sampled = self.sampler.sampled(ctx.trace_id) - if parent_ctx is not None and parent_ctx.correlation_id is not None: + if parent_ctx is not None: ctx.correlation_type = parent_ctx.correlation_type ctx.correlation_id = parent_ctx.correlation_id + ctx.traceparent = parent_ctx.traceparent + ctx.tracestate = parent_ctx.tracestate # Tie it all together span = InstanaSpan(self, diff --git a/tests/frameworks/test_django.py b/tests/frameworks/test_django.py index 7d020ef5..b4011304 100644 --- a/tests/frameworks/test_django.py +++ b/tests/frameworks/test_django.py @@ -463,8 +463,6 @@ def test_with_incoming_traceparent_tracestate_disable_traceparent(self): self.assertEqual(django_span.t, 'a3ce929d0e0e4736') # last 16 chars from traceparent trace_id self.assertEqual(django_span.p, '8357ccd9da194656') - self.assertEqual(django_span.ia.t, 'a3ce929d0e0e4736') - self.assertEqual(django_span.ia.p, '8357ccd9da194656') assert ('X-INSTANA-T' in response.headers) assert (int(response.headers['X-INSTANA-T'], 16)) From 2fb89d7b540b286b260b590cddd3796217b2fab3 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 07:05:41 +0200 Subject: [PATCH 29/39] apply fixes on w3c logic --- instana/propagators/base_propagator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 4037b509..311b2934 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -152,7 +152,8 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar elif synthetic: ctx = SpanContext(synthetic=synthetic) - else: + + if ctx is None: ctx = SpanContext() self.__set_correlation_properties(level, ctx) From a2503ee526629d05dfe06aef761a12955e891a59 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 10:18:23 +0200 Subject: [PATCH 30/39] small refactoring --- instana/propagators/base_propagator.py | 67 ++++++++++++++++---------- tests/apps/sanic_app/server.py | 4 +- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 311b2934..13cc7624 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -108,25 +108,27 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar :param span_id: instana span id :param level: instana level :param synthetic: instana synthetic - :return: ctx or None + :return: ctx """ + correlation = False disable_traceparent = os.environ.get("INSTANA_DISABLE_W3C_TRACE_CORRELATION", "") instana_ancestor = None - ctx = None + ctx = SpanContext() if level and "correlationType" in level: trace_id, span_id = [None] * 2 + correlation = True - try: - ctx_level = int(level.split(",")[0]) if level else 1 - except Exception: - ctx_level = 1 + ctx_level = self.__get_ctx_level(level) if trace_id and span_id: + ctx.trace_id = trace_id[-16:] # only the last 16 chars + ctx.span_id = span_id[-16:] # only the last 16 chars + ctx.level = ctx_level + ctx.synthetic = synthetic is not None - ctx = SpanContext(span_id=span_id[-16:], trace_id=trace_id[-16:], level=ctx_level, - baggage={}, sampled=True, synthetic=synthetic is not None) if len(trace_id) > 16: ctx.long_trace_id = trace_id + elif traceparent and trace_id is None and span_id is None: _, tp_trace_id, tp_parent_id, _ = self.__tp.get_traceparent_fields(traceparent) @@ -134,41 +136,56 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar instana_ancestor = self.__ts.get_instana_ancestor(tracestate) if disable_traceparent == "": - ctx = SpanContext(span_id=tp_parent_id, trace_id=tp_trace_id[-16:], - level=ctx_level, baggage={}, sampled=True, - synthetic=synthetic is not None) - + ctx.trace_id = tp_trace_id[-16:] + ctx.span_id = tp_parent_id + ctx.level = ctx_level + ctx.synthetic = synthetic is not None ctx.trace_parent = True ctx.instana_ancestor = instana_ancestor - ctx.long_trace_id = tp_trace_id else: if instana_ancestor: - ctx = SpanContext(span_id=instana_ancestor.p, trace_id=instana_ancestor.t, - level=ctx_level, baggage={}, sampled=True, - synthetic=synthetic is not None) - - self.__set_correlation_properties(level, ctx) + ctx.trace_id = instana_ancestor.t + ctx.span_id = instana_ancestor.p + ctx.level = ctx_level + ctx.synthetic = synthetic is not None elif synthetic: - ctx = SpanContext(synthetic=synthetic) + ctx.synthetic = synthetic - if ctx is None: - ctx = SpanContext() + if correlation: self.__set_correlation_properties(level, ctx) - if ctx and traceparent: + if traceparent: ctx.traceparent = traceparent ctx.tracestate = tracestate return ctx + @staticmethod + def __get_ctx_level(level): + """ + Extract the level value and return it, as it may include correlation values + :param level: + :return: + """ + try: + ctx_level = int(level.split(",")[0]) if level else 1 + except Exception: + ctx_level = 1 + return ctx_level + @staticmethod def __set_correlation_properties(level, ctx): + """ + Set the correlation values if they are present + :param level: + :param ctx: + :return: + """ try: - if level and "correlationType" in level: - ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] - if level and "correlationId" in level: + ctx.correlation_type = level.split(",")[1].split("correlationType=")[1].split(";")[0] + if "correlationId" in level: ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] except Exception: logger.debug("extract instana correlation type/id error:", exc_info=True) diff --git a/tests/apps/sanic_app/server.py b/tests/apps/sanic_app/server.py index 1780e2bb..2cf712fc 100644 --- a/tests/apps/sanic_app/server.py +++ b/tests/apps/sanic_app/server.py @@ -3,8 +3,8 @@ from sanic import Sanic from sanic.exceptions import SanicException -from .simpleview import SimpleView -from .name import NameView +from tests.apps.sanic_app.simpleview import SimpleView +from tests.apps.sanic_app.name import NameView from sanic.response import text import instana From 7b4632c0fbbe7d679682a7aef717b7ca7a44e7fe Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 10:35:09 +0200 Subject: [PATCH 31/39] adding the extra span attributes in SDK entry spans too --- instana/span.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/instana/span.py b/instana/span.py index 942c39b0..a18bd40f 100644 --- a/instana/span.py +++ b/instana/span.py @@ -117,6 +117,18 @@ def __init__(self, span, source, service_name, **kwargs): self.__dict__.update(kwargs) + def _populate_extra_span_attributes(self, span): + if span.context.trace_parent: + self.tp = span.context.trace_parent + if span.context.instana_ancestor: + self.ia = span.context.instana_ancestor + if span.context.long_trace_id: + self.lt = span.context.long_trace_id + if span.context.correlation_type: + self.crtp = span.context.correlation_type + if span.context.correlation_id: + self.crid = span.context.correlation_id + def _validate_tags(self, tags): """ This method will loop through a set of tags to validate each key and value. @@ -192,6 +204,9 @@ def __init__(self, span, source, service_name, **kwargs): if self.k == 1 and service_name is not None: self.data["service"] = service_name + if self.k == 1: + self._populate_extra_span_attributes(span) + self.data["sdk"]["name"] = span.operation_name self.data["sdk"]["type"] = span_kind[0] self.data["sdk"]["custom"]["tags"] = self._validate_tags(span.tags) @@ -231,7 +246,7 @@ def get_span_kind(self, span): class RegisteredSpan(BaseSpan): - HTTP_SPANS = ("aiohttp-client", "aiohttp-server", "asgi", "django", "http", "soap", "tornado-client", + HTTP_SPANS = ("aiohttp-client", "aiohttp-server", "django", "http", "soap", "tornado-client", "tornado-server", "urllib3", "wsgi") EXIT_SPANS = ("aiohttp-client", "boto3", "cassandra", "celery-client", "couchbase", "log", "memcache", @@ -272,18 +287,6 @@ def __init__(self, span, source, service_name, **kwargs): if len(span.tags) > 0: self.data["custom"]["tags"] = self._validate_tags(span.tags) - def _populate_extra_span_attributes(self, span): - if span.context.trace_parent: - self.tp = span.context.trace_parent - if span.context.instana_ancestor: - self.ia = span.context.instana_ancestor - if span.context.long_trace_id: - self.lt = span.context.long_trace_id - if span.context.correlation_type: - self.crtp = span.context.correlation_type - if span.context.correlation_id: - self.crid = span.context.correlation_id - def _populate_entry_span_data(self, span): if span.operation_name in self.HTTP_SPANS: self._collect_http_tags(span) From 1888d4731d12a9a225ea4a952c467b26bf744bda Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 10:45:36 +0200 Subject: [PATCH 32/39] removed commented code block --- instana/util/ids.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/instana/util/ids.py b/instana/util/ids.py index 9c0462d0..59828c06 100644 --- a/instana/util/ids.py +++ b/instana/util/ids.py @@ -58,9 +58,6 @@ def header_to_id(header): if length < 16: # Left pad ID with zeros header = header.zfill(16) - # elif length > 16: - # # Phase 0: Discard everything but the last 16byte - # header = header[-16:] return header except ValueError: From 05954f3f5d8f9cfc08536cf669dc171c83df4b17 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 14:33:17 +0200 Subject: [PATCH 33/39] adding asgi in the registered spans --- instana/recorder.py | 2 +- instana/span.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/instana/recorder.py b/instana/recorder.py index 037e2614..6ff0699e 100644 --- a/instana/recorder.py +++ b/instana/recorder.py @@ -26,7 +26,7 @@ class StanRecorder(object): "gcps-consumer", "log", "memcache", "mongo", "mysql", "postgres", "pymongo", "rabbitmq", "redis","render", "rpc-client", "rpc-server", "sqlalchemy", "soap", - "tornado-client", "tornado-server", "urllib3", "wsgi") + "tornado-client", "tornado-server", "urllib3", "wsgi", "asgi") # Recorder thread for collection/reporting of spans thread = None diff --git a/instana/span.py b/instana/span.py index a18bd40f..22a0c1ef 100644 --- a/instana/span.py +++ b/instana/span.py @@ -204,8 +204,8 @@ def __init__(self, span, source, service_name, **kwargs): if self.k == 1 and service_name is not None: self.data["service"] = service_name - if self.k == 1: - self._populate_extra_span_attributes(span) + # if self.k == 1: + # self._populate_extra_span_attributes(span) self.data["sdk"]["name"] = span.operation_name self.data["sdk"]["type"] = span_kind[0] @@ -247,14 +247,14 @@ def get_span_kind(self, span): class RegisteredSpan(BaseSpan): HTTP_SPANS = ("aiohttp-client", "aiohttp-server", "django", "http", "soap", "tornado-client", - "tornado-server", "urllib3", "wsgi") + "tornado-server", "urllib3", "wsgi", "asgi") EXIT_SPANS = ("aiohttp-client", "boto3", "cassandra", "celery-client", "couchbase", "log", "memcache", "mongo", "mysql", "postgres", "rabbitmq", "redis", "rpc-client", "sqlalchemy", "soap", "tornado-client", "urllib3", "pymongo", "gcs", "gcps-producer") ENTRY_SPANS = ("aiohttp-server", "aws.lambda.entry", "celery-worker", "django", "wsgi", "rabbitmq", - "rpc-server", "tornado-server", "gcps-consumer") + "rpc-server", "tornado-server", "gcps-consumer", "asgi") LOCAL_SPANS = ("render") From 37fe336002a4ffffee9399a5ffe2b30399fbe079 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 11 Aug 2021 15:27:40 +0200 Subject: [PATCH 34/39] fixing asgi switching span type related tests --- tests/frameworks/test_fastapi.py | 127 ++++++++++++++--------------- tests/frameworks/test_sanic.py | 126 ++++++++++++++-------------- tests/frameworks/test_starlette.py | 92 ++++++++++----------- 3 files changed, 171 insertions(+), 174 deletions(-) diff --git a/tests/frameworks/test_fastapi.py b/tests/frameworks/test_fastapi.py index 624e251a..9765db7f 100644 --- a/tests/frameworks/test_fastapi.py +++ b/tests/frameworks/test_fastapi.py @@ -35,7 +35,7 @@ def test_vanilla_get(server): spans = tracer.recorder.queued_spans() # FastAPI instrumentation (like all instrumentation) _always_ traces unless told otherwise assert len(spans) == 1 - assert spans[0].n == 'sdk' + assert spans[0].n == 'asgi' def test_basic_get(server): @@ -56,7 +56,7 @@ def test_basic_get(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert (urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == "asgi" asgi_span = get_first_span_by_filter(spans, span_filter) assert (asgi_span) @@ -74,13 +74,13 @@ def test_basic_get(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert (asgi_span.ec == None) - assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) def test_400(server): @@ -101,7 +101,7 @@ def test_400(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert (urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert (asgi_span) @@ -119,14 +119,13 @@ def test_400(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert (asgi_span.ec == None) - assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/400') - assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/400') - assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 400) - assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) - + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/400') + assert (asgi_span.data['http']['path_tpl'] == '/400') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 400) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) def test_500(server): result = None @@ -146,7 +145,7 @@ def test_500(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert (urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert (asgi_span) @@ -164,14 +163,13 @@ def test_500(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert (asgi_span.ec == 1) - assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/500') - assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/500') - assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 500) - assert (asgi_span.data['sdk']['custom']['tags']['http.error'] == '500 response') - assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) - + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/500') + assert (asgi_span.data['http']['path_tpl'] == '/500') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 500) + assert (asgi_span.data['http']['error'] == '500 response') + assert (asgi_span.data['http']['params'] is None) def test_path_templates(server): result = None @@ -191,7 +189,7 @@ def test_path_templates(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert (urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert (asgi_span) @@ -209,13 +207,13 @@ def test_path_templates(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert (asgi_span.ec == None) - assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/users/1') - assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/users/{user_id}') - assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/users/1') + assert (asgi_span.data['http']['path_tpl'] == '/users/{user_id}') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) def test_secret_scrubbing(server): @@ -236,7 +234,7 @@ def test_secret_scrubbing(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert (urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert (asgi_span) @@ -254,14 +252,13 @@ def test_secret_scrubbing(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert (asgi_span.ec == None) - assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert (asgi_span.data['sdk']['custom']['tags']['http.params'] == 'secret=') - assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) - + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] == 'secret=') def test_synthetic_request(server): request_headers = { @@ -283,7 +280,7 @@ def test_synthetic_request(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert (urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert (asgi_span) @@ -301,13 +298,13 @@ def test_synthetic_request(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert (asgi_span.ec == None) - assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) assert (asgi_span.sy) assert (urllib3_span.sy is None) @@ -339,7 +336,7 @@ def test_custom_header_capture(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert (urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert (asgi_span) @@ -357,15 +354,15 @@ def test_custom_header_capture(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert (asgi_span.ec == None) - assert (asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert (asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert (asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert (asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert (asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert ('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert ('http.params' not in asgi_span.data['sdk']['custom']['tags']) - - assert ("http.header.X-Capture-This" in asgi_span.data["sdk"]["custom"]['tags']) - assert ("this" == asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-This"]) - assert ("http.header.X-Capture-That" in asgi_span.data["sdk"]["custom"]['tags']) - assert ("that" == asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-That"]) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) + + assert ("X-Capture-This" in asgi_span.data["http"]["header"]) + assert ("this" == asgi_span.data["http"]["header"]["X-Capture-This"]) + assert ("X-Capture-That" in asgi_span.data["http"]["header"]) + assert ("that" == asgi_span.data["http"]["header"]["X-Capture-That"]) diff --git a/tests/frameworks/test_sanic.py b/tests/frameworks/test_sanic.py index d0dd0091..00f5c85a 100644 --- a/tests/frameworks/test_sanic.py +++ b/tests/frameworks/test_sanic.py @@ -39,7 +39,7 @@ def test_vanilla_get(self): self.assertIn("Server-Timing", result.headers) spans = tracer.recorder.queued_spans() self.assertEqual(len(spans), 1) - self.assertEqual(spans[0].n, 'sdk') + self.assertEqual(spans[0].n, 'asgi') def test_basic_get(self): result = None @@ -59,7 +59,7 @@ def test_basic_get(self): urllib3_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(asgi_span) @@ -76,13 +76,13 @@ def test_basic_get(self): self.assertEqual(result.headers["Server-Timing"], ("intid;desc=%s" % asgi_span.t)) self.assertIsNone(asgi_span.ec) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.host'], '127.0.0.1:1337') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path'], '/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'], '/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.method'], 'GET') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.status_code'], 200) - self.assertNotIn('http.error', asgi_span.data['sdk']['custom']['tags']) - self.assertNotIn('http.params', asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1:1337') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) def test_404(self): result = None @@ -102,7 +102,7 @@ def test_404(self): urllib3_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(asgi_span) @@ -119,13 +119,13 @@ def test_404(self): self.assertEqual(result.headers["Server-Timing"], ("intid;desc=%s" % asgi_span.t)) self.assertIsNone(asgi_span.ec) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.host'], '127.0.0.1:1337') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path'], '/foo/not_an_int') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.method'], 'GET') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.status_code'], 404) - self.assertNotIn('http.error', asgi_span.data['sdk']['custom']['tags']) - self.assertNotIn('http.params', asgi_span.data['sdk']['custom']['tags']) - self.assertNotIn('http.path_tpl', asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1:1337') + assert (asgi_span.data['http']['path'] == '/foo/not_an_int') + assert (asgi_span.data['http']['path_tpl'] is None) + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 404) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) def test_500(self): result = None @@ -145,7 +145,7 @@ def test_500(self): urllib3_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(asgi_span) @@ -162,14 +162,13 @@ def test_500(self): self.assertEqual(result.headers["Server-Timing"], ("intid;desc=%s" % asgi_span.t)) self.assertEqual(asgi_span.ec, 1) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.host'], '127.0.0.1:1337') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path'], '/test_request_args') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'], '/test_request_args') - - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.method'], 'GET') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.status_code'], 500) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.error'], 'Something went wrong.') - self.assertNotIn('http.params', asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1:1337') + assert (asgi_span.data['http']['path'] == '/test_request_args') + assert (asgi_span.data['http']['path_tpl'] == '/test_request_args') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 500) + assert (asgi_span.data['http']['error'] == 'Something went wrong.') + assert (asgi_span.data['http']['params'] is None) def test_path_templates(self): result = None @@ -189,7 +188,7 @@ def test_path_templates(self): urllib3_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(asgi_span) @@ -206,13 +205,14 @@ def test_path_templates(self): self.assertEqual(result.headers["Server-Timing"], ("intid;desc=%s" % asgi_span.t)) self.assertIsNone(asgi_span.ec) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.host'], '127.0.0.1:1337') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path'], '/foo/1') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'], '/foo/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.method'], 'GET') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.status_code'], 200) - self.assertNotIn('http.error', asgi_span.data['sdk']['custom']['tags']) - self.assertNotIn('http.params', asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1:1337') + assert (asgi_span.data['http']['path'] == '/foo/1') + assert (asgi_span.data['http']['path_tpl'] == '/foo/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) + def test_secret_scrubbing(self): result = None @@ -232,7 +232,7 @@ def test_secret_scrubbing(self): urllib3_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(asgi_span) @@ -249,13 +249,13 @@ def test_secret_scrubbing(self): self.assertEqual(result.headers["Server-Timing"], ("intid;desc=%s" % asgi_span.t)) self.assertIsNone(asgi_span.ec) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.host'], '127.0.0.1:1337') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path'], '/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'], '/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.method'], 'GET') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.status_code'], 200) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.params'], 'secret=') - self.assertNotIn('http.error', asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1:1337') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] == 'secret=') def test_synthetic_request(self): request_headers = { @@ -277,7 +277,7 @@ def test_synthetic_request(self): urllib3_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(asgi_span) @@ -294,13 +294,13 @@ def test_synthetic_request(self): self.assertEqual(result.headers["Server-Timing"], ("intid;desc=%s" % asgi_span.t)) self.assertIsNone(asgi_span.ec) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.host'], '127.0.0.1:1337') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path'], '/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'], '/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.method'], 'GET') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.status_code'], 200) - self.assertNotIn('http.error', asgi_span.data['sdk']['custom']['tags']) - self.assertNotIn('http.params', asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1:1337') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) self.assertIsNotNone(asgi_span.sy) self.assertIsNone(urllib3_span.sy) @@ -327,7 +327,7 @@ def test_custom_header_capture(self): urllib3_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) self.assertIsNotNone(asgi_span) @@ -344,15 +344,15 @@ def test_custom_header_capture(self): self.assertEqual(result.headers["Server-Timing"], ("intid;desc=%s" % asgi_span.t)) self.assertIsNone(asgi_span.ec) - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.host'], '127.0.0.1:1337') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path'], '/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'], '/') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.method'], 'GET') - self.assertEqual(asgi_span.data['sdk']['custom']['tags']['http.status_code'], 200) - self.assertNotIn('http.error', asgi_span.data['sdk']['custom']['tags']) - self.assertNotIn('http.params', asgi_span.data['sdk']['custom']['tags']) - - self.assertIn("http.header.X-Capture-This", asgi_span.data["sdk"]["custom"]['tags']) - self.assertEqual("this", asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-This"]) - self.assertIn("http.header.X-Capture-That", asgi_span.data["sdk"]["custom"]['tags']) - self.assertEqual("that", asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-That"]) + assert (asgi_span.data['http']['host'] == '127.0.0.1:1337') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) + + assert ("X-Capture-This" in asgi_span.data["http"]["header"]) + assert ("this" == asgi_span.data["http"]["header"]["X-Capture-This"]) + assert ("X-Capture-That" in asgi_span.data["http"]["header"]) + assert ("that" == asgi_span.data["http"]["header"]["X-Capture-That"]) diff --git a/tests/frameworks/test_starlette.py b/tests/frameworks/test_starlette.py index 33cc7b00..72736e84 100644 --- a/tests/frameworks/test_starlette.py +++ b/tests/frameworks/test_starlette.py @@ -26,7 +26,7 @@ def test_vanilla_get(server): spans = tracer.recorder.queued_spans() # Starlette instrumentation (like all instrumentation) _always_ traces unless told otherwise assert len(spans) == 1 - assert spans[0].n == 'sdk' + assert spans[0].n == 'asgi' assert "X-INSTANA-T" in result.headers assert "X-INSTANA-S" in result.headers @@ -52,7 +52,7 @@ def test_basic_get(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert(asgi_span) @@ -70,13 +70,13 @@ def test_basic_get(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) def test_path_templates(server): result = None @@ -96,7 +96,7 @@ def test_path_templates(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert(asgi_span) @@ -114,13 +114,13 @@ def test_path_templates(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/users/1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/users/{user_id}') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/users/1') + assert (asgi_span.data['http']['path_tpl'] == '/users/{user_id}') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) def test_secret_scrubbing(server): result = None @@ -140,7 +140,7 @@ def test_secret_scrubbing(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert(asgi_span) @@ -158,13 +158,13 @@ def test_secret_scrubbing(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.params'] == 'secret=') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] == 'secret=') def test_synthetic_request(server): request_headers = { @@ -186,7 +186,7 @@ def test_synthetic_request(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert(asgi_span) @@ -204,13 +204,13 @@ def test_synthetic_request(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) assert(asgi_span.sy) assert(urllib3_span.sy is None) @@ -241,7 +241,7 @@ def test_custom_header_capture(server): urllib3_span = get_first_span_by_filter(spans, span_filter) assert(urllib3_span) - span_filter = lambda span: span.n == "sdk" and span.data['sdk']['name'] == 'asgi' + span_filter = lambda span: span.n == 'asgi' asgi_span = get_first_span_by_filter(spans, span_filter) assert(asgi_span) @@ -259,15 +259,15 @@ def test_custom_header_capture(server): assert result.headers["Server-Timing"] == ("intid;desc=%s" % asgi_span.t) assert(asgi_span.ec == None) - assert(asgi_span.data['sdk']['custom']['tags']['http.host'] == '127.0.0.1') - assert(asgi_span.data['sdk']['custom']['tags']['http.path'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.path_tpl'] == '/') - assert(asgi_span.data['sdk']['custom']['tags']['http.method'] == 'GET') - assert(asgi_span.data['sdk']['custom']['tags']['http.status_code'] == 200) - assert('http.error' not in asgi_span.data['sdk']['custom']['tags']) - assert('http.params' not in asgi_span.data['sdk']['custom']['tags']) - - assert("http.header.X-Capture-This" in asgi_span.data["sdk"]["custom"]['tags']) - assert("this" == asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-This"]) - assert("http.header.X-Capture-That" in asgi_span.data["sdk"]["custom"]['tags']) - assert("that" == asgi_span.data["sdk"]["custom"]['tags']["http.header.X-Capture-That"]) + assert (asgi_span.data['http']['host'] == '127.0.0.1') + assert (asgi_span.data['http']['path'] == '/') + assert (asgi_span.data['http']['path_tpl'] == '/') + assert (asgi_span.data['http']['method'] == 'GET') + assert (asgi_span.data['http']['status'] == 200) + assert (asgi_span.data['http']['error'] is None) + assert (asgi_span.data['http']['params'] is None) + + assert ("X-Capture-This" in asgi_span.data["http"]["header"]) + assert ("this" == asgi_span.data["http"]["header"]["X-Capture-This"]) + assert ("X-Capture-That" in asgi_span.data["http"]["header"]) + assert ("that" == asgi_span.data["http"]["header"]["X-Capture-That"]) From f5416f57a4dc390ce64760c07a0607b618580156 Mon Sep 17 00:00:00 2001 From: Dimitra Paraskevopoulou Date: Thu, 12 Aug 2021 08:32:05 +0200 Subject: [PATCH 35/39] Update instana/version.py Co-authored-by: Hunter Madison --- instana/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instana/version.py b/instana/version.py index 19afcaaf..dfc3f8e8 100644 --- a/instana/version.py +++ b/instana/version.py @@ -3,4 +3,4 @@ # Module version file. Used by setup.py and snapshot reporting. -VERSION = '1.35.0' +VERSION = '1.35.0.dev0' From 335e63239488a4b0b6d23e3726ae47d4fc4664c7 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Thu, 12 Aug 2021 13:31:03 +0200 Subject: [PATCH 36/39] decoupling the binary and http propagators into separate respective trace context propagators and update all instrumentations to use the right one for headers extract/inject --- instana/instrumentation/aiohttp/client.py | 2 +- instana/instrumentation/aiohttp/server.py | 8 +- instana/instrumentation/asgi.py | 5 +- instana/instrumentation/aws/triggers.py | 5 +- instana/instrumentation/boto3_inst.py | 3 +- instana/instrumentation/django/middleware.py | 4 +- instana/instrumentation/flask/common.py | 3 +- instana/instrumentation/flask/vanilla.py | 8 +- instana/instrumentation/flask/with_blinker.py | 8 +- instana/instrumentation/pyramid/tweens.py | 7 +- instana/instrumentation/sanic_inst.py | 5 +- instana/instrumentation/sudsjurko.py | 7 +- instana/instrumentation/tornado/client.py | 8 +- instana/instrumentation/tornado/server.py | 18 +- instana/instrumentation/urllib3.py | 10 +- instana/instrumentation/webapp2_inst.py | 4 +- instana/instrumentation/wsgi.py | 4 +- instana/propagators/base_propagator.py | 149 +------------ instana/propagators/binary_propagator.py | 39 +--- instana/propagators/binary_propagator_tc.py | 90 ++++++++ instana/propagators/http_propagator.py | 129 ++++++++--- instana/propagators/http_propagator_tc.py | 204 ++++++++++++++++++ instana/propagators/text_propagator.py | 4 +- instana/span.py | 3 - instana/tracer.py | 21 +- tests/clients/test_pika.py | 4 - tests/propagators/test_binary_propagator.py | 4 +- ...opagator.py => test_http_propagator_tc.py} | 32 +-- 28 files changed, 500 insertions(+), 288 deletions(-) create mode 100644 instana/propagators/binary_propagator_tc.py create mode 100644 instana/propagators/http_propagator_tc.py rename tests/propagators/{test_base_propagator.py => test_http_propagator_tc.py} (90%) diff --git a/instana/instrumentation/aiohttp/client.py b/instana/instrumentation/aiohttp/client.py index 418233ce..4811e607 100644 --- a/instana/instrumentation/aiohttp/client.py +++ b/instana/instrumentation/aiohttp/client.py @@ -27,7 +27,7 @@ async def stan_request_start(session, trace_config_ctx, params): scope = async_tracer.start_active_span("aiohttp-client", child_of=parent_span) trace_config_ctx.scope = scope - async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, params.headers) + async_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), params.headers) parts = str(params.url).split('?') if len(parts) > 1: diff --git a/instana/instrumentation/aiohttp/server.py b/instana/instrumentation/aiohttp/server.py index 30a482d7..cf71e87d 100644 --- a/instana/instrumentation/aiohttp/server.py +++ b/instana/instrumentation/aiohttp/server.py @@ -10,17 +10,17 @@ from ...singletons import agent, async_tracer from ...util.secrets import strip_secrets_from_query - try: import aiohttp import asyncio from aiohttp.web import middleware + @middleware async def stan_middleware(request, handler): try: - ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, request.headers) + ctx = async_tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), request.headers) request['scope'] = async_tracer.start_active_span('aiohttp-server', child_of=ctx) scope = request['scope'] @@ -56,7 +56,8 @@ async def stan_middleware(request, handler): scope.span.mark_as_errored() scope.span.set_tag("http.status_code", response.status) - async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers) + async_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + response.headers) response.headers['Server-Timing'] = "intid;desc=%s" % scope.span.context.trace_id return response @@ -80,6 +81,7 @@ def init_with_instana(wrapped, instance, argv, kwargs): return wrapped(*argv, **kwargs) + logger.debug("Instrumenting aiohttp server") except ImportError: pass diff --git a/instana/instrumentation/asgi.py b/instana/instrumentation/asgi.py index f8478a10..c87282a4 100644 --- a/instana/instrumentation/asgi.py +++ b/instana/instrumentation/asgi.py @@ -67,7 +67,7 @@ async def __call__(self, scope, receive, send): request_headers = scope.get('headers') if isinstance(request_headers, list): - request_context = async_tracer.extract(opentracing.Format.BINARY, request_headers) + request_context = async_tracer.extract("{}_trace_context".format(opentracing.Format.BINARY), request_headers) async def send_wrapper(response): span = async_tracer.active_span @@ -84,8 +84,7 @@ async def send_wrapper(response): headers = response.get('headers') if headers is not None: - async_tracer.inject(span.context, opentracing.Format.BINARY, headers, - binary_w3c_injection=True) + async_tracer.inject(span.context, "{}_trace_context".format(opentracing.Format.BINARY), headers) except Exception: logger.debug("send_wrapper: ", exc_info=True) diff --git a/instana/instrumentation/aws/triggers.py b/instana/instrumentation/aws/triggers.py index 70b043d4..aa179ab6 100644 --- a/instana/instrumentation/aws/triggers.py +++ b/instana/instrumentation/aws/triggers.py @@ -8,6 +8,7 @@ import json import base64 from io import BytesIO +import opentracing as ot from ...log import logger @@ -21,9 +22,9 @@ def get_context(tracer, event): is_application_load_balancer_trigger(event) if is_proxy_event: - return tracer.extract('http_headers', event.get('headers', {})) + return tracer.extract(ot.Format.HTTP_HEADERS, event.get('headers', {})) - return tracer.extract('http_headers', event) + return tracer.extract(ot.Format.HTTP_HEADERS, event) def is_api_gateway_proxy_trigger(event): diff --git a/instana/instrumentation/boto3_inst.py b/instana/instrumentation/boto3_inst.py index c7a6c8b3..98e30209 100644 --- a/instana/instrumentation/boto3_inst.py +++ b/instana/instrumentation/boto3_inst.py @@ -12,6 +12,7 @@ from ..util.traceutils import get_active_tracer try: + import opentracing as ot import boto3 from boto3.s3 import inject @@ -28,7 +29,7 @@ def lambda_inject_context(payload, scope): if not isinstance(invoke_payload, dict): invoke_payload = json.loads(invoke_payload) - tracer.inject(scope.span.context, 'http_headers', invoke_payload) + tracer.inject(scope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), invoke_payload) payload['Payload'] = json.dumps(invoke_payload) except Exception: logger.debug("non-fatal lambda_inject_context: ", exc_info=True) diff --git a/instana/instrumentation/django/middleware.py b/instana/instrumentation/django/middleware.py index 21b5a5c4..c314ee0d 100644 --- a/instana/instrumentation/django/middleware.py +++ b/instana/instrumentation/django/middleware.py @@ -33,7 +33,7 @@ def process_request(self, request): try: env = request.environ - ctx = tracer.extract(ot.Format.HTTP_HEADERS, env) + ctx = tracer.extract("{}_trace_context".format(ot.Format.HTTP_HEADERS), env) request.iscope = tracer.start_active_span('django', child_of=ctx) if agent.options.extra_http_headers is not None: @@ -76,7 +76,7 @@ def process_response(self, request, response): if path_tpl: request.iscope.span.set_tag("http.path_tpl", path_tpl) request.iscope.span.set_tag(ext.HTTP_STATUS_CODE, response.status_code) - tracer.inject(request.iscope.span.context, ot.Format.HTTP_HEADERS, response) + tracer.inject(request.iscope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), response) response['Server-Timing'] = "intid;desc=%s" % request.iscope.span.context.trace_id except Exception: logger.debug("Instana middleware @ process_response", exc_info=True) diff --git a/instana/instrumentation/flask/common.py b/instana/instrumentation/flask/common.py index c3413e31..52aa10e5 100644 --- a/instana/instrumentation/flask/common.py +++ b/instana/instrumentation/flask/common.py @@ -63,7 +63,8 @@ def handle_user_exception_with_instana(wrapped, instance, argv, kwargs): span.set_tag(ext.HTTP_STATUS_CODE, int(status_code)) if hasattr(response, 'headers'): - tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers) + tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + response.headers) value = "intid;desc=%s" % scope.span.context.trace_id if hasattr(response.headers, 'add'): response.headers.add('Server-Timing', value) diff --git a/instana/instrumentation/flask/vanilla.py b/instana/instrumentation/flask/vanilla.py index 734d3fa5..460d2abd 100644 --- a/instana/instrumentation/flask/vanilla.py +++ b/instana/instrumentation/flask/vanilla.py @@ -22,7 +22,7 @@ def before_request_with_instana(*argv, **kwargs): env = flask.request.environ ctx = None - ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env) + ctx = tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), env) flask.g.scope = tracer.start_active_span('wsgi', child_of=ctx) span = flask.g.scope.span @@ -38,7 +38,8 @@ def before_request_with_instana(*argv, **kwargs): if 'PATH_INFO' in env: span.set_tag(ext.HTTP_URL, env['PATH_INFO']) if 'QUERY_STRING' in env and len(env['QUERY_STRING']): - scrubbed_params = strip_secrets_from_query(env['QUERY_STRING'], agent.options.secrets_matcher, agent.options.secrets_list) + scrubbed_params = strip_secrets_from_query(env['QUERY_STRING'], agent.options.secrets_matcher, + agent.options.secrets_list) span.set_tag("http.params", scrubbed_params) if 'HTTP_HOST' in env: span.set_tag("http.host", env['HTTP_HOST']) @@ -69,7 +70,8 @@ def after_request_with_instana(response): span.mark_as_errored() span.set_tag(ext.HTTP_STATUS_CODE, int(response.status_code)) - tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers) + tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + response.headers) response.headers.add('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id) except: logger.debug("Flask after_request", exc_info=True) diff --git a/instana/instrumentation/flask/with_blinker.py b/instana/instrumentation/flask/with_blinker.py index 254a20b7..c91d809d 100644 --- a/instana/instrumentation/flask/with_blinker.py +++ b/instana/instrumentation/flask/with_blinker.py @@ -23,7 +23,7 @@ def request_started_with_instana(sender, **extra): env = flask.request.environ ctx = None - ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env) + ctx = tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), env) flask.g.scope = tracer.start_active_span('wsgi', child_of=ctx) span = flask.g.scope.span @@ -39,7 +39,8 @@ def request_started_with_instana(sender, **extra): if 'PATH_INFO' in env: span.set_tag(ext.HTTP_URL, env['PATH_INFO']) if 'QUERY_STRING' in env and len(env['QUERY_STRING']): - scrubbed_params = strip_secrets_from_query(env['QUERY_STRING'], agent.options.secrets_matcher, agent.options.secrets_list) + scrubbed_params = strip_secrets_from_query(env['QUERY_STRING'], agent.options.secrets_matcher, + agent.options.secrets_list) span.set_tag("http.params", scrubbed_params) if 'HTTP_HOST' in env: span.set_tag("http.host", env['HTTP_HOST']) @@ -67,7 +68,8 @@ def request_finished_with_instana(sender, response, **extra): span.mark_as_errored() span.set_tag(ext.HTTP_STATUS_CODE, int(response.status_code)) - tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers) + tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + response.headers) response.headers.add('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id) except: logger.debug("Flask after_request", exc_info=True) diff --git a/instana/instrumentation/pyramid/tweens.py b/instana/instrumentation/pyramid/tweens.py index dd5479b0..09fbaa0b 100644 --- a/instana/instrumentation/pyramid/tweens.py +++ b/instana/instrumentation/pyramid/tweens.py @@ -20,7 +20,7 @@ def __init__(self, handler, registry): self.handler = handler def __call__(self, request): - ctx = tracer.extract(ot.Format.HTTP_HEADERS, dict(request.headers)) + ctx = tracer.extract("{}_trace_context".format(ot.Format.HTTP_HEADERS), dict(request.headers)) scope = tracer.start_active_span('http', child_of=ctx) scope.span.set_tag(ext.SPAN_KIND, ext.SPAN_KIND_RPC_SERVER) @@ -39,14 +39,15 @@ def __call__(self, request): scope.span.set_tag("http.header.%s" % custom_header, request.headers[h]) if len(request.query_string): - scrubbed_params = strip_secrets_from_query(request.query_string, agent.options.secrets_matcher, agent.options.secrets_list) + scrubbed_params = strip_secrets_from_query(request.query_string, agent.options.secrets_matcher, + agent.options.secrets_list) scope.span.set_tag("http.params", scrubbed_params) response = None try: response = self.handler(request) - tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, response.headers) + tracer.inject(scope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), response.headers) response.headers['Server-Timing'] = "intid;desc=%s" % scope.span.context.trace_id except HTTPException as e: response = e diff --git a/instana/instrumentation/sanic_inst.py b/instana/instrumentation/sanic_inst.py index bac2ce94..f6b3d72b 100644 --- a/instana/instrumentation/sanic_inst.py +++ b/instana/instrumentation/sanic_inst.py @@ -40,7 +40,8 @@ def response_details(span, response): span.set_tag('http.status_code', status_code) if response.headers is not None: - async_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS, response.headers) + async_tracer.inject(span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + response.headers) response.headers['Server-Timing'] = "intid;desc=%s" % span.context.trace_id except Exception: logger.debug("send_wrapper: ", exc_info=True) @@ -101,7 +102,7 @@ async def handle_request_with_instana(wrapped, instance, args, kwargs): except AttributeError: pass headers = request.headers.copy() - ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, headers) + ctx = async_tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), headers) with async_tracer.start_active_span("asgi", child_of=ctx) as scope: scope.span.set_tag('span.kind', 'entry') scope.span.set_tag('http.path', request.path) diff --git a/instana/instrumentation/sudsjurko.py b/instana/instrumentation/sudsjurko.py index b0aa81e9..c3899c44 100644 --- a/instana/instrumentation/sudsjurko.py +++ b/instana/instrumentation/sudsjurko.py @@ -13,13 +13,14 @@ from ..util.traceutils import get_active_tracer try: - import suds # noqa + import suds # noqa if (LooseVersion(suds.version.__version__) <= LooseVersion('0.6')): class_method = 'SoapClient.send' else: class_method = '_SoapClient.send' + @wrapt.patch_function_wrapper('suds.client', class_method) def send_with_instana(wrapped, instance, args, kwargs): active_tracer = get_active_tracer() @@ -34,7 +35,8 @@ def send_with_instana(wrapped, instance, args, kwargs): scope.span.set_tag(ext.HTTP_URL, instance.method.location) scope.span.set_tag(ext.HTTP_METHOD, 'POST') - active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance.options.headers) + active_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + instance.options.headers) rv = wrapped(*args, **kwargs) @@ -46,6 +48,7 @@ def send_with_instana(wrapped, instance, args, kwargs): scope.span.set_tag(ext.HTTP_STATUS_CODE, 200) return rv + logger.debug("Instrumenting suds-jurko") except ImportError: pass diff --git a/instana/instrumentation/tornado/client.py b/instana/instrumentation/tornado/client.py index 66f99c86..bea0d64d 100644 --- a/instana/instrumentation/tornado/client.py +++ b/instana/instrumentation/tornado/client.py @@ -47,12 +47,14 @@ def fetch_with_instana(wrapped, instance, argv, kwargs): kwargs = new_kwargs scope = tornado_tracer.start_active_span('tornado-client', child_of=parent_span) - tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, request.headers) + tornado_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + request.headers) # Query param scrubbing parts = request.url.split('?') if len(parts) > 1: - cleaned_qp = strip_secrets_from_query(parts[1], agent.options.secrets_matcher, agent.options.secrets_list) + cleaned_qp = strip_secrets_from_query(parts[1], agent.options.secrets_matcher, + agent.options.secrets_list) scope.span.set_tag("http.params", cleaned_qp) scope.span.set_tag("http.url", parts[0]) @@ -69,6 +71,7 @@ def fetch_with_instana(wrapped, instance, argv, kwargs): logger.debug("tornado fetch", exc_info=True) raise + def finish_tracing(future, scope): try: response = future.result() @@ -84,4 +87,3 @@ def finish_tracing(future, scope): logger.debug("Instrumenting tornado client") except ImportError: pass - diff --git a/instana/instrumentation/tornado/server.py b/instana/instrumentation/tornado/server.py index 89b3599e..a676aa0d 100644 --- a/instana/instrumentation/tornado/server.py +++ b/instana/instrumentation/tornado/server.py @@ -29,12 +29,14 @@ def execute_with_instana(wrapped, instance, argv, kwargs): with tracer_stack_context(): ctx = None if hasattr(instance.request.headers, '__dict__') and '_dict' in instance.request.headers.__dict__: - ctx = tornado_tracer.extract(opentracing.Format.HTTP_HEADERS, instance.request.headers.__dict__['_dict']) + ctx = tornado_tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + instance.request.headers.__dict__['_dict']) scope = tornado_tracer.start_active_span('tornado-server', child_of=ctx) # Query param scrubbing if instance.request.query is not None and len(instance.request.query) > 0: - cleaned_qp = strip_secrets_from_query(instance.request.query, agent.options.secrets_matcher, agent.options.secrets_list) + cleaned_qp = strip_secrets_from_query(instance.request.query, agent.options.secrets_matcher, + agent.options.secrets_list) scope.span.set_tag("http.params", cleaned_qp) url = "%s://%s%s" % (instance.request.protocol, instance.request.host, instance.request.path) @@ -47,13 +49,15 @@ def execute_with_instana(wrapped, instance, argv, kwargs): if agent.options.extra_http_headers is not None: for custom_header in agent.options.extra_http_headers: if custom_header in instance.request.headers: - scope.span.set_tag("http.header.%s" % custom_header, instance.request.headers[custom_header]) + scope.span.set_tag("http.header.%s" % custom_header, + instance.request.headers[custom_header]) setattr(instance.request, "_instana", scope) # Set the context response headers now because tornado doesn't give us a better option to do so # later for this request. - tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance._headers) + tornado_tracer.inject(scope.span.context, + "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), instance._headers) instance.set_header(name='Server-Timing', value="intid;desc=%s" % scope.span.context.trace_id) return wrapped(*argv, **kwargs) @@ -67,7 +71,8 @@ def set_default_headers_with_instana(wrapped, instance, argv, kwargs): return wrapped(*argv, **kwargs) scope = instance.request._instana - tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance._headers) + tornado_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + instance._headers) instance.set_header(name='Server-Timing', value="intid;desc=%s" % scope.span.context.trace_id) @@ -91,6 +96,7 @@ def on_finish_with_instana(wrapped, instance, argv, kwargs): except Exception: logger.debug("tornado on_finish", exc_info=True) + @wrapt.patch_function_wrapper('tornado.web', 'RequestHandler.log_exception') def log_exception_with_instana(wrapped, instance, argv, kwargs): try: @@ -105,7 +111,7 @@ def log_exception_with_instana(wrapped, instance, argv, kwargs): except Exception: logger.debug("tornado log_exception", exc_info=True) + logger.debug("Instrumenting tornado server") except ImportError: pass - diff --git a/instana/instrumentation/urllib3.py b/instana/instrumentation/urllib3.py index f783c23f..9034f1b2 100644 --- a/instana/instrumentation/urllib3.py +++ b/instana/instrumentation/urllib3.py @@ -15,6 +15,7 @@ try: import urllib3 + def collect(instance, args, kwargs): """ Build and return a fully qualified URL for this request """ kvs = dict() @@ -36,7 +37,8 @@ def collect(instance, args, kwargs): parts = kvs['path'].split('?') kvs['path'] = parts[0] if len(parts) == 2: - kvs['query'] = strip_secrets_from_query(parts[1], agent.options.secrets_matcher, agent.options.secrets_list) + kvs['query'] = strip_secrets_from_query(parts[1], agent.options.secrets_matcher, + agent.options.secrets_list) if type(instance) is urllib3.connectionpool.HTTPSConnectionPool: kvs['url'] = 'https://%s:%d%s' % (kvs['host'], kvs['port'], kvs['path']) @@ -48,6 +50,7 @@ def collect(instance, args, kwargs): else: return kvs + def collect_response(scope, response): try: scope.span.set_tag(ext.HTTP_STATUS_CODE, response.status) @@ -62,6 +65,7 @@ def collect_response(scope, response): except Exception: logger.debug("collect_response", exc_info=True) + @wrapt.patch_function_wrapper('urllib3', 'HTTPConnectionPool.urlopen') def urlopen_with_instana(wrapped, instance, args, kwargs): active_tracer = get_active_tracer() @@ -81,7 +85,8 @@ def urlopen_with_instana(wrapped, instance, args, kwargs): scope.span.set_tag(ext.HTTP_METHOD, kvs['method']) if 'headers' in kwargs: - active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, kwargs['headers']) + active_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), + kwargs['headers']) response = wrapped(*args, **kwargs) @@ -92,6 +97,7 @@ def urlopen_with_instana(wrapped, instance, args, kwargs): scope.span.mark_as_errored({'message': e}) raise + logger.debug("Instrumenting urllib3") except ImportError: pass diff --git a/instana/instrumentation/webapp2_inst.py b/instana/instrumentation/webapp2_inst.py index 2e091db5..319a9fa6 100644 --- a/instana/instrumentation/webapp2_inst.py +++ b/instana/instrumentation/webapp2_inst.py @@ -26,7 +26,7 @@ def new_start_response(status, headers, exc_info=None): """Modified start response with additional headers.""" if 'stan_scope' in env: scope = env['stan_scope'] - tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, headers) + tracer.inject(scope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), headers) headers.append(('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id)) res = start_response(status, headers, exc_info) @@ -41,7 +41,7 @@ def new_start_response(status, headers, exc_info=None): else: return start_response(status, headers, exc_info) - ctx = tracer.extract(ot.Format.HTTP_HEADERS, env) + ctx = tracer.extract("{}_trace_context".format(ot.Format.HTTP_HEADERS), env) scope = env['stan_scope'] = tracer.start_active_span("wsgi", child_of=ctx) if agent.options.extra_http_headers is not None: diff --git a/instana/instrumentation/wsgi.py b/instana/instrumentation/wsgi.py index 9e17fece..a2ea5a8c 100644 --- a/instana/instrumentation/wsgi.py +++ b/instana/instrumentation/wsgi.py @@ -22,7 +22,7 @@ def __call__(self, environ, start_response): def new_start_response(status, headers, exc_info=None): """Modified start response with additional headers.""" - tracer.inject(self.scope.span.context, ot.Format.HTTP_HEADERS, headers) + tracer.inject(self.scope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), headers) headers.append(('Server-Timing', "intid;desc=%s" % self.scope.span.context.trace_id)) res = start_response(status, headers, exc_info) @@ -35,7 +35,7 @@ def new_start_response(status, headers, exc_info=None): self.scope.close() return res - ctx = tracer.extract(ot.Format.HTTP_HEADERS, env) + ctx = tracer.extract("{}_trace_context".format(ot.Format.HTTP_HEADERS), env) self.scope = tracer.start_active_span("wsgi", child_of=ctx) if agent.options.extra_http_headers is not None: diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 13cc7624..99b5d63f 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -6,11 +6,6 @@ import sys from ..log import logger -from ..util.ids import header_to_id -from ..span_context import SpanContext -from ..w3c_trace_context.traceparent import Traceparent -from ..w3c_trace_context.tracestate import Tracestate -import os PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -51,12 +46,8 @@ class BasePropagator(object): ALT_HEADER_KEY_TRACEPARENT = "http_traceparent" ALT_HEADER_KEY_TRACESTATE = "http_tracestate" - def __init__(self): - self.__tp = Traceparent() - self.__ts = Tracestate() - @staticmethod - def __extract_headers_dict(carrier): + def _extract_headers_dict(carrier): """ This method converts the incoming carrier into a dict :param carrier: @@ -75,95 +66,8 @@ def __extract_headers_dict(carrier): return dc - def extract(self, carrier): - """ - This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP - requests more extracting steps and logic was required - :param carrier: - :return: the context or None - """ - try: - headers = self.__extract_headers_dict(carrier=carrier) - if headers is None: - return None - headers = {k.lower(): v for k, v in headers.items()} - - trace_id, span_id, level, synthetic, traceparent, tracestate = self.__extract_instana_headers(dc=headers) - - if traceparent: - traceparent = self.__tp.validate(traceparent) - - ctx = self.__determine_span_context(trace_id, span_id, level, synthetic, traceparent, tracestate) - - return ctx - except Exception: - logger.debug("extract error:", exc_info=True) - - def __determine_span_context(self, trace_id, span_id, level, synthetic, traceparent, tracestate): - """ - This method determines the span context depending on a set of conditions being met - Detailed description of the conditions can be found here: - https://github.com/instana/technical-documentation/tree/master/tracing/specification#http-processing-for-instana-tracers - :param trace_id: instana trace id - :param span_id: instana span id - :param level: instana level - :param synthetic: instana synthetic - :return: ctx - """ - correlation = False - disable_traceparent = os.environ.get("INSTANA_DISABLE_W3C_TRACE_CORRELATION", "") - instana_ancestor = None - ctx = SpanContext() - if level and "correlationType" in level: - trace_id, span_id = [None] * 2 - correlation = True - - ctx_level = self.__get_ctx_level(level) - - if trace_id and span_id: - ctx.trace_id = trace_id[-16:] # only the last 16 chars - ctx.span_id = span_id[-16:] # only the last 16 chars - ctx.level = ctx_level - ctx.synthetic = synthetic is not None - - if len(trace_id) > 16: - ctx.long_trace_id = trace_id - - elif traceparent and trace_id is None and span_id is None: - _, tp_trace_id, tp_parent_id, _ = self.__tp.get_traceparent_fields(traceparent) - - if tracestate and "in=" in tracestate: - instana_ancestor = self.__ts.get_instana_ancestor(tracestate) - - if disable_traceparent == "": - ctx.trace_id = tp_trace_id[-16:] - ctx.span_id = tp_parent_id - ctx.level = ctx_level - ctx.synthetic = synthetic is not None - ctx.trace_parent = True - ctx.instana_ancestor = instana_ancestor - ctx.long_trace_id = tp_trace_id - else: - if instana_ancestor: - ctx.trace_id = instana_ancestor.t - ctx.span_id = instana_ancestor.p - ctx.level = ctx_level - ctx.synthetic = synthetic is not None - - elif synthetic: - ctx.synthetic = synthetic - - if correlation: - self.__set_correlation_properties(level, ctx) - - if traceparent: - ctx.traceparent = traceparent - ctx.tracestate = tracestate - - return ctx - @staticmethod - def __get_ctx_level(level): + def _get_ctx_level(level): """ Extract the level value and return it, as it may include correlation values :param level: @@ -176,7 +80,7 @@ def __get_ctx_level(level): return ctx_level @staticmethod - def __set_correlation_properties(level, ctx): + def _set_correlation_properties(level, ctx): """ Set the correlation values if they are present :param level: @@ -189,50 +93,3 @@ def __set_correlation_properties(level, ctx): ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] except Exception: logger.debug("extract instana correlation type/id error:", exc_info=True) - - def __extract_instana_headers(self, dc): - """ - Search carrier for the *HEADER* keys and return the tracing key-values - - :param dc: The dict or list potentially containing context - :return: trace_id, span_id, level, synthetic, traceparent, tracestate - """ - trace_id, span_id, level, synthetic, traceparent, tracestate = [None] * 6 - - # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style - try: - trace_id = dc.get(self.LC_HEADER_KEY_T) or dc.get(self.ALT_LC_HEADER_KEY_T) or dc.get( - self.LC_HEADER_KEY_T.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_T.encode("utf-8")) - if trace_id: - trace_id = header_to_id(trace_id) - - span_id = dc.get(self.LC_HEADER_KEY_S) or dc.get(self.ALT_LC_HEADER_KEY_S) or dc.get( - self.LC_HEADER_KEY_S.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_S.encode("utf-8")) - if span_id: - span_id = header_to_id(span_id) - - level = dc.get(self.LC_HEADER_KEY_L) or dc.get(self.ALT_LC_HEADER_KEY_L) or dc.get( - self.LC_HEADER_KEY_L.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_L.encode("utf-8")) - if level and PY3 is True and isinstance(level, bytes): - level = level.decode("utf-8") - - synthetic = dc.get(self.LC_HEADER_KEY_SYNTHETIC) or dc.get(self.ALT_LC_HEADER_KEY_SYNTHETIC) or dc.get( - self.LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) or dc.get( - self.ALT_LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) - if synthetic: - synthetic = synthetic in ['1', b'1'] - - traceparent = dc.get(self.HEADER_KEY_TRACEPARENT) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT) or dc.get( - self.HEADER_KEY_TRACEPARENT.encode("utf-8")) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT.encode("utf-8")) - if traceparent and PY3 is True and isinstance(traceparent, bytes): - traceparent = traceparent.decode("utf-8") - - tracestate = dc.get(self.HEADER_KEY_TRACESTATE) or dc.get(self.ALT_HEADER_KEY_TRACESTATE) or dc.get( - self.HEADER_KEY_TRACESTATE.encode("utf-8")) or dc.get(self.ALT_HEADER_KEY_TRACESTATE.encode("utf-8")) - if tracestate and PY3 is True and isinstance(tracestate, bytes): - tracestate = tracestate.decode("utf-8") - - except Exception: - logger.debug("extract error:", exc_info=True) - - return trace_id, span_id, level, synthetic, traceparent, tracestate diff --git a/instana/propagators/binary_propagator.py b/instana/propagators/binary_propagator.py index 25ea8121..996cd928 100644 --- a/instana/propagators/binary_propagator.py +++ b/instana/propagators/binary_propagator.py @@ -4,12 +4,10 @@ from __future__ import absolute_import from ..log import logger -from .base_propagator import BasePropagator -from ..w3c_trace_context.traceparent import Traceparent -from ..w3c_trace_context.tracestate import Tracestate +from .http_propagator import HTTPPropagator -class BinaryPropagator(BasePropagator): +class BinaryPropagator(HTTPPropagator): """ A Propagator for BINARY. The BINARY format represents SpanContexts in an opaque bytearray carrier. @@ -20,62 +18,33 @@ class BinaryPropagator(BasePropagator): HEADER_KEY_S = b'x-instana-s' HEADER_KEY_L = b'x-instana-l' HEADER_SERVER_TIMING = b'server-timing' - HEADER_KEY_TRACEPARENT = b'traceparent' - HEADER_KEY_TRACESTATE = b'tracestate' def __init__(self): - self.__tp = Traceparent() - self.__ts = Tracestate() super(BinaryPropagator, self).__init__() - def inject(self, span_context, carrier, binary_w3c_injection=False): + def inject(self, span_context, carrier): try: trace_id = str.encode(span_context.trace_id) span_id = str.encode(span_context.span_id) level = str.encode(str(span_context.level)) server_timing = str.encode("intid;desc=%s" % span_context.trace_id) - if binary_w3c_injection: - traceparent = span_context.traceparent - tracestate = span_context.tracestate - traceparent = self.__tp.update_traceparent(traceparent, span_context.trace_id, span_context.span_id, - span_context.level) - tracestate = self.__ts.update_tracestate(tracestate, span_context.trace_id, span_context.span_id) - try: - traceparent = str.encode(traceparent) - tracestate = str.encode(tracestate) - except Exception: - traceparent, tracestate = [None] * 2 - else: - traceparent, tracestate = [None] * 2 if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): - if traceparent and tracestate: - carrier[self.HEADER_KEY_TRACEPARENT] = traceparent - carrier[self.HEADER_KEY_TRACESTATE] = tracestate carrier[self.HEADER_KEY_T] = trace_id carrier[self.HEADER_KEY_S] = span_id carrier[self.HEADER_KEY_L] = level carrier[self.HEADER_SERVER_TIMING] = server_timing elif isinstance(carrier, list): - if traceparent and tracestate: - carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) - carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) carrier.append((self.HEADER_KEY_T, trace_id)) carrier.append((self.HEADER_KEY_S, span_id)) carrier.append((self.HEADER_KEY_L, level)) carrier.append((self.HEADER_SERVER_TIMING, server_timing)) elif isinstance(carrier, tuple): - if traceparent and tracestate: - carrier = carrier.__add__(((self.HEADER_KEY_TRACEPARENT, traceparent),)) - carrier = carrier.__add__(((self.HEADER_KEY_TRACESTATE, tracestate),)) carrier = carrier.__add__(((self.HEADER_KEY_T, trace_id),)) carrier = carrier.__add__(((self.HEADER_KEY_S, span_id),)) carrier = carrier.__add__(((self.HEADER_KEY_L, level),)) carrier = carrier.__add__(((self.HEADER_SERVER_TIMING, server_timing),)) elif hasattr(carrier, '__setitem__'): - if traceparent and tracestate: - carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) - carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) carrier.__setitem__(self.HEADER_KEY_T, trace_id) carrier.__setitem__(self.HEADER_KEY_S, span_id) carrier.__setitem__(self.HEADER_KEY_L, level) @@ -86,3 +55,5 @@ def inject(self, span_context, carrier, binary_w3c_injection=False): return carrier except Exception: logger.debug("inject error:", exc_info=True) + + diff --git a/instana/propagators/binary_propagator_tc.py b/instana/propagators/binary_propagator_tc.py new file mode 100644 index 00000000..50e2ce9d --- /dev/null +++ b/instana/propagators/binary_propagator_tc.py @@ -0,0 +1,90 @@ +# (c) Copyright IBM Corp. 2021 +# (c) Copyright Instana Inc. 2020 + +from __future__ import absolute_import + +from ..log import logger +from .http_propagator_tc import HTTPPropagatorTC +from ..w3c_trace_context.traceparent import Traceparent +from ..w3c_trace_context.tracestate import Tracestate + + +class BinaryPropagatorTC(HTTPPropagatorTC): + """ + A Propagator for BINARY. + The BINARY format represents SpanContexts in an opaque bytearray carrier. + """ + + # ByteArray variations from base class + HEADER_KEY_T = b'x-instana-t' + HEADER_KEY_S = b'x-instana-s' + HEADER_KEY_L = b'x-instana-l' + HEADER_SERVER_TIMING = b'server-timing' + HEADER_KEY_TRACEPARENT = b'traceparent' + HEADER_KEY_TRACESTATE = b'tracestate' + + def __init__(self): + self.__tp = Traceparent() + self.__ts = Tracestate() + super(BinaryPropagatorTC, self).__init__() + + def inject(self, span_context, carrier, binary_w3c_injection=False): + try: + trace_id = str.encode(span_context.trace_id) + span_id = str.encode(span_context.span_id) + level = str.encode(str(span_context.level)) + server_timing = str.encode("intid;desc=%s" % span_context.trace_id) + if binary_w3c_injection: + traceparent = span_context.traceparent + tracestate = span_context.tracestate + traceparent = self.__tp.update_traceparent(traceparent, span_context.trace_id, span_context.span_id, + span_context.level) + tracestate = self.__ts.update_tracestate(tracestate, span_context.trace_id, span_context.span_id) + try: + traceparent = str.encode(traceparent) + tracestate = str.encode(tracestate) + except Exception: + traceparent, tracestate = [None] * 2 + else: + traceparent, tracestate = [None] * 2 + + if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): + if traceparent and tracestate: + carrier[self.HEADER_KEY_TRACEPARENT] = traceparent + carrier[self.HEADER_KEY_TRACESTATE] = tracestate + carrier[self.HEADER_KEY_T] = trace_id + carrier[self.HEADER_KEY_S] = span_id + carrier[self.HEADER_KEY_L] = level + carrier[self.HEADER_SERVER_TIMING] = server_timing + elif isinstance(carrier, list): + if traceparent and tracestate: + carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) + carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) + carrier.append((self.HEADER_KEY_T, trace_id)) + carrier.append((self.HEADER_KEY_S, span_id)) + carrier.append((self.HEADER_KEY_L, level)) + carrier.append((self.HEADER_SERVER_TIMING, server_timing)) + elif isinstance(carrier, tuple): + if traceparent and tracestate: + carrier = carrier.__add__(((self.HEADER_KEY_TRACEPARENT, traceparent),)) + carrier = carrier.__add__(((self.HEADER_KEY_TRACESTATE, tracestate),)) + carrier = carrier.__add__(((self.HEADER_KEY_T, trace_id),)) + carrier = carrier.__add__(((self.HEADER_KEY_S, span_id),)) + carrier = carrier.__add__(((self.HEADER_KEY_L, level),)) + carrier = carrier.__add__(((self.HEADER_SERVER_TIMING, server_timing),)) + elif hasattr(carrier, '__setitem__'): + if traceparent and tracestate: + carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) + carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) + carrier.__setitem__(self.HEADER_KEY_T, trace_id) + carrier.__setitem__(self.HEADER_KEY_S, span_id) + carrier.__setitem__(self.HEADER_KEY_L, level) + carrier.__setitem__(self.HEADER_SERVER_TIMING, server_timing) + else: + raise Exception("Unsupported carrier type", type(carrier)) + + return carrier + except Exception: + logger.debug("inject error:", exc_info=True) + + diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index 6d346866..f9cbe243 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -3,10 +3,14 @@ from __future__ import absolute_import +import sys from ..log import logger from .base_propagator import BasePropagator -from ..w3c_trace_context.traceparent import Traceparent -from ..w3c_trace_context.tracestate import Tracestate +from ..util.ids import header_to_id +from ..span_context import SpanContext + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 class HTTPPropagator(BasePropagator): @@ -18,8 +22,6 @@ class HTTPPropagator(BasePropagator): """ def __init__(self): - self.__tp = Traceparent() - self.__ts = Tracestate() super(HTTPPropagator, self).__init__() def inject(self, span_context, carrier): @@ -27,38 +29,115 @@ def inject(self, span_context, carrier): trace_id = span_context.trace_id span_id = span_context.span_id level = span_context.level - if span_context.long_trace_id and not span_context.trace_parent: - tp_trace_id = span_context.long_trace_id - else: - tp_trace_id = trace_id - traceparent = span_context.traceparent - tracestate = span_context.tracestate - traceparent = self.__tp.update_traceparent(traceparent, tp_trace_id, span_id, level) - tracestate = self.__ts.update_tracestate(tracestate, trace_id, span_id) if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): - if traceparent and tracestate: - carrier[self.HEADER_KEY_TRACEPARENT] = traceparent - carrier[self.HEADER_KEY_TRACESTATE] = tracestate carrier[self.HEADER_KEY_T] = trace_id carrier[self.HEADER_KEY_S] = span_id - carrier[self.HEADER_KEY_L] = "1" + carrier[self.HEADER_KEY_L] = str(level) elif isinstance(carrier, list): - if traceparent and tracestate: - carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) - carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) carrier.append((self.HEADER_KEY_T, trace_id)) carrier.append((self.HEADER_KEY_S, span_id)) - carrier.append((self.HEADER_KEY_L, "1")) + carrier.append((self.HEADER_KEY_L, str(level))) elif hasattr(carrier, '__setitem__'): - if traceparent and tracestate: - carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) - carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) carrier.__setitem__(self.HEADER_KEY_T, trace_id) carrier.__setitem__(self.HEADER_KEY_S, span_id) - carrier.__setitem__(self.HEADER_KEY_L, "1") + carrier.__setitem__(self.HEADER_KEY_L, str(level)) else: raise Exception("Unsupported carrier type", type(carrier)) except Exception: - logger.debug("inject error:", exc_info=True) \ No newline at end of file + logger.debug("inject error:", exc_info=True) + + def extract(self, carrier): + """ + This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP + requests more extracting steps and logic was required + :param carrier: + :return: the context or None + """ + try: + headers = self._extract_headers_dict(carrier=carrier) + if headers is None: + return None + headers = {k.lower(): v for k, v in headers.items()} + + trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) + + ctx = self.__determine_span_context(trace_id, span_id, level, synthetic) + + return ctx + except Exception: + logger.debug("extract error:", exc_info=True) + + def __extract_instana_headers(self, dc): + """ + Search carrier for the *HEADER* keys and return the tracing key-values + + :param dc: The dict or list potentially containing context + :return: trace_id, span_id, level, synthetic, traceparent, tracestate + """ + trace_id, span_id, level, synthetic, traceparent, tracestate = [None] * 6 + + # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style + try: + trace_id = dc.get(self.LC_HEADER_KEY_T) or dc.get(self.ALT_LC_HEADER_KEY_T) or dc.get( + self.LC_HEADER_KEY_T.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_T.encode("utf-8")) + if trace_id: + trace_id = header_to_id(trace_id) + + span_id = dc.get(self.LC_HEADER_KEY_S) or dc.get(self.ALT_LC_HEADER_KEY_S) or dc.get( + self.LC_HEADER_KEY_S.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_S.encode("utf-8")) + if span_id: + span_id = header_to_id(span_id) + + level = dc.get(self.LC_HEADER_KEY_L) or dc.get(self.ALT_LC_HEADER_KEY_L) or dc.get( + self.LC_HEADER_KEY_L.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_L.encode("utf-8")) + if level and PY3 is True and isinstance(level, bytes): + level = level.decode("utf-8") + + synthetic = dc.get(self.LC_HEADER_KEY_SYNTHETIC) or dc.get(self.ALT_LC_HEADER_KEY_SYNTHETIC) or dc.get( + self.LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) or dc.get( + self.ALT_LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) + if synthetic: + synthetic = synthetic in ['1', b'1'] + + except Exception: + logger.debug("extract error:", exc_info=True) + + return trace_id, span_id, level, synthetic + + def __determine_span_context(self, trace_id, span_id, level, synthetic): + """ + This method determines the span context + :param **kwargs: + :param **kwargs: + :param trace_id: instana trace id + :param span_id: instana span id + :param level: instana level + :param synthetic: instana synthetic + :return: ctx + """ + correlation = False + ctx = SpanContext() + if level and "correlationType" in level: + trace_id, span_id = [None] * 2 + correlation = True + + ctx_level = self._get_ctx_level(level) + + if trace_id and span_id: + ctx.trace_id = trace_id[-16:] # only the last 16 chars + ctx.span_id = span_id[-16:] # only the last 16 chars + ctx.level = ctx_level + ctx.synthetic = synthetic is not None + + if len(trace_id) > 16: + ctx.long_trace_id = trace_id + + elif synthetic: + ctx.synthetic = synthetic + + if correlation: + self._set_correlation_properties(level, ctx) + + return ctx diff --git a/instana/propagators/http_propagator_tc.py b/instana/propagators/http_propagator_tc.py new file mode 100644 index 00000000..076c441e --- /dev/null +++ b/instana/propagators/http_propagator_tc.py @@ -0,0 +1,204 @@ +# (c) Copyright IBM Corp. 2021 +# (c) Copyright Instana Inc. 2020 + +from __future__ import absolute_import + +import sys +from ..log import logger +from .base_propagator import BasePropagator +from ..util.ids import header_to_id +from ..span_context import SpanContext +from ..w3c_trace_context.traceparent import Traceparent +from ..w3c_trace_context.tracestate import Tracestate +import os +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + + +class HTTPPropagatorTC(BasePropagator): + """ + Instana Propagator for Format.HTTP_HEADERS. + + The HTTP_HEADERS format deals with key-values with string to string mapping. + The character set should be restricted to HTTP compatible. + """ + + def __init__(self): + self._tp = Traceparent() + self._ts = Tracestate() + super(HTTPPropagatorTC, self).__init__() + + def inject(self, span_context, carrier): + try: + trace_id = span_context.trace_id + span_id = span_context.span_id + level = span_context.level + if span_context.long_trace_id and not span_context.trace_parent: + tp_trace_id = span_context.long_trace_id + else: + tp_trace_id = trace_id + traceparent = span_context.traceparent + tracestate = span_context.tracestate + traceparent = self._tp.update_traceparent(traceparent, tp_trace_id, span_id, level) + tracestate = self._ts.update_tracestate(tracestate, trace_id, span_id) + + if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): + if traceparent and tracestate: + carrier[self.HEADER_KEY_TRACEPARENT] = traceparent + carrier[self.HEADER_KEY_TRACESTATE] = tracestate + carrier[self.HEADER_KEY_T] = trace_id + carrier[self.HEADER_KEY_S] = span_id + carrier[self.HEADER_KEY_L] = "1" + elif isinstance(carrier, list): + if traceparent and tracestate: + carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) + carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) + carrier.append((self.HEADER_KEY_T, trace_id)) + carrier.append((self.HEADER_KEY_S, span_id)) + carrier.append((self.HEADER_KEY_L, "1")) + elif hasattr(carrier, '__setitem__'): + if traceparent and tracestate: + carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) + carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) + carrier.__setitem__(self.HEADER_KEY_T, trace_id) + carrier.__setitem__(self.HEADER_KEY_S, span_id) + carrier.__setitem__(self.HEADER_KEY_L, "1") + else: + raise Exception("Unsupported carrier type", type(carrier)) + + except Exception: + logger.debug("inject error:", exc_info=True) + + def extract(self, carrier): + """ + This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP + requests more extracting steps and logic was required + :param carrier: + :return: the context or None + """ + try: + headers = self._extract_headers_dict(carrier=carrier) + if headers is None: + return None + headers = {k.lower(): v for k, v in headers.items()} + + trace_id, span_id, level, synthetic, traceparent, tracestate = self.__extract_instana_headers(dc=headers) + + if traceparent: + traceparent = self._tp.validate(traceparent) + + ctx = self.__determine_span_context(trace_id, span_id, level, synthetic, traceparent, tracestate) + + return ctx + except Exception: + logger.debug("extract error:", exc_info=True) + + def __determine_span_context(self, trace_id, span_id, level, synthetic, traceparent, tracestate): + """ + This method determines the span context depending on a set of conditions being met + Detailed description of the conditions can be found here: + https://github.com/instana/technical-documentation/tree/master/tracing/specification#http-processing-for-instana-tracers + :param trace_id: instana trace id + :param span_id: instana span id + :param level: instana level + :param synthetic: instana synthetic + :return: ctx + """ + correlation = False + disable_traceparent = os.environ.get("INSTANA_DISABLE_W3C_TRACE_CORRELATION", "") + instana_ancestor = None + ctx = SpanContext() + if level and "correlationType" in level: + trace_id, span_id = [None] * 2 + correlation = True + + ctx_level = self._get_ctx_level(level) + + if trace_id and span_id: + ctx.trace_id = trace_id[-16:] # only the last 16 chars + ctx.span_id = span_id[-16:] # only the last 16 chars + ctx.level = ctx_level + ctx.synthetic = synthetic is not None + + if len(trace_id) > 16: + ctx.long_trace_id = trace_id + + elif traceparent and trace_id is None and span_id is None: + _, tp_trace_id, tp_parent_id, _ = self._tp.get_traceparent_fields(traceparent) + + if tracestate and "in=" in tracestate: + instana_ancestor = self._ts.get_instana_ancestor(tracestate) + + if disable_traceparent == "": + ctx.trace_id = tp_trace_id[-16:] + ctx.span_id = tp_parent_id + ctx.level = ctx_level + ctx.synthetic = synthetic is not None + ctx.trace_parent = True + ctx.instana_ancestor = instana_ancestor + ctx.long_trace_id = tp_trace_id + else: + if instana_ancestor: + ctx.trace_id = instana_ancestor.t + ctx.span_id = instana_ancestor.p + ctx.level = ctx_level + ctx.synthetic = synthetic is not None + + elif synthetic: + ctx.synthetic = synthetic + + if correlation: + self._set_correlation_properties(level, ctx) + + if traceparent: + ctx.traceparent = traceparent + ctx.tracestate = tracestate + + return ctx + + def __extract_instana_headers(self, dc): + """ + Search carrier for the *HEADER* keys and return the tracing key-values + + :param dc: The dict or list potentially containing context + :return: trace_id, span_id, level, synthetic, traceparent, tracestate + """ + trace_id, span_id, level, synthetic, traceparent, tracestate = [None] * 6 + + # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style + try: + trace_id = dc.get(self.LC_HEADER_KEY_T) or dc.get(self.ALT_LC_HEADER_KEY_T) or dc.get( + self.LC_HEADER_KEY_T.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_T.encode("utf-8")) + if trace_id: + trace_id = header_to_id(trace_id) + + span_id = dc.get(self.LC_HEADER_KEY_S) or dc.get(self.ALT_LC_HEADER_KEY_S) or dc.get( + self.LC_HEADER_KEY_S.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_S.encode("utf-8")) + if span_id: + span_id = header_to_id(span_id) + + level = dc.get(self.LC_HEADER_KEY_L) or dc.get(self.ALT_LC_HEADER_KEY_L) or dc.get( + self.LC_HEADER_KEY_L.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_L.encode("utf-8")) + if level and PY3 is True and isinstance(level, bytes): + level = level.decode("utf-8") + + synthetic = dc.get(self.LC_HEADER_KEY_SYNTHETIC) or dc.get(self.ALT_LC_HEADER_KEY_SYNTHETIC) or dc.get( + self.LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) or dc.get( + self.ALT_LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) + if synthetic: + synthetic = synthetic in ['1', b'1'] + + traceparent = dc.get(self.HEADER_KEY_TRACEPARENT) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT) or dc.get( + self.HEADER_KEY_TRACEPARENT) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT.encode("utf-8")) + if traceparent and PY3 is True and isinstance(traceparent, bytes): + traceparent = traceparent.decode("utf-8") + + tracestate = dc.get(self.HEADER_KEY_TRACESTATE) or dc.get(self.ALT_HEADER_KEY_TRACESTATE) or dc.get( + self.HEADER_KEY_TRACESTATE) or dc.get(self.ALT_HEADER_KEY_TRACESTATE.encode("utf-8")) + if tracestate and PY3 is True and isinstance(tracestate, bytes): + tracestate = tracestate.decode("utf-8") + + except Exception: + logger.debug("extract error:", exc_info=True) + + return trace_id, span_id, level, synthetic, traceparent, tracestate diff --git a/instana/propagators/text_propagator.py b/instana/propagators/text_propagator.py index be0c553e..d780a61f 100644 --- a/instana/propagators/text_propagator.py +++ b/instana/propagators/text_propagator.py @@ -4,10 +4,10 @@ from __future__ import absolute_import from ..log import logger -from .base_propagator import BasePropagator +from .http_propagator import HTTPPropagator -class TextPropagator(BasePropagator): +class TextPropagator(HTTPPropagator): """ Instana context propagator for TEXT_MAP. diff --git a/instana/span.py b/instana/span.py index 22a0c1ef..bd33d67c 100644 --- a/instana/span.py +++ b/instana/span.py @@ -204,9 +204,6 @@ def __init__(self, span, source, service_name, **kwargs): if self.k == 1 and service_name is not None: self.data["service"] = service_name - # if self.k == 1: - # self._populate_extra_span_attributes(span) - self.data["sdk"]["name"] = span.operation_name self.data["sdk"]["type"] = span_kind[0] self.data["sdk"]["custom"]["tags"] = self._validate_tags(span.tags) diff --git a/instana/tracer.py b/instana/tracer.py index 728f022c..636b60d4 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -16,8 +16,10 @@ from .span import InstanaSpan, RegisteredSpan from .recorder import StanRecorder, InstanaSampler from .propagators.http_propagator import HTTPPropagator +from .propagators.http_propagator_tc import HTTPPropagatorTC from .propagators.text_propagator import TextPropagator from .propagators.binary_propagator import BinaryPropagator +from .propagators.binary_propagator_tc import BinaryPropagatorTC class InstanaTracer(BasicTracer): @@ -30,8 +32,10 @@ def __init__(self, scope_manager=None, recorder=None): recorder, InstanaSampler(), scope_manager) self._propagators[ot.Format.HTTP_HEADERS] = HTTPPropagator() + self._propagators["{}_trace_context".format(ot.Format.HTTP_HEADERS)] = HTTPPropagatorTC() self._propagators[ot.Format.TEXT_MAP] = TextPropagator() self._propagators[ot.Format.BINARY] = BinaryPropagator() + self._propagators["{}_trace_context".format(ot.Format.BINARY)] = BinaryPropagatorTC() def start_active_span(self, operation_name, @@ -121,22 +125,9 @@ def start_span(self, return span - def inject(self, span_context, format, carrier, binary_w3c_injection=False): - """ - In this method the binary_w3c_injection parameter is to be used only for the binary injection call, - by default in the binary injection the w3c trace context parameters are not getting injected. if there is - a wish to inject them for a specific instrumentation the inject method here should be called with the - parameter set to True - :param span_context: - :param format: - :param carrier: - :param binary_w3c_injection: default False - :return: - """ - if format in self._propagators and format != ot.Format.BINARY: + def inject(self, span_context, format, carrier): + if format in self._propagators: return self._propagators[format].inject(span_context, carrier) - elif format == ot.Format.BINARY: - return self._propagators[format].inject(span_context, carrier, binary_w3c_injection) raise ot.UnsupportedFormatException() diff --git a/tests/clients/test_pika.py b/tests/clients/test_pika.py index 5d85afe5..bfa9740c 100644 --- a/tests/clients/test_pika.py +++ b/tests/clients/test_pika.py @@ -80,8 +80,6 @@ def test_basic_publish(self, send_method, _unused): pika.spec.Basic.Publish( exchange="test.exchange", routing_key="test.queue"), (pika.spec.BasicProperties(headers={ - 'traceparent': '00-0000000000000000{}-{}-01'.format(rabbitmq_span.t, rabbitmq_span.s), - 'tracestate': 'in={};{}'.format(rabbitmq_span.t, rabbitmq_span.s), "X-INSTANA-T": rabbitmq_span.t, "X-INSTANA-S": rabbitmq_span.s, "X-INSTANA-L": "1" @@ -110,8 +108,6 @@ def test_basic_publish_with_headers(self, send_method, _unused): pika.spec.Basic.Publish( exchange="test.exchange", routing_key="test.queue"), (pika.spec.BasicProperties(headers={ - 'traceparent': '00-0000000000000000{}-{}-01'.format(rabbitmq_span.t, rabbitmq_span.s), - 'tracestate': 'in={};{}'.format(rabbitmq_span.t, rabbitmq_span.s), "X-Custom-1": "test", "X-INSTANA-T": rabbitmq_span.t, "X-INSTANA-S": rabbitmq_span.s, diff --git a/tests/propagators/test_binary_propagator.py b/tests/propagators/test_binary_propagator.py index 3fd15cb1..089c79be 100644 --- a/tests/propagators/test_binary_propagator.py +++ b/tests/propagators/test_binary_propagator.py @@ -1,14 +1,14 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 -from instana.propagators.binary_propagator import BinaryPropagator +from instana.propagators.binary_propagator_tc import BinaryPropagatorTC from instana.span_context import SpanContext import unittest class TestBinaryPropagator(unittest.TestCase): def setUp(self): - self.bp = BinaryPropagator() + self.bp = BinaryPropagatorTC() def test_inject_carrier_dict(self): carrier = {} diff --git a/tests/propagators/test_base_propagator.py b/tests/propagators/test_http_propagator_tc.py similarity index 90% rename from tests/propagators/test_base_propagator.py rename to tests/propagators/test_http_propagator_tc.py index d527465c..f83ace5d 100644 --- a/tests/propagators/test_base_propagator.py +++ b/tests/propagators/test_http_propagator_tc.py @@ -1,16 +1,16 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 -from instana.propagators.base_propagator import BasePropagator +from instana.propagators.http_propagator_tc import HTTPPropagatorTC from instana.w3c_trace_context.traceparent import Traceparent from instana.span_context import SpanContext from mock import patch import unittest -class TestBasePropagator(unittest.TestCase): +class TestHTTPPropagatorTC(unittest.TestCase): def setUp(self): - self.bp = BasePropagator() + self.hptc = HTTPPropagatorTC() @patch.object(Traceparent, "get_traceparent_fields") @patch.object(Traceparent, "validate") @@ -24,7 +24,7 @@ def test_extract_carrier_dict(self, mock_validate, mock_get_traceparent_fields): } mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' mock_get_traceparent_fields.return_value = ["00", "4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7", "01"] - ctx = self.bp.extract(carrier) + ctx = self.hptc.extract(carrier) self.assertEqual(ctx.correlation_id, '1234567890abcdef') self.assertEqual(ctx.correlation_type, "web") self.assertIsNone(ctx.instana_ancestor) @@ -40,17 +40,17 @@ def test_extract_carrier_dict(self, mock_validate, mock_get_traceparent_fields): @patch.object(Traceparent, "get_traceparent_fields") @patch.object(Traceparent, "validate") def test_extract_carrier_list(self, mock_validate, mock_get_traceparent_fields): - carrier = [(b'user-agent', b'python-requests/2.23.0'), (b'accept-encoding', b'gzip, deflate'), - (b'accept', b'*/*'), (b'connection', b'keep-alive'), - (b'traceparent', b'00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'), - (b'tracestate', b'congo=t61rcWkgMzE'), - (b'X-INSTANA-T', b'1234d0e0e4736234'), - (b'X-INSTANA-S', b'1234567890abcdef'), - (b'X-INSTANA-L', b'1')] + carrier = [('user-agent', 'python-requests/2.23.0'), ('accept-encoding', 'gzip, deflate'), + ('accept', '*/*'), ('connection', 'keep-alive'), + ('traceparent', '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'), + ('tracestate', 'congo=t61rcWkgMzE'), + ('X-INSTANA-T', '1234d0e0e4736234'), + ('X-INSTANA-S', '1234567890abcdef'), + ('X-INSTANA-L', '1')] mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' mock_get_traceparent_fields.return_value = ["00", "4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7", "01"] - ctx = self.bp.extract(carrier) + ctx = self.hptc.extract(carrier) self.assertIsNone(ctx.correlation_id) self.assertIsNone(ctx.correlation_type) self.assertIsNone(ctx.instana_ancestor) @@ -80,7 +80,7 @@ def test_extract_carrier_dict_validate_Exception_None_returned(self, mock_valida 'X-INSTANA-L': '1, correlationType=web; correlationId=1234567890abcdef' } mock_validate.return_value = None - ctx = self.bp.extract(carrier) + ctx = self.hptc.extract(carrier) self.assertTrue(isinstance(ctx, SpanContext)) assert ctx.trace_id is None assert ctx.span_id is None @@ -98,7 +98,7 @@ def test_extract_fake_exception(self, mock_validate): 'X-INSTANA-L': '1, correlationType=web; correlationId=1234567890abcdef' } mock_validate.side_effect = Exception - ctx = self.bp.extract(carrier) + ctx = self.hptc.extract(carrier) self.assertIsNone(ctx) @patch.object(Traceparent, "get_traceparent_fields") @@ -120,7 +120,7 @@ def test_extract_carrier_dict_corrupted_level_header(self, mock_validate, mock_g } mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' mock_get_traceparent_fields.return_value = ["00", "4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7", "01"] - ctx = self.bp.extract(carrier) + ctx = self.hptc.extract(carrier) self.assertIsNone(ctx.correlation_id) self.assertIsNone(ctx.correlation_type) self.assertIsNone(ctx.instana_ancestor) @@ -152,7 +152,7 @@ def test_extract_carrier_dict_level_header_not_splitable(self, mock_validate, mo } mock_validate.return_value = '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01' mock_get_traceparent_fields.return_value = ["00", "4bf92f3577b34da6a3ce929d0e0e4736", "00f067aa0ba902b7", "01"] - ctx = self.bp.extract(carrier) + ctx = self.hptc.extract(carrier) self.assertIsNone(ctx.correlation_id) self.assertIsNone(ctx.correlation_type) self.assertIsNone(ctx.instana_ancestor) From cd880450f0b16d5f8e19dede89eb0f8320f1ad78 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 1 Sep 2021 08:54:55 +0200 Subject: [PATCH 37/39] changes based on the review, propagators got merged, a parameter is defining the w3c trace context propagation --- instana/instrumentation/aiohttp/client.py | 15 +- instana/instrumentation/aiohttp/server.py | 7 +- instana/instrumentation/asgi.py | 6 +- instana/instrumentation/boto3_inst.py | 2 +- instana/instrumentation/django/middleware.py | 5 +- instana/instrumentation/flask/common.py | 4 +- instana/instrumentation/flask/vanilla.py | 6 +- instana/instrumentation/flask/with_blinker.py | 6 +- instana/instrumentation/pyramid/tweens.py | 4 +- instana/instrumentation/sanic_inst.py | 6 +- instana/instrumentation/sudsjurko.py | 4 +- instana/instrumentation/tornado/client.py | 4 +- instana/instrumentation/tornado/server.py | 13 +- instana/instrumentation/urllib3.py | 4 +- instana/instrumentation/webapp2_inst.py | 4 +- instana/instrumentation/wsgi.py | 4 +- instana/propagators/base_propagator.py | 203 ++++++++++++++++- instana/propagators/binary_propagator.py | 32 ++- instana/propagators/binary_propagator_tc.py | 90 -------- instana/propagators/http_propagator.py | 120 ++--------- instana/propagators/http_propagator_tc.py | 204 ------------------ instana/propagators/text_propagator.py | 8 +- instana/tracer.py | 12 +- instana/util/ids.py | 34 ++- instana/version.py | 2 +- instana/w3c_trace_context/traceparent.py | 2 +- tests/propagators/test_binary_propagator.py | 10 +- ...opagator_tc.py => test_http_propagator.py} | 4 +- tests/test_id_management.py | 18 +- 29 files changed, 359 insertions(+), 474 deletions(-) delete mode 100644 instana/propagators/binary_propagator_tc.py delete mode 100644 instana/propagators/http_propagator_tc.py rename tests/propagators/{test_http_propagator_tc.py => test_http_propagator.py} (98%) diff --git a/instana/instrumentation/aiohttp/client.py b/instana/instrumentation/aiohttp/client.py index 4811e607..f6261b69 100644 --- a/instana/instrumentation/aiohttp/client.py +++ b/instana/instrumentation/aiohttp/client.py @@ -10,11 +10,11 @@ from ...singletons import agent, async_tracer from ...util.secrets import strip_secrets_from_query - try: import aiohttp import asyncio + async def stan_request_start(session, trace_config_ctx, params): try: parent_span = async_tracer.active_span @@ -27,17 +27,20 @@ async def stan_request_start(session, trace_config_ctx, params): scope = async_tracer.start_active_span("aiohttp-client", child_of=parent_span) trace_config_ctx.scope = scope - async_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), params.headers) + async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, params.headers, + disable_w3c_trace_context=False) parts = str(params.url).split('?') if len(parts) > 1: - cleaned_qp = strip_secrets_from_query(parts[1], agent.options.secrets_matcher, agent.options.secrets_list) + cleaned_qp = strip_secrets_from_query(parts[1], agent.options.secrets_matcher, + agent.options.secrets_list) scope.span.set_tag("http.params", cleaned_qp) scope.span.set_tag("http.url", parts[0]) scope.span.set_tag('http.method', params.method) except Exception: logger.debug("stan_request_start", exc_info=True) + async def stan_request_end(session, trace_config_ctx, params): try: scope = trace_config_ctx.scope @@ -56,6 +59,7 @@ async def stan_request_end(session, trace_config_ctx, params): except Exception: logger.debug("stan_request_end", exc_info=True) + async def stan_request_exception(session, trace_config_ctx, params): try: scope = trace_config_ctx.scope @@ -66,7 +70,8 @@ async def stan_request_exception(session, trace_config_ctx, params): except Exception: logger.debug("stan_request_exception", exc_info=True) - @wrapt.patch_function_wrapper('aiohttp.client','ClientSession.__init__') + + @wrapt.patch_function_wrapper('aiohttp.client', 'ClientSession.__init__') def init_with_instana(wrapped, instance, argv, kwargs): instana_trace_config = aiohttp.TraceConfig() instana_trace_config.on_request_start.append(stan_request_start) @@ -79,7 +84,7 @@ def init_with_instana(wrapped, instance, argv, kwargs): return wrapped(*argv, **kwargs) + logger.debug("Instrumenting aiohttp client") except ImportError: pass - diff --git a/instana/instrumentation/aiohttp/server.py b/instana/instrumentation/aiohttp/server.py index cf71e87d..787cb077 100644 --- a/instana/instrumentation/aiohttp/server.py +++ b/instana/instrumentation/aiohttp/server.py @@ -20,7 +20,8 @@ @middleware async def stan_middleware(request, handler): try: - ctx = async_tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), request.headers) + ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, request.headers, + disable_w3c_trace_context=False) request['scope'] = async_tracer.start_active_span('aiohttp-server', child_of=ctx) scope = request['scope'] @@ -56,8 +57,8 @@ async def stan_middleware(request, handler): scope.span.mark_as_errored() scope.span.set_tag("http.status_code", response.status) - async_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - response.headers) + async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers, + disable_w3c_trace_context=False) response.headers['Server-Timing'] = "intid;desc=%s" % scope.span.context.trace_id return response diff --git a/instana/instrumentation/asgi.py b/instana/instrumentation/asgi.py index c87282a4..d52a0dd9 100644 --- a/instana/instrumentation/asgi.py +++ b/instana/instrumentation/asgi.py @@ -67,7 +67,8 @@ async def __call__(self, scope, receive, send): request_headers = scope.get('headers') if isinstance(request_headers, list): - request_context = async_tracer.extract("{}_trace_context".format(opentracing.Format.BINARY), request_headers) + request_context = async_tracer.extract(opentracing.Format.BINARY, request_headers, + disable_w3c_trace_context=False) async def send_wrapper(response): span = async_tracer.active_span @@ -84,7 +85,8 @@ async def send_wrapper(response): headers = response.get('headers') if headers is not None: - async_tracer.inject(span.context, "{}_trace_context".format(opentracing.Format.BINARY), headers) + async_tracer.inject(span.context, opentracing.Format.BINARY, headers, + disable_w3c_trace_context=False) except Exception: logger.debug("send_wrapper: ", exc_info=True) diff --git a/instana/instrumentation/boto3_inst.py b/instana/instrumentation/boto3_inst.py index 98e30209..b66a267a 100644 --- a/instana/instrumentation/boto3_inst.py +++ b/instana/instrumentation/boto3_inst.py @@ -29,7 +29,7 @@ def lambda_inject_context(payload, scope): if not isinstance(invoke_payload, dict): invoke_payload = json.loads(invoke_payload) - tracer.inject(scope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), invoke_payload) + tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, invoke_payload, disable_w3c_trace_context=False) payload['Payload'] = json.dumps(invoke_payload) except Exception: logger.debug("non-fatal lambda_inject_context: ", exc_info=True) diff --git a/instana/instrumentation/django/middleware.py b/instana/instrumentation/django/middleware.py index c314ee0d..539ef649 100644 --- a/instana/instrumentation/django/middleware.py +++ b/instana/instrumentation/django/middleware.py @@ -33,7 +33,7 @@ def process_request(self, request): try: env = request.environ - ctx = tracer.extract("{}_trace_context".format(ot.Format.HTTP_HEADERS), env) + ctx = tracer.extract(ot.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) request.iscope = tracer.start_active_span('django', child_of=ctx) if agent.options.extra_http_headers is not None: @@ -76,7 +76,8 @@ def process_response(self, request, response): if path_tpl: request.iscope.span.set_tag("http.path_tpl", path_tpl) request.iscope.span.set_tag(ext.HTTP_STATUS_CODE, response.status_code) - tracer.inject(request.iscope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), response) + tracer.inject(request.iscope.span.context, ot.Format.HTTP_HEADERS, response, + disable_w3c_trace_context=False) response['Server-Timing'] = "intid;desc=%s" % request.iscope.span.context.trace_id except Exception: logger.debug("Instana middleware @ process_response", exc_info=True) diff --git a/instana/instrumentation/flask/common.py b/instana/instrumentation/flask/common.py index 52aa10e5..0b5527b9 100644 --- a/instana/instrumentation/flask/common.py +++ b/instana/instrumentation/flask/common.py @@ -63,8 +63,8 @@ def handle_user_exception_with_instana(wrapped, instance, argv, kwargs): span.set_tag(ext.HTTP_STATUS_CODE, int(status_code)) if hasattr(response, 'headers'): - tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - response.headers) + tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers, + disable_w3c_trace_context=False) value = "intid;desc=%s" % scope.span.context.trace_id if hasattr(response.headers, 'add'): response.headers.add('Server-Timing', value) diff --git a/instana/instrumentation/flask/vanilla.py b/instana/instrumentation/flask/vanilla.py index 460d2abd..08b359b1 100644 --- a/instana/instrumentation/flask/vanilla.py +++ b/instana/instrumentation/flask/vanilla.py @@ -22,7 +22,7 @@ def before_request_with_instana(*argv, **kwargs): env = flask.request.environ ctx = None - ctx = tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), env) + ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) flask.g.scope = tracer.start_active_span('wsgi', child_of=ctx) span = flask.g.scope.span @@ -70,8 +70,8 @@ def after_request_with_instana(response): span.mark_as_errored() span.set_tag(ext.HTTP_STATUS_CODE, int(response.status_code)) - tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - response.headers) + tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers, + disable_w3c_trace_context=False) response.headers.add('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id) except: logger.debug("Flask after_request", exc_info=True) diff --git a/instana/instrumentation/flask/with_blinker.py b/instana/instrumentation/flask/with_blinker.py index c91d809d..80efd720 100644 --- a/instana/instrumentation/flask/with_blinker.py +++ b/instana/instrumentation/flask/with_blinker.py @@ -23,7 +23,7 @@ def request_started_with_instana(sender, **extra): env = flask.request.environ ctx = None - ctx = tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), env) + ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) flask.g.scope = tracer.start_active_span('wsgi', child_of=ctx) span = flask.g.scope.span @@ -68,8 +68,8 @@ def request_finished_with_instana(sender, response, **extra): span.mark_as_errored() span.set_tag(ext.HTTP_STATUS_CODE, int(response.status_code)) - tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - response.headers) + tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers, + disable_w3c_trace_context=False) response.headers.add('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id) except: logger.debug("Flask after_request", exc_info=True) diff --git a/instana/instrumentation/pyramid/tweens.py b/instana/instrumentation/pyramid/tweens.py index 09fbaa0b..5c84712b 100644 --- a/instana/instrumentation/pyramid/tweens.py +++ b/instana/instrumentation/pyramid/tweens.py @@ -20,7 +20,7 @@ def __init__(self, handler, registry): self.handler = handler def __call__(self, request): - ctx = tracer.extract("{}_trace_context".format(ot.Format.HTTP_HEADERS), dict(request.headers)) + ctx = tracer.extract(ot.Format.HTTP_HEADERS, dict(request.headers), disable_w3c_trace_context=False) scope = tracer.start_active_span('http', child_of=ctx) scope.span.set_tag(ext.SPAN_KIND, ext.SPAN_KIND_RPC_SERVER) @@ -47,7 +47,7 @@ def __call__(self, request): try: response = self.handler(request) - tracer.inject(scope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), response.headers) + tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, response.headers, disable_w3c_trace_context=False) response.headers['Server-Timing'] = "intid;desc=%s" % scope.span.context.trace_id except HTTPException as e: response = e diff --git a/instana/instrumentation/sanic_inst.py b/instana/instrumentation/sanic_inst.py index f6b3d72b..431eb6b9 100644 --- a/instana/instrumentation/sanic_inst.py +++ b/instana/instrumentation/sanic_inst.py @@ -40,8 +40,8 @@ def response_details(span, response): span.set_tag('http.status_code', status_code) if response.headers is not None: - async_tracer.inject(span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - response.headers) + async_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS, response.headers, + disable_w3c_trace_context=False) response.headers['Server-Timing'] = "intid;desc=%s" % span.context.trace_id except Exception: logger.debug("send_wrapper: ", exc_info=True) @@ -102,7 +102,7 @@ async def handle_request_with_instana(wrapped, instance, args, kwargs): except AttributeError: pass headers = request.headers.copy() - ctx = async_tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), headers) + ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, headers, disable_w3c_trace_context=False) with async_tracer.start_active_span("asgi", child_of=ctx) as scope: scope.span.set_tag('span.kind', 'entry') scope.span.set_tag('http.path', request.path) diff --git a/instana/instrumentation/sudsjurko.py b/instana/instrumentation/sudsjurko.py index c3899c44..2a214694 100644 --- a/instana/instrumentation/sudsjurko.py +++ b/instana/instrumentation/sudsjurko.py @@ -35,8 +35,8 @@ def send_with_instana(wrapped, instance, args, kwargs): scope.span.set_tag(ext.HTTP_URL, instance.method.location) scope.span.set_tag(ext.HTTP_METHOD, 'POST') - active_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - instance.options.headers) + active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance.options.headers, + disable_w3c_trace_context=False) rv = wrapped(*args, **kwargs) diff --git a/instana/instrumentation/tornado/client.py b/instana/instrumentation/tornado/client.py index bea0d64d..12455899 100644 --- a/instana/instrumentation/tornado/client.py +++ b/instana/instrumentation/tornado/client.py @@ -47,8 +47,8 @@ def fetch_with_instana(wrapped, instance, argv, kwargs): kwargs = new_kwargs scope = tornado_tracer.start_active_span('tornado-client', child_of=parent_span) - tornado_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - request.headers) + tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, request.headers, + disable_w3c_trace_context=False) # Query param scrubbing parts = request.url.split('?') diff --git a/instana/instrumentation/tornado/server.py b/instana/instrumentation/tornado/server.py index a676aa0d..0166e7aa 100644 --- a/instana/instrumentation/tornado/server.py +++ b/instana/instrumentation/tornado/server.py @@ -29,8 +29,9 @@ def execute_with_instana(wrapped, instance, argv, kwargs): with tracer_stack_context(): ctx = None if hasattr(instance.request.headers, '__dict__') and '_dict' in instance.request.headers.__dict__: - ctx = tornado_tracer.extract("{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - instance.request.headers.__dict__['_dict']) + ctx = tornado_tracer.extract(opentracing.Format.HTTP_HEADERS, + instance.request.headers.__dict__['_dict'], + disable_w3c_trace_context=False) scope = tornado_tracer.start_active_span('tornado-server', child_of=ctx) # Query param scrubbing @@ -56,8 +57,8 @@ def execute_with_instana(wrapped, instance, argv, kwargs): # Set the context response headers now because tornado doesn't give us a better option to do so # later for this request. - tornado_tracer.inject(scope.span.context, - "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), instance._headers) + tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance._headers, + disable_w3c_trace_context=False) instance.set_header(name='Server-Timing', value="intid;desc=%s" % scope.span.context.trace_id) return wrapped(*argv, **kwargs) @@ -71,8 +72,8 @@ def set_default_headers_with_instana(wrapped, instance, argv, kwargs): return wrapped(*argv, **kwargs) scope = instance.request._instana - tornado_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - instance._headers) + tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance._headers, + disable_w3c_trace_context=False) instance.set_header(name='Server-Timing', value="intid;desc=%s" % scope.span.context.trace_id) diff --git a/instana/instrumentation/urllib3.py b/instana/instrumentation/urllib3.py index 9034f1b2..5e479a3b 100644 --- a/instana/instrumentation/urllib3.py +++ b/instana/instrumentation/urllib3.py @@ -85,8 +85,8 @@ def urlopen_with_instana(wrapped, instance, args, kwargs): scope.span.set_tag(ext.HTTP_METHOD, kvs['method']) if 'headers' in kwargs: - active_tracer.inject(scope.span.context, "{}_trace_context".format(opentracing.Format.HTTP_HEADERS), - kwargs['headers']) + active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, kwargs['headers'], + disable_w3c_trace_context=False) response = wrapped(*args, **kwargs) diff --git a/instana/instrumentation/webapp2_inst.py b/instana/instrumentation/webapp2_inst.py index 319a9fa6..68ac7968 100644 --- a/instana/instrumentation/webapp2_inst.py +++ b/instana/instrumentation/webapp2_inst.py @@ -26,7 +26,7 @@ def new_start_response(status, headers, exc_info=None): """Modified start response with additional headers.""" if 'stan_scope' in env: scope = env['stan_scope'] - tracer.inject(scope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), headers) + tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, headers, disable_w3c_trace_context=False) headers.append(('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id)) res = start_response(status, headers, exc_info) @@ -41,7 +41,7 @@ def new_start_response(status, headers, exc_info=None): else: return start_response(status, headers, exc_info) - ctx = tracer.extract("{}_trace_context".format(ot.Format.HTTP_HEADERS), env) + ctx = tracer.extract(ot.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) scope = env['stan_scope'] = tracer.start_active_span("wsgi", child_of=ctx) if agent.options.extra_http_headers is not None: diff --git a/instana/instrumentation/wsgi.py b/instana/instrumentation/wsgi.py index a2ea5a8c..db46cd62 100644 --- a/instana/instrumentation/wsgi.py +++ b/instana/instrumentation/wsgi.py @@ -22,7 +22,7 @@ def __call__(self, environ, start_response): def new_start_response(status, headers, exc_info=None): """Modified start response with additional headers.""" - tracer.inject(self.scope.span.context, "{}_trace_context".format(ot.Format.HTTP_HEADERS), headers) + tracer.inject(self.scope.span.context, ot.Format.HTTP_HEADERS, headers, disable_w3c_trace_context=False) headers.append(('Server-Timing', "intid;desc=%s" % self.scope.span.context.trace_id)) res = start_response(status, headers, exc_info) @@ -35,7 +35,7 @@ def new_start_response(status, headers, exc_info=None): self.scope.close() return res - ctx = tracer.extract("{}_trace_context".format(ot.Format.HTTP_HEADERS), env) + ctx = tracer.extract(ot.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) self.scope = tracer.start_active_span("wsgi", child_of=ctx) if agent.options.extra_http_headers is not None: diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 99b5d63f..3401150b 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -5,7 +5,12 @@ import sys -from ..log import logger +from instana.log import logger +from instana.util.ids import header_to_id_no_truncation +from instana.span_context import SpanContext +from instana.w3c_trace_context.traceparent import Traceparent +from instana.w3c_trace_context.tracestate import Tracestate +import os PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 @@ -29,6 +34,8 @@ class BasePropagator(object): HEADER_KEY_S = 'X-INSTANA-S' HEADER_KEY_L = 'X-INSTANA-L' HEADER_KEY_SYNTHETIC = 'X-INSTANA-SYNTHETIC' + HEADER_KEY_TRACEPARENT = "traceparent" + HEADER_KEY_TRACESTATE = "tracestate" LC_HEADER_KEY_T = 'x-instana-t' LC_HEADER_KEY_S = 'x-instana-s' @@ -39,13 +46,29 @@ class BasePropagator(object): ALT_LC_HEADER_KEY_S = 'http_x_instana_s' ALT_LC_HEADER_KEY_L = 'http_x_instana_l' ALT_LC_HEADER_KEY_SYNTHETIC = 'http_x_instana_synthetic' - - HEADER_KEY_TRACEPARENT = "traceparent" - HEADER_KEY_TRACESTATE = "tracestate" - ALT_HEADER_KEY_TRACEPARENT = "http_traceparent" ALT_HEADER_KEY_TRACESTATE = "http_tracestate" + # ByteArray variations + B_HEADER_KEY_T = b'x-instana-t' + B_HEADER_KEY_S = b'x-instana-s' + B_HEADER_KEY_L = b'x-instana-l' + B_HEADER_KEY_SYNTHETIC = b'x-instana-synthetic' + B_HEADER_SERVER_TIMING = b'server-timing' + B_HEADER_KEY_TRACEPARENT = b'traceparent' + B_HEADER_KEY_TRACESTATE = b'tracestate' + + B_ALT_LC_HEADER_KEY_T = b'http_x_instana_t' + B_ALT_LC_HEADER_KEY_S = b'http_x_instana_s' + B_ALT_LC_HEADER_KEY_L = b'http_x_instana_l' + B_ALT_LC_HEADER_KEY_SYNTHETIC = b'http_x_instana_synthetic' + B_ALT_HEADER_KEY_TRACEPARENT = b'http_traceparent' + B_ALT_HEADER_KEY_TRACESTATE = b'http_tracestate' + + def __init__(self): + self._tp = Traceparent() + self._ts = Tracestate() + @staticmethod def _extract_headers_dict(carrier): """ @@ -93,3 +116,173 @@ def _set_correlation_properties(level, ctx): ctx.correlation_id = level.split(",")[1].split("correlationId=")[1].split(";")[0] except Exception: logger.debug("extract instana correlation type/id error:", exc_info=True) + + def _get_participating_trace_context(self, span_context): + """ + + :param span_context: + :return: + """ + if span_context.long_trace_id and not span_context.trace_parent: + tp_trace_id = span_context.long_trace_id + else: + tp_trace_id = span_context.trace_id + traceparent = span_context.traceparent + tracestate = span_context.tracestate + traceparent = self._tp.update_traceparent(traceparent, tp_trace_id, span_context.span_id, span_context.level) + tracestate = self._ts.update_tracestate(tracestate, span_context.trace_id, span_context.span_id) + return traceparent, tracestate + + def __determine_span_context(self, trace_id, span_id, level, synthetic, traceparent, tracestate, + disable_w3c_trace_context): + """ + This method determines the span context depending on a set of conditions being met + Detailed description of the conditions can be found here: + https://github.com/instana/technical-documentation/tree/master/tracing/specification#http-processing-for-instana-tracers + :param trace_id: instana trace id + :param span_id: instana span id + :param level: instana level + :param synthetic: instana synthetic + :return: ctx + """ + correlation = False + disable_traceparent = os.environ.get("INSTANA_DISABLE_W3C_TRACE_CORRELATION", "") + instana_ancestor = None + ctx = SpanContext() + if level and "correlationType" in level: + trace_id, span_id = [None] * 2 + correlation = True + + ctx_level = self._get_ctx_level(level) + + if trace_id and span_id: + ctx.trace_id = trace_id[-16:] # only the last 16 chars + ctx.span_id = span_id[-16:] # only the last 16 chars + ctx.level = ctx_level + ctx.synthetic = synthetic is not None + + if len(trace_id) > 16: + ctx.long_trace_id = trace_id + + elif not disable_w3c_trace_context and traceparent and trace_id is None and span_id is None: + _, tp_trace_id, tp_parent_id, _ = self._tp.get_traceparent_fields(traceparent) + + if tracestate and "in=" in tracestate: + instana_ancestor = self._ts.get_instana_ancestor(tracestate) + + if disable_traceparent == "": + ctx.trace_id = tp_trace_id[-16:] + ctx.span_id = tp_parent_id + ctx.level = ctx_level + ctx.synthetic = synthetic is not None + ctx.trace_parent = True + ctx.instana_ancestor = instana_ancestor + ctx.long_trace_id = tp_trace_id + else: + if instana_ancestor: + ctx.trace_id = instana_ancestor.t + ctx.span_id = instana_ancestor.p + ctx.level = ctx_level + ctx.synthetic = synthetic is not None + + elif synthetic: + ctx.synthetic = synthetic + + if correlation: + self._set_correlation_properties(level, ctx) + + if traceparent: + ctx.traceparent = traceparent + ctx.tracestate = tracestate + + return ctx + + def __extract_instana_headers(self, dc): + """ + Search carrier for the *HEADER* keys and return the tracing key-values + + :param dc: The dict or list potentially containing context + :return: trace_id, span_id, level, synthetic, traceparent, tracestate + """ + trace_id, span_id, level, synthetic = [None] * 4 + + # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style + try: + trace_id = dc.get(self.LC_HEADER_KEY_T) or dc.get(self.ALT_LC_HEADER_KEY_T) or dc.get( + self.B_HEADER_KEY_T) or dc.get(self.B_ALT_LC_HEADER_KEY_T) + if trace_id: + trace_id = header_to_id_no_truncation(trace_id) + + span_id = dc.get(self.LC_HEADER_KEY_S) or dc.get(self.ALT_LC_HEADER_KEY_S) or dc.get( + self.B_HEADER_KEY_S) or dc.get(self.B_ALT_LC_HEADER_KEY_S) + if span_id: + span_id = header_to_id_no_truncation(span_id) + + level = dc.get(self.LC_HEADER_KEY_L) or dc.get(self.ALT_LC_HEADER_KEY_L) or dc.get( + self.B_HEADER_KEY_L) or dc.get(self.B_ALT_LC_HEADER_KEY_L) + if level and PY3 is True and isinstance(level, bytes): + level = level.decode("utf-8") + + synthetic = dc.get(self.LC_HEADER_KEY_SYNTHETIC) or dc.get(self.ALT_LC_HEADER_KEY_SYNTHETIC) or dc.get( + self.B_HEADER_KEY_SYNTHETIC) or dc.get(self.B_ALT_LC_HEADER_KEY_SYNTHETIC) + if synthetic: + synthetic = synthetic in ['1', b'1'] + + except Exception: + logger.debug("extract error:", exc_info=True) + + return trace_id, span_id, level, synthetic + + def __extract_w3c_trace_context_headers(self, dc): + """ + Search carrier for the *HEADER* keys and return the tracing key-values + + :param dc: The dict or list potentially containing context + :return: traceparent, tracestate + """ + traceparent, tracestate = [None] * 2 + + try: + traceparent = dc.get(self.HEADER_KEY_TRACEPARENT) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT) or dc.get( + self.B_HEADER_KEY_TRACEPARENT) or dc.get(self.B_ALT_HEADER_KEY_TRACEPARENT) + if traceparent and PY3 is True and isinstance(traceparent, bytes): + traceparent = traceparent.decode("utf-8") + + tracestate = dc.get(self.HEADER_KEY_TRACESTATE) or dc.get(self.ALT_HEADER_KEY_TRACESTATE) or dc.get( + self.B_HEADER_KEY_TRACESTATE) or dc.get(self.B_ALT_HEADER_KEY_TRACESTATE) + if tracestate and PY3 is True and isinstance(tracestate, bytes): + tracestate = tracestate.decode("utf-8") + + except Exception: + logger.debug("extract error:", exc_info=True) + + return traceparent, tracestate + + def extract(self, carrier, disable_w3c_trace_context=False): + """ + This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP + requests more extracting steps and logic was required + :param disable_w3c_trace_context: + :param carrier: + :return: the context or None + """ + try: + traceparent, tracestate = [None] * 2 + headers = self._extract_headers_dict(carrier=carrier) + if headers is None: + return None + headers = {k.lower(): v for k, v in headers.items()} + + trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) + if not disable_w3c_trace_context: + traceparent, tracestate = self.__extract_w3c_trace_context_headers(dc=headers) + + if traceparent: + traceparent = self._tp.validate(traceparent) + + ctx = self.__determine_span_context(trace_id, span_id, level, synthetic, traceparent, tracestate, + disable_w3c_trace_context) + + return ctx + except Exception: + logger.debug("extract error:", exc_info=True) \ No newline at end of file diff --git a/instana/propagators/binary_propagator.py b/instana/propagators/binary_propagator.py index 996cd928..cf3d7926 100644 --- a/instana/propagators/binary_propagator.py +++ b/instana/propagators/binary_propagator.py @@ -3,11 +3,11 @@ from __future__ import absolute_import -from ..log import logger -from .http_propagator import HTTPPropagator +from instana.log import logger +from instana.propagators.base_propagator import BasePropagator -class BinaryPropagator(HTTPPropagator): +class BinaryPropagator(BasePropagator): """ A Propagator for BINARY. The BINARY format represents SpanContexts in an opaque bytearray carrier. @@ -18,33 +18,57 @@ class BinaryPropagator(HTTPPropagator): HEADER_KEY_S = b'x-instana-s' HEADER_KEY_L = b'x-instana-l' HEADER_SERVER_TIMING = b'server-timing' + HEADER_KEY_TRACEPARENT = b'traceparent' + HEADER_KEY_TRACESTATE = b'tracestate' def __init__(self): super(BinaryPropagator, self).__init__() - def inject(self, span_context, carrier): + def inject(self, span_context, carrier, disable_w3c_trace_context=True): try: trace_id = str.encode(span_context.trace_id) span_id = str.encode(span_context.span_id) level = str.encode(str(span_context.level)) server_timing = str.encode("intid;desc=%s" % span_context.trace_id) + if disable_w3c_trace_context: + traceparent, tracestate = [None] * 2 + else: + traceparent, tracestate = self._get_participating_trace_context(span_context) + try: + traceparent = str.encode(traceparent) + tracestate = str.encode(tracestate) + except Exception: + traceparent, tracestate = [None] * 2 + if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): + if traceparent and tracestate: + carrier[self.HEADER_KEY_TRACEPARENT] = traceparent + carrier[self.HEADER_KEY_TRACESTATE] = tracestate carrier[self.HEADER_KEY_T] = trace_id carrier[self.HEADER_KEY_S] = span_id carrier[self.HEADER_KEY_L] = level carrier[self.HEADER_SERVER_TIMING] = server_timing elif isinstance(carrier, list): + if traceparent and tracestate: + carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) + carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) carrier.append((self.HEADER_KEY_T, trace_id)) carrier.append((self.HEADER_KEY_S, span_id)) carrier.append((self.HEADER_KEY_L, level)) carrier.append((self.HEADER_SERVER_TIMING, server_timing)) elif isinstance(carrier, tuple): + if traceparent and tracestate: + carrier = carrier.__add__(((self.HEADER_KEY_TRACEPARENT, traceparent),)) + carrier = carrier.__add__(((self.HEADER_KEY_TRACESTATE, tracestate),)) carrier = carrier.__add__(((self.HEADER_KEY_T, trace_id),)) carrier = carrier.__add__(((self.HEADER_KEY_S, span_id),)) carrier = carrier.__add__(((self.HEADER_KEY_L, level),)) carrier = carrier.__add__(((self.HEADER_SERVER_TIMING, server_timing),)) elif hasattr(carrier, '__setitem__'): + if traceparent and tracestate: + carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) + carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) carrier.__setitem__(self.HEADER_KEY_T, trace_id) carrier.__setitem__(self.HEADER_KEY_S, span_id) carrier.__setitem__(self.HEADER_KEY_L, level) diff --git a/instana/propagators/binary_propagator_tc.py b/instana/propagators/binary_propagator_tc.py deleted file mode 100644 index 50e2ce9d..00000000 --- a/instana/propagators/binary_propagator_tc.py +++ /dev/null @@ -1,90 +0,0 @@ -# (c) Copyright IBM Corp. 2021 -# (c) Copyright Instana Inc. 2020 - -from __future__ import absolute_import - -from ..log import logger -from .http_propagator_tc import HTTPPropagatorTC -from ..w3c_trace_context.traceparent import Traceparent -from ..w3c_trace_context.tracestate import Tracestate - - -class BinaryPropagatorTC(HTTPPropagatorTC): - """ - A Propagator for BINARY. - The BINARY format represents SpanContexts in an opaque bytearray carrier. - """ - - # ByteArray variations from base class - HEADER_KEY_T = b'x-instana-t' - HEADER_KEY_S = b'x-instana-s' - HEADER_KEY_L = b'x-instana-l' - HEADER_SERVER_TIMING = b'server-timing' - HEADER_KEY_TRACEPARENT = b'traceparent' - HEADER_KEY_TRACESTATE = b'tracestate' - - def __init__(self): - self.__tp = Traceparent() - self.__ts = Tracestate() - super(BinaryPropagatorTC, self).__init__() - - def inject(self, span_context, carrier, binary_w3c_injection=False): - try: - trace_id = str.encode(span_context.trace_id) - span_id = str.encode(span_context.span_id) - level = str.encode(str(span_context.level)) - server_timing = str.encode("intid;desc=%s" % span_context.trace_id) - if binary_w3c_injection: - traceparent = span_context.traceparent - tracestate = span_context.tracestate - traceparent = self.__tp.update_traceparent(traceparent, span_context.trace_id, span_context.span_id, - span_context.level) - tracestate = self.__ts.update_tracestate(tracestate, span_context.trace_id, span_context.span_id) - try: - traceparent = str.encode(traceparent) - tracestate = str.encode(tracestate) - except Exception: - traceparent, tracestate = [None] * 2 - else: - traceparent, tracestate = [None] * 2 - - if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): - if traceparent and tracestate: - carrier[self.HEADER_KEY_TRACEPARENT] = traceparent - carrier[self.HEADER_KEY_TRACESTATE] = tracestate - carrier[self.HEADER_KEY_T] = trace_id - carrier[self.HEADER_KEY_S] = span_id - carrier[self.HEADER_KEY_L] = level - carrier[self.HEADER_SERVER_TIMING] = server_timing - elif isinstance(carrier, list): - if traceparent and tracestate: - carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) - carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) - carrier.append((self.HEADER_KEY_T, trace_id)) - carrier.append((self.HEADER_KEY_S, span_id)) - carrier.append((self.HEADER_KEY_L, level)) - carrier.append((self.HEADER_SERVER_TIMING, server_timing)) - elif isinstance(carrier, tuple): - if traceparent and tracestate: - carrier = carrier.__add__(((self.HEADER_KEY_TRACEPARENT, traceparent),)) - carrier = carrier.__add__(((self.HEADER_KEY_TRACESTATE, tracestate),)) - carrier = carrier.__add__(((self.HEADER_KEY_T, trace_id),)) - carrier = carrier.__add__(((self.HEADER_KEY_S, span_id),)) - carrier = carrier.__add__(((self.HEADER_KEY_L, level),)) - carrier = carrier.__add__(((self.HEADER_SERVER_TIMING, server_timing),)) - elif hasattr(carrier, '__setitem__'): - if traceparent and tracestate: - carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) - carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) - carrier.__setitem__(self.HEADER_KEY_T, trace_id) - carrier.__setitem__(self.HEADER_KEY_S, span_id) - carrier.__setitem__(self.HEADER_KEY_L, level) - carrier.__setitem__(self.HEADER_SERVER_TIMING, server_timing) - else: - raise Exception("Unsupported carrier type", type(carrier)) - - return carrier - except Exception: - logger.debug("inject error:", exc_info=True) - - diff --git a/instana/propagators/http_propagator.py b/instana/propagators/http_propagator.py index f9cbe243..4e2b22cf 100644 --- a/instana/propagators/http_propagator.py +++ b/instana/propagators/http_propagator.py @@ -3,14 +3,8 @@ from __future__ import absolute_import -import sys -from ..log import logger -from .base_propagator import BasePropagator -from ..util.ids import header_to_id -from ..span_context import SpanContext - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 +from instana.log import logger +from instana.propagators.base_propagator import BasePropagator class HTTPPropagator(BasePropagator): @@ -24,120 +18,50 @@ class HTTPPropagator(BasePropagator): def __init__(self): super(HTTPPropagator, self).__init__() - def inject(self, span_context, carrier): + def inject(self, span_context, carrier, disable_w3c_trace_context=False): try: trace_id = span_context.trace_id span_id = span_context.span_id level = span_context.level + if disable_w3c_trace_context: + traceparent, tracestate = [None] * 2 + else: + traceparent, tracestate = self._get_participating_trace_context(span_context) + if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): + if traceparent and tracestate: + carrier[self.HEADER_KEY_TRACEPARENT] = traceparent + carrier[self.HEADER_KEY_TRACESTATE] = tracestate carrier[self.HEADER_KEY_T] = trace_id carrier[self.HEADER_KEY_S] = span_id - carrier[self.HEADER_KEY_L] = str(level) + carrier[self.HEADER_KEY_L] = "1" elif isinstance(carrier, list): + if traceparent and tracestate: + carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) + carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) carrier.append((self.HEADER_KEY_T, trace_id)) carrier.append((self.HEADER_KEY_S, span_id)) - carrier.append((self.HEADER_KEY_L, str(level))) + carrier.append((self.HEADER_KEY_L, "1")) elif hasattr(carrier, '__setitem__'): + if traceparent and tracestate: + carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) + carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) carrier.__setitem__(self.HEADER_KEY_T, trace_id) carrier.__setitem__(self.HEADER_KEY_S, span_id) - carrier.__setitem__(self.HEADER_KEY_L, str(level)) + carrier.__setitem__(self.HEADER_KEY_L, "1") else: raise Exception("Unsupported carrier type", type(carrier)) except Exception: logger.debug("inject error:", exc_info=True) - def extract(self, carrier): - """ - This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP - requests more extracting steps and logic was required - :param carrier: - :return: the context or None - """ - try: - headers = self._extract_headers_dict(carrier=carrier) - if headers is None: - return None - headers = {k.lower(): v for k, v in headers.items()} - trace_id, span_id, level, synthetic = self.__extract_instana_headers(dc=headers) - ctx = self.__determine_span_context(trace_id, span_id, level, synthetic) - return ctx - except Exception: - logger.debug("extract error:", exc_info=True) - def __extract_instana_headers(self, dc): - """ - Search carrier for the *HEADER* keys and return the tracing key-values - :param dc: The dict or list potentially containing context - :return: trace_id, span_id, level, synthetic, traceparent, tracestate - """ - trace_id, span_id, level, synthetic, traceparent, tracestate = [None] * 6 - # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style - try: - trace_id = dc.get(self.LC_HEADER_KEY_T) or dc.get(self.ALT_LC_HEADER_KEY_T) or dc.get( - self.LC_HEADER_KEY_T.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_T.encode("utf-8")) - if trace_id: - trace_id = header_to_id(trace_id) - - span_id = dc.get(self.LC_HEADER_KEY_S) or dc.get(self.ALT_LC_HEADER_KEY_S) or dc.get( - self.LC_HEADER_KEY_S.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_S.encode("utf-8")) - if span_id: - span_id = header_to_id(span_id) - - level = dc.get(self.LC_HEADER_KEY_L) or dc.get(self.ALT_LC_HEADER_KEY_L) or dc.get( - self.LC_HEADER_KEY_L.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_L.encode("utf-8")) - if level and PY3 is True and isinstance(level, bytes): - level = level.decode("utf-8") - - synthetic = dc.get(self.LC_HEADER_KEY_SYNTHETIC) or dc.get(self.ALT_LC_HEADER_KEY_SYNTHETIC) or dc.get( - self.LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) or dc.get( - self.ALT_LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) - if synthetic: - synthetic = synthetic in ['1', b'1'] - except Exception: - logger.debug("extract error:", exc_info=True) - - return trace_id, span_id, level, synthetic - - def __determine_span_context(self, trace_id, span_id, level, synthetic): - """ - This method determines the span context - :param **kwargs: - :param **kwargs: - :param trace_id: instana trace id - :param span_id: instana span id - :param level: instana level - :param synthetic: instana synthetic - :return: ctx - """ - correlation = False - ctx = SpanContext() - if level and "correlationType" in level: - trace_id, span_id = [None] * 2 - correlation = True - - ctx_level = self._get_ctx_level(level) - - if trace_id and span_id: - ctx.trace_id = trace_id[-16:] # only the last 16 chars - ctx.span_id = span_id[-16:] # only the last 16 chars - ctx.level = ctx_level - ctx.synthetic = synthetic is not None - - if len(trace_id) > 16: - ctx.long_trace_id = trace_id - - elif synthetic: - ctx.synthetic = synthetic - - if correlation: - self._set_correlation_properties(level, ctx) - - return ctx + + diff --git a/instana/propagators/http_propagator_tc.py b/instana/propagators/http_propagator_tc.py deleted file mode 100644 index 076c441e..00000000 --- a/instana/propagators/http_propagator_tc.py +++ /dev/null @@ -1,204 +0,0 @@ -# (c) Copyright IBM Corp. 2021 -# (c) Copyright Instana Inc. 2020 - -from __future__ import absolute_import - -import sys -from ..log import logger -from .base_propagator import BasePropagator -from ..util.ids import header_to_id -from ..span_context import SpanContext -from ..w3c_trace_context.traceparent import Traceparent -from ..w3c_trace_context.tracestate import Tracestate -import os -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - - -class HTTPPropagatorTC(BasePropagator): - """ - Instana Propagator for Format.HTTP_HEADERS. - - The HTTP_HEADERS format deals with key-values with string to string mapping. - The character set should be restricted to HTTP compatible. - """ - - def __init__(self): - self._tp = Traceparent() - self._ts = Tracestate() - super(HTTPPropagatorTC, self).__init__() - - def inject(self, span_context, carrier): - try: - trace_id = span_context.trace_id - span_id = span_context.span_id - level = span_context.level - if span_context.long_trace_id and not span_context.trace_parent: - tp_trace_id = span_context.long_trace_id - else: - tp_trace_id = trace_id - traceparent = span_context.traceparent - tracestate = span_context.tracestate - traceparent = self._tp.update_traceparent(traceparent, tp_trace_id, span_id, level) - tracestate = self._ts.update_tracestate(tracestate, trace_id, span_id) - - if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): - if traceparent and tracestate: - carrier[self.HEADER_KEY_TRACEPARENT] = traceparent - carrier[self.HEADER_KEY_TRACESTATE] = tracestate - carrier[self.HEADER_KEY_T] = trace_id - carrier[self.HEADER_KEY_S] = span_id - carrier[self.HEADER_KEY_L] = "1" - elif isinstance(carrier, list): - if traceparent and tracestate: - carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) - carrier.append((self.HEADER_KEY_TRACESTATE, tracestate)) - carrier.append((self.HEADER_KEY_T, trace_id)) - carrier.append((self.HEADER_KEY_S, span_id)) - carrier.append((self.HEADER_KEY_L, "1")) - elif hasattr(carrier, '__setitem__'): - if traceparent and tracestate: - carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) - carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) - carrier.__setitem__(self.HEADER_KEY_T, trace_id) - carrier.__setitem__(self.HEADER_KEY_S, span_id) - carrier.__setitem__(self.HEADER_KEY_L, "1") - else: - raise Exception("Unsupported carrier type", type(carrier)) - - except Exception: - logger.debug("inject error:", exc_info=True) - - def extract(self, carrier): - """ - This method overrides the one of the Baseclass as with the introduction of W3C trace context for the HTTP - requests more extracting steps and logic was required - :param carrier: - :return: the context or None - """ - try: - headers = self._extract_headers_dict(carrier=carrier) - if headers is None: - return None - headers = {k.lower(): v for k, v in headers.items()} - - trace_id, span_id, level, synthetic, traceparent, tracestate = self.__extract_instana_headers(dc=headers) - - if traceparent: - traceparent = self._tp.validate(traceparent) - - ctx = self.__determine_span_context(trace_id, span_id, level, synthetic, traceparent, tracestate) - - return ctx - except Exception: - logger.debug("extract error:", exc_info=True) - - def __determine_span_context(self, trace_id, span_id, level, synthetic, traceparent, tracestate): - """ - This method determines the span context depending on a set of conditions being met - Detailed description of the conditions can be found here: - https://github.com/instana/technical-documentation/tree/master/tracing/specification#http-processing-for-instana-tracers - :param trace_id: instana trace id - :param span_id: instana span id - :param level: instana level - :param synthetic: instana synthetic - :return: ctx - """ - correlation = False - disable_traceparent = os.environ.get("INSTANA_DISABLE_W3C_TRACE_CORRELATION", "") - instana_ancestor = None - ctx = SpanContext() - if level and "correlationType" in level: - trace_id, span_id = [None] * 2 - correlation = True - - ctx_level = self._get_ctx_level(level) - - if trace_id and span_id: - ctx.trace_id = trace_id[-16:] # only the last 16 chars - ctx.span_id = span_id[-16:] # only the last 16 chars - ctx.level = ctx_level - ctx.synthetic = synthetic is not None - - if len(trace_id) > 16: - ctx.long_trace_id = trace_id - - elif traceparent and trace_id is None and span_id is None: - _, tp_trace_id, tp_parent_id, _ = self._tp.get_traceparent_fields(traceparent) - - if tracestate and "in=" in tracestate: - instana_ancestor = self._ts.get_instana_ancestor(tracestate) - - if disable_traceparent == "": - ctx.trace_id = tp_trace_id[-16:] - ctx.span_id = tp_parent_id - ctx.level = ctx_level - ctx.synthetic = synthetic is not None - ctx.trace_parent = True - ctx.instana_ancestor = instana_ancestor - ctx.long_trace_id = tp_trace_id - else: - if instana_ancestor: - ctx.trace_id = instana_ancestor.t - ctx.span_id = instana_ancestor.p - ctx.level = ctx_level - ctx.synthetic = synthetic is not None - - elif synthetic: - ctx.synthetic = synthetic - - if correlation: - self._set_correlation_properties(level, ctx) - - if traceparent: - ctx.traceparent = traceparent - ctx.tracestate = tracestate - - return ctx - - def __extract_instana_headers(self, dc): - """ - Search carrier for the *HEADER* keys and return the tracing key-values - - :param dc: The dict or list potentially containing context - :return: trace_id, span_id, level, synthetic, traceparent, tracestate - """ - trace_id, span_id, level, synthetic, traceparent, tracestate = [None] * 6 - - # Headers can exist in the standard X-Instana-T/S format or the alternate HTTP_X_INSTANA_T/S style - try: - trace_id = dc.get(self.LC_HEADER_KEY_T) or dc.get(self.ALT_LC_HEADER_KEY_T) or dc.get( - self.LC_HEADER_KEY_T.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_T.encode("utf-8")) - if trace_id: - trace_id = header_to_id(trace_id) - - span_id = dc.get(self.LC_HEADER_KEY_S) or dc.get(self.ALT_LC_HEADER_KEY_S) or dc.get( - self.LC_HEADER_KEY_S.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_S.encode("utf-8")) - if span_id: - span_id = header_to_id(span_id) - - level = dc.get(self.LC_HEADER_KEY_L) or dc.get(self.ALT_LC_HEADER_KEY_L) or dc.get( - self.LC_HEADER_KEY_L.encode("utf-8")) or dc.get(self.ALT_LC_HEADER_KEY_L.encode("utf-8")) - if level and PY3 is True and isinstance(level, bytes): - level = level.decode("utf-8") - - synthetic = dc.get(self.LC_HEADER_KEY_SYNTHETIC) or dc.get(self.ALT_LC_HEADER_KEY_SYNTHETIC) or dc.get( - self.LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) or dc.get( - self.ALT_LC_HEADER_KEY_SYNTHETIC.encode("utf-8")) - if synthetic: - synthetic = synthetic in ['1', b'1'] - - traceparent = dc.get(self.HEADER_KEY_TRACEPARENT) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT) or dc.get( - self.HEADER_KEY_TRACEPARENT) or dc.get(self.ALT_HEADER_KEY_TRACEPARENT.encode("utf-8")) - if traceparent and PY3 is True and isinstance(traceparent, bytes): - traceparent = traceparent.decode("utf-8") - - tracestate = dc.get(self.HEADER_KEY_TRACESTATE) or dc.get(self.ALT_HEADER_KEY_TRACESTATE) or dc.get( - self.HEADER_KEY_TRACESTATE) or dc.get(self.ALT_HEADER_KEY_TRACESTATE.encode("utf-8")) - if tracestate and PY3 is True and isinstance(tracestate, bytes): - tracestate = tracestate.decode("utf-8") - - except Exception: - logger.debug("extract error:", exc_info=True) - - return trace_id, span_id, level, synthetic, traceparent, tracestate diff --git a/instana/propagators/text_propagator.py b/instana/propagators/text_propagator.py index d780a61f..02af14a5 100644 --- a/instana/propagators/text_propagator.py +++ b/instana/propagators/text_propagator.py @@ -3,11 +3,11 @@ from __future__ import absolute_import -from ..log import logger -from .http_propagator import HTTPPropagator +from instana.log import logger +from instana.propagators.base_propagator import BasePropagator -class TextPropagator(HTTPPropagator): +class TextPropagator(BasePropagator): """ Instana context propagator for TEXT_MAP. @@ -15,7 +15,7 @@ class TextPropagator(HTTPPropagator): The character set is unrestricted. """ - def inject(self, span_context, carrier): + def inject(self, span_context, carrier, disable_w3c_trace_context=True): try: trace_id = span_context.trace_id span_id = span_context.span_id diff --git a/instana/tracer.py b/instana/tracer.py index 636b60d4..93f4216b 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -16,10 +16,8 @@ from .span import InstanaSpan, RegisteredSpan from .recorder import StanRecorder, InstanaSampler from .propagators.http_propagator import HTTPPropagator -from .propagators.http_propagator_tc import HTTPPropagatorTC from .propagators.text_propagator import TextPropagator from .propagators.binary_propagator import BinaryPropagator -from .propagators.binary_propagator_tc import BinaryPropagatorTC class InstanaTracer(BasicTracer): @@ -32,10 +30,8 @@ def __init__(self, scope_manager=None, recorder=None): recorder, InstanaSampler(), scope_manager) self._propagators[ot.Format.HTTP_HEADERS] = HTTPPropagator() - self._propagators["{}_trace_context".format(ot.Format.HTTP_HEADERS)] = HTTPPropagatorTC() self._propagators[ot.Format.TEXT_MAP] = TextPropagator() self._propagators[ot.Format.BINARY] = BinaryPropagator() - self._propagators["{}_trace_context".format(ot.Format.BINARY)] = BinaryPropagatorTC() def start_active_span(self, operation_name, @@ -125,15 +121,15 @@ def start_span(self, return span - def inject(self, span_context, format, carrier): + def inject(self, span_context, format, carrier, disable_w3c_trace_context=True): if format in self._propagators: - return self._propagators[format].inject(span_context, carrier) + return self._propagators[format].inject(span_context, carrier, disable_w3c_trace_context) raise ot.UnsupportedFormatException() - def extract(self, format, carrier): + def extract(self, format, carrier, disable_w3c_trace_context=True): if format in self._propagators: - return self._propagators[format].extract(carrier) + return self._propagators[format].extract(carrier, disable_w3c_trace_context) raise ot.UnsupportedFormatException() diff --git a/instana/util/ids.py b/instana/util/ids.py index 59828c06..912e5adb 100644 --- a/instana/util/ids.py +++ b/instana/util/ids.py @@ -19,6 +19,7 @@ else: string_types = str + def generate_id(): """ Generate a 64bit base 16 ID for use as a Span or Trace ID """ global _current_pid @@ -35,7 +36,7 @@ def generate_id(): return new_id -def header_to_id(header): +def header_to_id_no_truncation(header): """ We can receive headers in the following formats: 1. unsigned base 16 hex string (or bytes) of variable length @@ -62,3 +63,34 @@ def header_to_id(header): return header except ValueError: return BAD_ID + + +def header_to_id(header): + """ + We can receive headers in the following formats: + 1. unsigned base 16 hex string (or bytes) of variable length + 2. [eventual] + + :param header: the header to analyze, validate and convert (if needed) + :return: a valid ID to be used internal to the tracer + """ + if PY3 is True and isinstance(header, bytes): + header = header.decode('utf-8') + + if not isinstance(header, string_types): + return BAD_ID + + try: + # Test that header is truly a hexadecimal value before we try to convert + int(header, 16) + + length = len(header) + if length < 16: + # Left pad ID with zeros + header = header.zfill(16) + elif length > 16: + # Phase 0: Discard everything but the last 16byte + header = header[-16:] + return header + except ValueError: + return BAD_ID diff --git a/instana/version.py b/instana/version.py index dfc3f8e8..19afcaaf 100644 --- a/instana/version.py +++ b/instana/version.py @@ -3,4 +3,4 @@ # Module version file. Used by setup.py and snapshot reporting. -VERSION = '1.35.0.dev0' +VERSION = '1.35.0' diff --git a/instana/w3c_trace_context/traceparent.py b/instana/w3c_trace_context/traceparent.py index b2194889..a3876a9c 100644 --- a/instana/w3c_trace_context/traceparent.py +++ b/instana/w3c_trace_context/traceparent.py @@ -19,7 +19,7 @@ def validate(self, traceparent): if self.TRACEPARENT_REGEX.match(traceparent): return traceparent except Exception: - logger.debug("traceparent does not follow version 00 specification") + logger.debug("traceparent does not follow version {} specification".format(self.SPECIFICATION_VERSION)) return None @staticmethod diff --git a/tests/propagators/test_binary_propagator.py b/tests/propagators/test_binary_propagator.py index 089c79be..d96b97a3 100644 --- a/tests/propagators/test_binary_propagator.py +++ b/tests/propagators/test_binary_propagator.py @@ -1,14 +1,14 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 -from instana.propagators.binary_propagator_tc import BinaryPropagatorTC +from instana.propagators.binary_propagator import BinaryPropagator from instana.span_context import SpanContext import unittest class TestBinaryPropagator(unittest.TestCase): def setUp(self): - self.bp = BinaryPropagatorTC() + self.bp = BinaryPropagator() def test_inject_carrier_dict(self): carrier = {} @@ -23,7 +23,7 @@ def test_inject_carrier_dict_w3c_True(self): ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", level=1, baggage={}, sampled=True, synthetic=False) - carrier = self.bp.inject(ctx, carrier, binary_w3c_injection=True) + carrier = self.bp.inject(ctx, carrier, disable_w3c_trace_context=False) self.assertEqual(carrier[b'x-instana-t'], b"1234d0e0e4736234") self.assertEqual(carrier[b'traceparent'], b'00-00000000000000001234d0e0e4736234-1234567890abcdef-01') self.assertEqual(carrier[b'tracestate'], b'in=1234d0e0e4736234;1234567890abcdef') @@ -41,7 +41,7 @@ def test_inject_carrier_list_w3c_True(self): ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", level=1, baggage={}, sampled=True, synthetic=False) - carrier = self.bp.inject(ctx, carrier, binary_w3c_injection=True) + carrier = self.bp.inject(ctx, carrier, disable_w3c_trace_context=False) self.assertEqual(carrier[2], (b'x-instana-t', b'1234d0e0e4736234')) self.assertEqual(carrier[0], (b'traceparent', b'00-00000000000000001234d0e0e4736234-1234567890abcdef-01')) self.assertEqual(carrier[1], (b'tracestate', b'in=1234d0e0e4736234;1234567890abcdef')) @@ -59,7 +59,7 @@ def test_inject_carrier_tupple_w3c_True(self): ctx = SpanContext(span_id="1234567890abcdef", trace_id="1234d0e0e4736234", level=1, baggage={}, sampled=True, synthetic=False) - carrier = self.bp.inject(ctx, carrier, binary_w3c_injection=True) + carrier = self.bp.inject(ctx, carrier, disable_w3c_trace_context=False) self.assertEqual(carrier[2], (b'x-instana-t', b'1234d0e0e4736234')) self.assertEqual(carrier[0], (b'traceparent', b'00-00000000000000001234d0e0e4736234-1234567890abcdef-01')) self.assertEqual(carrier[1], (b'tracestate', b'in=1234d0e0e4736234;1234567890abcdef')) diff --git a/tests/propagators/test_http_propagator_tc.py b/tests/propagators/test_http_propagator.py similarity index 98% rename from tests/propagators/test_http_propagator_tc.py rename to tests/propagators/test_http_propagator.py index f83ace5d..159c971a 100644 --- a/tests/propagators/test_http_propagator_tc.py +++ b/tests/propagators/test_http_propagator.py @@ -1,7 +1,7 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2021 -from instana.propagators.http_propagator_tc import HTTPPropagatorTC +from instana.propagators.http_propagator import HTTPPropagator from instana.w3c_trace_context.traceparent import Traceparent from instana.span_context import SpanContext from mock import patch @@ -10,7 +10,7 @@ class TestHTTPPropagatorTC(unittest.TestCase): def setUp(self): - self.hptc = HTTPPropagatorTC() + self.hptc = HTTPPropagator() @patch.object(Traceparent, "get_traceparent_fields") @patch.object(Traceparent, "validate") diff --git a/tests/test_id_management.py b/tests/test_id_management.py index a2563fcc..20c671fc 100644 --- a/tests/test_id_management.py +++ b/tests/test_id_management.py @@ -24,38 +24,38 @@ def test_id_generation(): def test_various_header_to_id_conversion(): # Get a hex string to test against & convert header_id = instana.util.ids.generate_id() - converted_id = instana.util.ids.header_to_id(header_id) + converted_id = instana.util.ids.header_to_id_no_truncation(header_id) assert(header_id == converted_id) # Hex value - result should be left padded - result = instana.util.ids.header_to_id('abcdef') + result = instana.util.ids.header_to_id_no_truncation('abcdef') assert('0000000000abcdef' == result) # Hex value - result = instana.util.ids.header_to_id('0123456789abcdef') + result = instana.util.ids.header_to_id_no_truncation('0123456789abcdef') assert('0123456789abcdef' == result) # Very long incoming header should just return the rightmost 16 bytes - result = instana.util.ids.header_to_id('0x0123456789abcdef0123456789abcdef') + result = instana.util.ids.header_to_id_no_truncation('0x0123456789abcdef0123456789abcdef') assert('0x0123456789abcdef0123456789abcdef' == result) def test_header_to_id_conversion_with_bogus_header(): # Bogus nil arg - bogus_result = instana.util.ids.header_to_id(None) + bogus_result = instana.util.ids.header_to_id_no_truncation(None) assert(instana.util.ids.BAD_ID == bogus_result) # Bogus Integer arg - bogus_result = instana.util.ids.header_to_id(1234) + bogus_result = instana.util.ids.header_to_id_no_truncation(1234) assert(instana.util.ids.BAD_ID == bogus_result) # Bogus Array arg - bogus_result = instana.util.ids.header_to_id([1234]) + bogus_result = instana.util.ids.header_to_id_no_truncation([1234]) assert(instana.util.ids.BAD_ID == bogus_result) # Bogus Hex Values in String - bogus_result = instana.util.ids.header_to_id('0xZZZZZZ') + bogus_result = instana.util.ids.header_to_id_no_truncation('0xZZZZZZ') assert(instana.util.ids.BAD_ID == bogus_result) - bogus_result = instana.util.ids.header_to_id('ZZZZZZ') + bogus_result = instana.util.ids.header_to_id_no_truncation('ZZZZZZ') assert(instana.util.ids.BAD_ID == bogus_result) From 739f908160140933ba8fba5abb897473ec38c5f7 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 1 Sep 2021 13:56:59 +0200 Subject: [PATCH 38/39] apply latest review changes --- instana/instrumentation/asynqp.py | 22 +++++---- instana/instrumentation/aws/triggers.py | 4 +- instana/instrumentation/celery/hooks.py | 13 +++++- .../instrumentation/google/cloud/pubsub.py | 4 +- instana/instrumentation/grpcio.py | 46 +++++++++++++------ instana/instrumentation/pika.py | 12 +++-- instana/propagators/base_propagator.py | 15 +++--- instana/tracer.py | 4 +- instana/util/ids.py | 2 +- tests/test_id_management.py | 18 ++++---- 10 files changed, 90 insertions(+), 50 deletions(-) diff --git a/instana/instrumentation/asynqp.py b/instana/instrumentation/asynqp.py index c3b40f1f..9e5c67e1 100644 --- a/instana/instrumentation/asynqp.py +++ b/instana/instrumentation/asynqp.py @@ -13,7 +13,8 @@ import asynqp import asyncio - @wrapt.patch_function_wrapper('asynqp.exchange','Exchange.publish') + + @wrapt.patch_function_wrapper('asynqp.exchange', 'Exchange.publish') def publish_with_instana(wrapped, instance, argv, kwargs): parent_span = async_tracer.active_span @@ -27,12 +28,13 @@ def publish_with_instana(wrapped, instance, argv, kwargs): msg = argv[0] if msg.headers is None: msg.headers = {} - async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, msg.headers) + async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, msg.headers, + disable_w3c_trace_context=True) try: scope.span.set_tag("exchange", instance.name) scope.span.set_tag("sort", "publish") - scope.span.set_tag("address", host + ":" + str(port) ) + scope.span.set_tag("address", host + ":" + str(port)) if 'routing_key' in kwargs: scope.span.set_tag("key", kwargs['routing_key']) @@ -46,8 +48,9 @@ def publish_with_instana(wrapped, instance, argv, kwargs): else: return rv + @asyncio.coroutine - @wrapt.patch_function_wrapper('asynqp.queue','Queue.get') + @wrapt.patch_function_wrapper('asynqp.queue', 'Queue.get') def get_with_instana(wrapped, instance, argv, kwargs): parent_span = async_tracer.active_span @@ -59,7 +62,7 @@ def get_with_instana(wrapped, instance, argv, kwargs): host, port = instance.sender.protocol.transport._sock.getsockname() scope.span.set_tag("sort", "consume") - scope.span.set_tag("address", host + ":" + str(port) ) + scope.span.set_tag("address", host + ":" + str(port)) msg = yield from wrapped(*argv, **kwargs) @@ -69,15 +72,17 @@ def get_with_instana(wrapped, instance, argv, kwargs): return msg + @asyncio.coroutine - @wrapt.patch_function_wrapper('asynqp.queue','Queue.consume') + @wrapt.patch_function_wrapper('asynqp.queue', 'Queue.consume') def consume_with_instana(wrapped, instance, argv, kwargs): def callback_generator(original_callback): def callback_with_instana(*argv, **kwargs): ctx = None msg = argv[0] if msg.headers is not None: - ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, dict(msg.headers)) + ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, dict(msg.headers), + disable_w3c_trace_context=True) with async_tracer.start_active_span("rabbitmq", child_of=ctx) as scope: host, port = msg.sender.protocol.transport._sock.getsockname() @@ -85,7 +90,7 @@ def callback_with_instana(*argv, **kwargs): try: scope.span.set_tag("exchange", msg.exchange_name) scope.span.set_tag("sort", "consume") - scope.span.set_tag("address", host + ":" + str(port) ) + scope.span.set_tag("address", host + ":" + str(port)) scope.span.set_tag("key", msg.routing_key) original_callback(*argv, **kwargs) @@ -99,6 +104,7 @@ def callback_with_instana(*argv, **kwargs): argv = (callback_generator(cb),) return wrapped(*argv, **kwargs) + logger.debug("Instrumenting asynqp") except ImportError: pass diff --git a/instana/instrumentation/aws/triggers.py b/instana/instrumentation/aws/triggers.py index aa179ab6..c91aac33 100644 --- a/instana/instrumentation/aws/triggers.py +++ b/instana/instrumentation/aws/triggers.py @@ -22,9 +22,9 @@ def get_context(tracer, event): is_application_load_balancer_trigger(event) if is_proxy_event: - return tracer.extract(ot.Format.HTTP_HEADERS, event.get('headers', {})) + return tracer.extract(ot.Format.HTTP_HEADERS, event.get('headers', {}), disable_w3c_trace_context=True) - return tracer.extract(ot.Format.HTTP_HEADERS, event) + return tracer.extract(ot.Format.HTTP_HEADERS, event, disable_w3c_trace_context=True) def is_api_gateway_proxy_trigger(event): diff --git a/instana/instrumentation/celery/hooks.py b/instana/instrumentation/celery/hooks.py index 5d022813..2f6f7323 100644 --- a/instana/instrumentation/celery/hooks.py +++ b/instana/instrumentation/celery/hooks.py @@ -19,6 +19,7 @@ import urlparse as parse import urllib + def add_broker_tags(span, broker_url): try: url = parse.urlparse(broker_url) @@ -45,6 +46,7 @@ def add_broker_tags(span, broker_url): except Exception: logger.debug("Error parsing broker URL: %s" % broker_url, exc_info=True) + @signals.task_prerun.connect def task_prerun(*args, **kwargs): try: @@ -55,7 +57,7 @@ def task_prerun(*args, **kwargs): headers = task.request.get('headers', {}) if headers is not None: - ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, headers) + ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, headers, disable_w3c_trace_context=True) scope = tracer.start_active_span("celery-worker", child_of=ctx) scope.span.set_tag("task", task.name) @@ -67,6 +69,7 @@ def task_prerun(*args, **kwargs): except: logger.debug("task_prerun: ", exc_info=True) + @signals.task_postrun.connect def task_postrun(*args, **kwargs): try: @@ -78,6 +81,7 @@ def task_postrun(*args, **kwargs): except: logger.debug("after_task_publish: ", exc_info=True) + @signals.task_failure.connect def task_failure(*args, **kwargs): try: @@ -95,6 +99,7 @@ def task_failure(*args, **kwargs): except: logger.debug("task_failure: ", exc_info=True) + @signals.task_retry.connect def task_retry(*args, **kwargs): try: @@ -109,6 +114,7 @@ def task_retry(*args, **kwargs): except: logger.debug("task_failure: ", exc_info=True) + @signals.before_task_publish.connect def before_task_publish(*args, **kwargs): try: @@ -127,7 +133,8 @@ def before_task_publish(*args, **kwargs): # Context propagation context_headers = {} - active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, context_headers) + active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, context_headers, + disable_w3c_trace_context=True) # Fix for broken header propagation # https://github.com/celery/celery/issues/4875 @@ -141,6 +148,7 @@ def before_task_publish(*args, **kwargs): except: logger.debug("before_task_publish: ", exc_info=True) + @signals.after_task_publish.connect def after_task_publish(*args, **kwargs): try: @@ -152,6 +160,7 @@ def after_task_publish(*args, **kwargs): except: logger.debug("after_task_publish: ", exc_info=True) + logger.debug("Instrumenting celery") except ImportError: pass diff --git a/instana/instrumentation/google/cloud/pubsub.py b/instana/instrumentation/google/cloud/pubsub.py index baa864dd..b62b816f 100644 --- a/instana/instrumentation/google/cloud/pubsub.py +++ b/instana/instrumentation/google/cloud/pubsub.py @@ -47,7 +47,7 @@ def publish_with_instana(wrapped, instance, args, kwargs): with tracer.start_active_span('gcps-producer', child_of=parent_span) as scope: # trace continuity, inject to the span context headers = dict() - tracer.inject(scope.span.context, Format.TEXT_MAP, headers) + tracer.inject(scope.span.context, Format.TEXT_MAP, headers, disable_w3c_trace_context=True) # update the metadata dict with instana trace attributes kwargs.update(headers) @@ -73,7 +73,7 @@ def subscribe_with_instana(wrapped, instance, args, kwargs): def callback_with_instana(message): if message.attributes: - parent_span = tracer.extract(Format.TEXT_MAP, message.attributes) + parent_span = tracer.extract(Format.TEXT_MAP, message.attributes, disable_w3c_trace_context=True) else: parent_span = None diff --git a/instana/instrumentation/grpcio.py b/instana/instrumentation/grpcio.py index 919a2945..1c65abcd 100644 --- a/instana/instrumentation/grpcio.py +++ b/instana/instrumentation/grpcio.py @@ -12,12 +12,13 @@ try: import grpc from grpc._channel import _UnaryUnaryMultiCallable, _StreamUnaryMultiCallable, \ - _UnaryStreamMultiCallable, _StreamStreamMultiCallable + _UnaryStreamMultiCallable, _StreamStreamMultiCallable + + SUPPORTED_TYPES = [_UnaryUnaryMultiCallable, + _StreamUnaryMultiCallable, + _UnaryStreamMultiCallable, + _StreamStreamMultiCallable] - SUPPORTED_TYPES = [ _UnaryUnaryMultiCallable, - _StreamUnaryMultiCallable, - _UnaryStreamMultiCallable, - _StreamStreamMultiCallable ] def collect_tags(span, instance, argv, kwargs): try: @@ -58,7 +59,8 @@ def unary_unary_with_call_with_instana(wrapped, instance, argv, kwargs): if "metadata" not in kwargs: kwargs["metadata"] = [] - kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata']) + kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata'], + disable_w3c_trace_context=True) collect_tags(scope.span, instance, argv, kwargs) scope.span.set_tag('rpc.call_type', 'unary') @@ -69,6 +71,7 @@ def unary_unary_with_call_with_instana(wrapped, instance, argv, kwargs): else: return rv + @wrapt.patch_function_wrapper('grpc._channel', '_UnaryUnaryMultiCallable.future') def unary_unary_future_with_instana(wrapped, instance, argv, kwargs): parent_span = tracer.active_span @@ -82,7 +85,8 @@ def unary_unary_future_with_instana(wrapped, instance, argv, kwargs): if "metadata" not in kwargs: kwargs["metadata"] = [] - kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata']) + kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata'], + disable_w3c_trace_context=True) collect_tags(scope.span, instance, argv, kwargs) scope.span.set_tag('rpc.call_type', 'unary') @@ -93,6 +97,7 @@ def unary_unary_future_with_instana(wrapped, instance, argv, kwargs): else: return rv + @wrapt.patch_function_wrapper('grpc._channel', '_UnaryUnaryMultiCallable.__call__') def unary_unary_call_with_instana(wrapped, instance, argv, kwargs): parent_span = tracer.active_span @@ -106,7 +111,8 @@ def unary_unary_call_with_instana(wrapped, instance, argv, kwargs): if not "metadata" in kwargs: kwargs["metadata"] = [] - kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata']) + kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata'], + disable_w3c_trace_context=True) collect_tags(scope.span, instance, argv, kwargs) scope.span.set_tag('rpc.call_type', 'unary') @@ -117,6 +123,7 @@ def unary_unary_call_with_instana(wrapped, instance, argv, kwargs): else: return rv + @wrapt.patch_function_wrapper('grpc._channel', '_StreamUnaryMultiCallable.__call__') def stream_unary_call_with_instana(wrapped, instance, argv, kwargs): parent_span = tracer.active_span @@ -130,7 +137,8 @@ def stream_unary_call_with_instana(wrapped, instance, argv, kwargs): if not "metadata" in kwargs: kwargs["metadata"] = [] - kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata']) + kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata'], + disable_w3c_trace_context=True) collect_tags(scope.span, instance, argv, kwargs) scope.span.set_tag('rpc.call_type', 'stream') @@ -141,6 +149,7 @@ def stream_unary_call_with_instana(wrapped, instance, argv, kwargs): else: return rv + @wrapt.patch_function_wrapper('grpc._channel', '_StreamUnaryMultiCallable.with_call') def stream_unary_with_call_with_instana(wrapped, instance, argv, kwargs): parent_span = tracer.active_span @@ -154,7 +163,8 @@ def stream_unary_with_call_with_instana(wrapped, instance, argv, kwargs): if not "metadata" in kwargs: kwargs["metadata"] = [] - kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata']) + kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata'], + disable_w3c_trace_context=True) collect_tags(scope.span, instance, argv, kwargs) scope.span.set_tag('rpc.call_type', 'stream') @@ -165,6 +175,7 @@ def stream_unary_with_call_with_instana(wrapped, instance, argv, kwargs): else: return rv + @wrapt.patch_function_wrapper('grpc._channel', '_StreamUnaryMultiCallable.future') def stream_unary_future_with_instana(wrapped, instance, argv, kwargs): parent_span = tracer.active_span @@ -178,7 +189,8 @@ def stream_unary_future_with_instana(wrapped, instance, argv, kwargs): if not "metadata" in kwargs: kwargs["metadata"] = [] - kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata']) + kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata'], + disable_w3c_trace_context=True) collect_tags(scope.span, instance, argv, kwargs) scope.span.set_tag('rpc.call_type', 'stream') @@ -189,6 +201,7 @@ def stream_unary_future_with_instana(wrapped, instance, argv, kwargs): else: return rv + @wrapt.patch_function_wrapper('grpc._channel', '_UnaryStreamMultiCallable.__call__') def unary_stream_call_with_instana(wrapped, instance, argv, kwargs): parent_span = tracer.active_span @@ -202,7 +215,8 @@ def unary_stream_call_with_instana(wrapped, instance, argv, kwargs): if not "metadata" in kwargs: kwargs["metadata"] = [] - kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata']) + kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata'], + disable_w3c_trace_context=True) collect_tags(scope.span, instance, argv, kwargs) scope.span.set_tag('rpc.call_type', 'stream') @@ -213,6 +227,7 @@ def unary_stream_call_with_instana(wrapped, instance, argv, kwargs): else: return rv + @wrapt.patch_function_wrapper('grpc._channel', '_StreamStreamMultiCallable.__call__') def stream_stream_call_with_instana(wrapped, instance, argv, kwargs): parent_span = tracer.active_span @@ -226,7 +241,8 @@ def stream_stream_call_with_instana(wrapped, instance, argv, kwargs): if not "metadata" in kwargs: kwargs["metadata"] = [] - kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata']) + kwargs["metadata"] = tracer.inject(scope.span.context, opentracing.Format.BINARY, kwargs['metadata'], + disable_w3c_trace_context=True) collect_tags(scope.span, instance, argv, kwargs) scope.span.set_tag('rpc.call_type', 'stream') @@ -237,6 +253,7 @@ def stream_stream_call_with_instana(wrapped, instance, argv, kwargs): else: return rv + @wrapt.patch_function_wrapper('grpc._server', '_call_behavior') def call_behavior_with_instana(wrapped, instance, argv, kwargs): # Prep any incoming context headers @@ -245,7 +262,7 @@ def call_behavior_with_instana(wrapped, instance, argv, kwargs): for c in metadata: metadata_dict[c.key] = c.value - ctx = tracer.extract(opentracing.Format.BINARY, metadata_dict) + ctx = tracer.extract(opentracing.Format.BINARY, metadata_dict, disable_w3c_trace_context=True) with tracer.start_active_span("rpc-server", child_of=ctx) as scope: try: @@ -257,6 +274,7 @@ def call_behavior_with_instana(wrapped, instance, argv, kwargs): else: return rv + logger.debug("Instrumenting grpcio") except ImportError: pass diff --git a/instana/instrumentation/pika.py b/instana/instrumentation/pika.py index 3b844082..e26bf200 100644 --- a/instana/instrumentation/pika.py +++ b/instana/instrumentation/pika.py @@ -62,7 +62,8 @@ def _bind_args(exchange, routing_key, body, properties=None, *args, **kwargs): properties = properties or pika.BasicProperties() properties.headers = properties.headers or {} - tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, properties.headers) + tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, properties.headers, + disable_w3c_trace_context=True) args = (exchange, routing_key, body, properties) + args try: @@ -81,7 +82,8 @@ def _bind_args(queue, callback, *args, **kwargs): queue, callback, args, kwargs = _bind_args(*args, **kwargs) def _cb_wrapper(channel, method, properties, body): - parent_span = tracer.extract(opentracing.Format.HTTP_HEADERS, properties.headers) + parent_span = tracer.extract(opentracing.Format.HTTP_HEADERS, properties.headers, + disable_w3c_trace_context=True) with tracer.start_active_span("rabbitmq", child_of=parent_span) as scope: try: @@ -109,7 +111,8 @@ def _bind_args(queue, on_consume_callback, *args, **kwargs): queue, on_consume_callback, args, kwargs = _bind_args(*args, **kwargs) def _cb_wrapper(channel, method, properties, body): - parent_span = tracer.extract(opentracing.Format.HTTP_HEADERS, properties.headers) + parent_span = tracer.extract(opentracing.Format.HTTP_HEADERS, properties.headers, + disable_w3c_trace_context=True) with tracer.start_active_span("rabbitmq", child_of=parent_span) as scope: try: @@ -145,7 +148,8 @@ def _consume(gen): (method_frame, properties, body) = yilded - parent_span = tracer.extract(opentracing.Format.HTTP_HEADERS, properties.headers) + parent_span = tracer.extract(opentracing.Format.HTTP_HEADERS, properties.headers, + disable_w3c_trace_context=True) with tracer.start_active_span("rabbitmq", child_of=parent_span) as scope: try: _extract_consumer_tags(scope.span, diff --git a/instana/propagators/base_propagator.py b/instana/propagators/base_propagator.py index 3401150b..b33fcb22 100644 --- a/instana/propagators/base_propagator.py +++ b/instana/propagators/base_propagator.py @@ -6,7 +6,7 @@ import sys from instana.log import logger -from instana.util.ids import header_to_id_no_truncation +from instana.util.ids import header_to_id, header_to_long_id from instana.span_context import SpanContext from instana.w3c_trace_context.traceparent import Traceparent from instana.w3c_trace_context.tracestate import Tracestate @@ -119,9 +119,9 @@ def _set_correlation_properties(level, ctx): def _get_participating_trace_context(self, span_context): """ - + This method is called for getting the updated traceparent and tracestate values :param span_context: - :return: + :return: traceparent, tracestate """ if span_context.long_trace_id and not span_context.trace_parent: tp_trace_id = span_context.long_trace_id @@ -143,6 +143,9 @@ def __determine_span_context(self, trace_id, span_id, level, synthetic, tracepar :param span_id: instana span id :param level: instana level :param synthetic: instana synthetic + :param traceparent: + :param tracestate: + :param disable_w3c_trace_context: flag used to enable w3c trace context only on HTTP requests :return: ctx """ correlation = False @@ -202,7 +205,7 @@ def __extract_instana_headers(self, dc): Search carrier for the *HEADER* keys and return the tracing key-values :param dc: The dict or list potentially containing context - :return: trace_id, span_id, level, synthetic, traceparent, tracestate + :return: trace_id, span_id, level, synthetic """ trace_id, span_id, level, synthetic = [None] * 4 @@ -211,12 +214,12 @@ def __extract_instana_headers(self, dc): trace_id = dc.get(self.LC_HEADER_KEY_T) or dc.get(self.ALT_LC_HEADER_KEY_T) or dc.get( self.B_HEADER_KEY_T) or dc.get(self.B_ALT_LC_HEADER_KEY_T) if trace_id: - trace_id = header_to_id_no_truncation(trace_id) + trace_id = header_to_long_id(trace_id) span_id = dc.get(self.LC_HEADER_KEY_S) or dc.get(self.ALT_LC_HEADER_KEY_S) or dc.get( self.B_HEADER_KEY_S) or dc.get(self.B_ALT_LC_HEADER_KEY_S) if span_id: - span_id = header_to_id_no_truncation(span_id) + span_id = header_to_id(span_id) level = dc.get(self.LC_HEADER_KEY_L) or dc.get(self.ALT_LC_HEADER_KEY_L) or dc.get( self.B_HEADER_KEY_L) or dc.get(self.B_ALT_LC_HEADER_KEY_L) diff --git a/instana/tracer.py b/instana/tracer.py index 93f4216b..fd2e97ef 100644 --- a/instana/tracer.py +++ b/instana/tracer.py @@ -121,13 +121,13 @@ def start_span(self, return span - def inject(self, span_context, format, carrier, disable_w3c_trace_context=True): + def inject(self, span_context, format, carrier, disable_w3c_trace_context=False): if format in self._propagators: return self._propagators[format].inject(span_context, carrier, disable_w3c_trace_context) raise ot.UnsupportedFormatException() - def extract(self, format, carrier, disable_w3c_trace_context=True): + def extract(self, format, carrier, disable_w3c_trace_context=False): if format in self._propagators: return self._propagators[format].extract(carrier, disable_w3c_trace_context) diff --git a/instana/util/ids.py b/instana/util/ids.py index 912e5adb..4449916b 100644 --- a/instana/util/ids.py +++ b/instana/util/ids.py @@ -36,7 +36,7 @@ def generate_id(): return new_id -def header_to_id_no_truncation(header): +def header_to_long_id(header): """ We can receive headers in the following formats: 1. unsigned base 16 hex string (or bytes) of variable length diff --git a/tests/test_id_management.py b/tests/test_id_management.py index 20c671fc..69412e44 100644 --- a/tests/test_id_management.py +++ b/tests/test_id_management.py @@ -24,38 +24,38 @@ def test_id_generation(): def test_various_header_to_id_conversion(): # Get a hex string to test against & convert header_id = instana.util.ids.generate_id() - converted_id = instana.util.ids.header_to_id_no_truncation(header_id) + converted_id = instana.util.ids.header_to_long_id(header_id) assert(header_id == converted_id) # Hex value - result should be left padded - result = instana.util.ids.header_to_id_no_truncation('abcdef') + result = instana.util.ids.header_to_long_id('abcdef') assert('0000000000abcdef' == result) # Hex value - result = instana.util.ids.header_to_id_no_truncation('0123456789abcdef') + result = instana.util.ids.header_to_long_id('0123456789abcdef') assert('0123456789abcdef' == result) # Very long incoming header should just return the rightmost 16 bytes - result = instana.util.ids.header_to_id_no_truncation('0x0123456789abcdef0123456789abcdef') + result = instana.util.ids.header_to_long_id('0x0123456789abcdef0123456789abcdef') assert('0x0123456789abcdef0123456789abcdef' == result) def test_header_to_id_conversion_with_bogus_header(): # Bogus nil arg - bogus_result = instana.util.ids.header_to_id_no_truncation(None) + bogus_result = instana.util.ids.header_to_long_id(None) assert(instana.util.ids.BAD_ID == bogus_result) # Bogus Integer arg - bogus_result = instana.util.ids.header_to_id_no_truncation(1234) + bogus_result = instana.util.ids.header_to_long_id(1234) assert(instana.util.ids.BAD_ID == bogus_result) # Bogus Array arg - bogus_result = instana.util.ids.header_to_id_no_truncation([1234]) + bogus_result = instana.util.ids.header_to_long_id([1234]) assert(instana.util.ids.BAD_ID == bogus_result) # Bogus Hex Values in String - bogus_result = instana.util.ids.header_to_id_no_truncation('0xZZZZZZ') + bogus_result = instana.util.ids.header_to_long_id('0xZZZZZZ') assert(instana.util.ids.BAD_ID == bogus_result) - bogus_result = instana.util.ids.header_to_id_no_truncation('ZZZZZZ') + bogus_result = instana.util.ids.header_to_long_id('ZZZZZZ') assert(instana.util.ids.BAD_ID == bogus_result) From ef4a4195b41d0e6a040f0c79f43b0cc8bed87966 Mon Sep 17 00:00:00 2001 From: dimitraparaskevopoulou Date: Wed, 1 Sep 2021 14:41:20 +0200 Subject: [PATCH 39/39] remove unneeded parameter --- instana/instrumentation/aiohttp/client.py | 3 +-- instana/instrumentation/aiohttp/server.py | 6 ++---- instana/instrumentation/asgi.py | 6 ++---- instana/instrumentation/boto3_inst.py | 2 +- instana/instrumentation/django/middleware.py | 5 ++--- instana/instrumentation/flask/common.py | 3 +-- instana/instrumentation/flask/vanilla.py | 5 ++--- instana/instrumentation/flask/with_blinker.py | 5 ++--- instana/instrumentation/pyramid/tweens.py | 4 ++-- instana/instrumentation/sanic_inst.py | 5 ++--- instana/instrumentation/sudsjurko.py | 3 +-- instana/instrumentation/tornado/client.py | 3 +-- instana/instrumentation/tornado/server.py | 9 +++------ instana/instrumentation/urllib3.py | 3 +-- instana/instrumentation/webapp2_inst.py | 4 ++-- instana/instrumentation/wsgi.py | 4 ++-- 16 files changed, 27 insertions(+), 43 deletions(-) diff --git a/instana/instrumentation/aiohttp/client.py b/instana/instrumentation/aiohttp/client.py index f6261b69..3f2da1f8 100644 --- a/instana/instrumentation/aiohttp/client.py +++ b/instana/instrumentation/aiohttp/client.py @@ -27,8 +27,7 @@ async def stan_request_start(session, trace_config_ctx, params): scope = async_tracer.start_active_span("aiohttp-client", child_of=parent_span) trace_config_ctx.scope = scope - async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, params.headers, - disable_w3c_trace_context=False) + async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, params.headers) parts = str(params.url).split('?') if len(parts) > 1: diff --git a/instana/instrumentation/aiohttp/server.py b/instana/instrumentation/aiohttp/server.py index 787cb077..451e63a8 100644 --- a/instana/instrumentation/aiohttp/server.py +++ b/instana/instrumentation/aiohttp/server.py @@ -20,8 +20,7 @@ @middleware async def stan_middleware(request, handler): try: - ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, request.headers, - disable_w3c_trace_context=False) + ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, request.headers) request['scope'] = async_tracer.start_active_span('aiohttp-server', child_of=ctx) scope = request['scope'] @@ -57,8 +56,7 @@ async def stan_middleware(request, handler): scope.span.mark_as_errored() scope.span.set_tag("http.status_code", response.status) - async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers, - disable_w3c_trace_context=False) + async_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers) response.headers['Server-Timing'] = "intid;desc=%s" % scope.span.context.trace_id return response diff --git a/instana/instrumentation/asgi.py b/instana/instrumentation/asgi.py index d52a0dd9..b8c95f5c 100644 --- a/instana/instrumentation/asgi.py +++ b/instana/instrumentation/asgi.py @@ -67,8 +67,7 @@ async def __call__(self, scope, receive, send): request_headers = scope.get('headers') if isinstance(request_headers, list): - request_context = async_tracer.extract(opentracing.Format.BINARY, request_headers, - disable_w3c_trace_context=False) + request_context = async_tracer.extract(opentracing.Format.BINARY, request_headers) async def send_wrapper(response): span = async_tracer.active_span @@ -85,8 +84,7 @@ async def send_wrapper(response): headers = response.get('headers') if headers is not None: - async_tracer.inject(span.context, opentracing.Format.BINARY, headers, - disable_w3c_trace_context=False) + async_tracer.inject(span.context, opentracing.Format.BINARY, headers) except Exception: logger.debug("send_wrapper: ", exc_info=True) diff --git a/instana/instrumentation/boto3_inst.py b/instana/instrumentation/boto3_inst.py index b66a267a..cf6511b5 100644 --- a/instana/instrumentation/boto3_inst.py +++ b/instana/instrumentation/boto3_inst.py @@ -29,7 +29,7 @@ def lambda_inject_context(payload, scope): if not isinstance(invoke_payload, dict): invoke_payload = json.loads(invoke_payload) - tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, invoke_payload, disable_w3c_trace_context=False) + tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, invoke_payload) payload['Payload'] = json.dumps(invoke_payload) except Exception: logger.debug("non-fatal lambda_inject_context: ", exc_info=True) diff --git a/instana/instrumentation/django/middleware.py b/instana/instrumentation/django/middleware.py index 539ef649..21b5a5c4 100644 --- a/instana/instrumentation/django/middleware.py +++ b/instana/instrumentation/django/middleware.py @@ -33,7 +33,7 @@ def process_request(self, request): try: env = request.environ - ctx = tracer.extract(ot.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) + ctx = tracer.extract(ot.Format.HTTP_HEADERS, env) request.iscope = tracer.start_active_span('django', child_of=ctx) if agent.options.extra_http_headers is not None: @@ -76,8 +76,7 @@ def process_response(self, request, response): if path_tpl: request.iscope.span.set_tag("http.path_tpl", path_tpl) request.iscope.span.set_tag(ext.HTTP_STATUS_CODE, response.status_code) - tracer.inject(request.iscope.span.context, ot.Format.HTTP_HEADERS, response, - disable_w3c_trace_context=False) + tracer.inject(request.iscope.span.context, ot.Format.HTTP_HEADERS, response) response['Server-Timing'] = "intid;desc=%s" % request.iscope.span.context.trace_id except Exception: logger.debug("Instana middleware @ process_response", exc_info=True) diff --git a/instana/instrumentation/flask/common.py b/instana/instrumentation/flask/common.py index 0b5527b9..c3413e31 100644 --- a/instana/instrumentation/flask/common.py +++ b/instana/instrumentation/flask/common.py @@ -63,8 +63,7 @@ def handle_user_exception_with_instana(wrapped, instance, argv, kwargs): span.set_tag(ext.HTTP_STATUS_CODE, int(status_code)) if hasattr(response, 'headers'): - tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers, - disable_w3c_trace_context=False) + tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers) value = "intid;desc=%s" % scope.span.context.trace_id if hasattr(response.headers, 'add'): response.headers.add('Server-Timing', value) diff --git a/instana/instrumentation/flask/vanilla.py b/instana/instrumentation/flask/vanilla.py index 08b359b1..9cd4f2c8 100644 --- a/instana/instrumentation/flask/vanilla.py +++ b/instana/instrumentation/flask/vanilla.py @@ -22,7 +22,7 @@ def before_request_with_instana(*argv, **kwargs): env = flask.request.environ ctx = None - ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) + ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env) flask.g.scope = tracer.start_active_span('wsgi', child_of=ctx) span = flask.g.scope.span @@ -70,8 +70,7 @@ def after_request_with_instana(response): span.mark_as_errored() span.set_tag(ext.HTTP_STATUS_CODE, int(response.status_code)) - tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers, - disable_w3c_trace_context=False) + tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers) response.headers.add('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id) except: logger.debug("Flask after_request", exc_info=True) diff --git a/instana/instrumentation/flask/with_blinker.py b/instana/instrumentation/flask/with_blinker.py index 80efd720..8b2c6801 100644 --- a/instana/instrumentation/flask/with_blinker.py +++ b/instana/instrumentation/flask/with_blinker.py @@ -23,7 +23,7 @@ def request_started_with_instana(sender, **extra): env = flask.request.environ ctx = None - ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) + ctx = tracer.extract(opentracing.Format.HTTP_HEADERS, env) flask.g.scope = tracer.start_active_span('wsgi', child_of=ctx) span = flask.g.scope.span @@ -68,8 +68,7 @@ def request_finished_with_instana(sender, response, **extra): span.mark_as_errored() span.set_tag(ext.HTTP_STATUS_CODE, int(response.status_code)) - tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers, - disable_w3c_trace_context=False) + tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, response.headers) response.headers.add('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id) except: logger.debug("Flask after_request", exc_info=True) diff --git a/instana/instrumentation/pyramid/tweens.py b/instana/instrumentation/pyramid/tweens.py index 5c84712b..e4285019 100644 --- a/instana/instrumentation/pyramid/tweens.py +++ b/instana/instrumentation/pyramid/tweens.py @@ -20,7 +20,7 @@ def __init__(self, handler, registry): self.handler = handler def __call__(self, request): - ctx = tracer.extract(ot.Format.HTTP_HEADERS, dict(request.headers), disable_w3c_trace_context=False) + ctx = tracer.extract(ot.Format.HTTP_HEADERS, dict(request.headers)) scope = tracer.start_active_span('http', child_of=ctx) scope.span.set_tag(ext.SPAN_KIND, ext.SPAN_KIND_RPC_SERVER) @@ -47,7 +47,7 @@ def __call__(self, request): try: response = self.handler(request) - tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, response.headers, disable_w3c_trace_context=False) + tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, response.headers) response.headers['Server-Timing'] = "intid;desc=%s" % scope.span.context.trace_id except HTTPException as e: response = e diff --git a/instana/instrumentation/sanic_inst.py b/instana/instrumentation/sanic_inst.py index 431eb6b9..bac2ce94 100644 --- a/instana/instrumentation/sanic_inst.py +++ b/instana/instrumentation/sanic_inst.py @@ -40,8 +40,7 @@ def response_details(span, response): span.set_tag('http.status_code', status_code) if response.headers is not None: - async_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS, response.headers, - disable_w3c_trace_context=False) + async_tracer.inject(span.context, opentracing.Format.HTTP_HEADERS, response.headers) response.headers['Server-Timing'] = "intid;desc=%s" % span.context.trace_id except Exception: logger.debug("send_wrapper: ", exc_info=True) @@ -102,7 +101,7 @@ async def handle_request_with_instana(wrapped, instance, args, kwargs): except AttributeError: pass headers = request.headers.copy() - ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, headers, disable_w3c_trace_context=False) + ctx = async_tracer.extract(opentracing.Format.HTTP_HEADERS, headers) with async_tracer.start_active_span("asgi", child_of=ctx) as scope: scope.span.set_tag('span.kind', 'entry') scope.span.set_tag('http.path', request.path) diff --git a/instana/instrumentation/sudsjurko.py b/instana/instrumentation/sudsjurko.py index 2a214694..0e3e504d 100644 --- a/instana/instrumentation/sudsjurko.py +++ b/instana/instrumentation/sudsjurko.py @@ -35,8 +35,7 @@ def send_with_instana(wrapped, instance, args, kwargs): scope.span.set_tag(ext.HTTP_URL, instance.method.location) scope.span.set_tag(ext.HTTP_METHOD, 'POST') - active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance.options.headers, - disable_w3c_trace_context=False) + active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance.options.headers) rv = wrapped(*args, **kwargs) diff --git a/instana/instrumentation/tornado/client.py b/instana/instrumentation/tornado/client.py index 12455899..12f32911 100644 --- a/instana/instrumentation/tornado/client.py +++ b/instana/instrumentation/tornado/client.py @@ -47,8 +47,7 @@ def fetch_with_instana(wrapped, instance, argv, kwargs): kwargs = new_kwargs scope = tornado_tracer.start_active_span('tornado-client', child_of=parent_span) - tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, request.headers, - disable_w3c_trace_context=False) + tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, request.headers) # Query param scrubbing parts = request.url.split('?') diff --git a/instana/instrumentation/tornado/server.py b/instana/instrumentation/tornado/server.py index 0166e7aa..6666bb3e 100644 --- a/instana/instrumentation/tornado/server.py +++ b/instana/instrumentation/tornado/server.py @@ -30,8 +30,7 @@ def execute_with_instana(wrapped, instance, argv, kwargs): ctx = None if hasattr(instance.request.headers, '__dict__') and '_dict' in instance.request.headers.__dict__: ctx = tornado_tracer.extract(opentracing.Format.HTTP_HEADERS, - instance.request.headers.__dict__['_dict'], - disable_w3c_trace_context=False) + instance.request.headers.__dict__['_dict']) scope = tornado_tracer.start_active_span('tornado-server', child_of=ctx) # Query param scrubbing @@ -57,8 +56,7 @@ def execute_with_instana(wrapped, instance, argv, kwargs): # Set the context response headers now because tornado doesn't give us a better option to do so # later for this request. - tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance._headers, - disable_w3c_trace_context=False) + tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance._headers) instance.set_header(name='Server-Timing', value="intid;desc=%s" % scope.span.context.trace_id) return wrapped(*argv, **kwargs) @@ -72,8 +70,7 @@ def set_default_headers_with_instana(wrapped, instance, argv, kwargs): return wrapped(*argv, **kwargs) scope = instance.request._instana - tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance._headers, - disable_w3c_trace_context=False) + tornado_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, instance._headers) instance.set_header(name='Server-Timing', value="intid;desc=%s" % scope.span.context.trace_id) diff --git a/instana/instrumentation/urllib3.py b/instana/instrumentation/urllib3.py index 5e479a3b..3c1924c0 100644 --- a/instana/instrumentation/urllib3.py +++ b/instana/instrumentation/urllib3.py @@ -85,8 +85,7 @@ def urlopen_with_instana(wrapped, instance, args, kwargs): scope.span.set_tag(ext.HTTP_METHOD, kvs['method']) if 'headers' in kwargs: - active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, kwargs['headers'], - disable_w3c_trace_context=False) + active_tracer.inject(scope.span.context, opentracing.Format.HTTP_HEADERS, kwargs['headers']) response = wrapped(*args, **kwargs) diff --git a/instana/instrumentation/webapp2_inst.py b/instana/instrumentation/webapp2_inst.py index 68ac7968..2e091db5 100644 --- a/instana/instrumentation/webapp2_inst.py +++ b/instana/instrumentation/webapp2_inst.py @@ -26,7 +26,7 @@ def new_start_response(status, headers, exc_info=None): """Modified start response with additional headers.""" if 'stan_scope' in env: scope = env['stan_scope'] - tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, headers, disable_w3c_trace_context=False) + tracer.inject(scope.span.context, ot.Format.HTTP_HEADERS, headers) headers.append(('Server-Timing', "intid;desc=%s" % scope.span.context.trace_id)) res = start_response(status, headers, exc_info) @@ -41,7 +41,7 @@ def new_start_response(status, headers, exc_info=None): else: return start_response(status, headers, exc_info) - ctx = tracer.extract(ot.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) + ctx = tracer.extract(ot.Format.HTTP_HEADERS, env) scope = env['stan_scope'] = tracer.start_active_span("wsgi", child_of=ctx) if agent.options.extra_http_headers is not None: diff --git a/instana/instrumentation/wsgi.py b/instana/instrumentation/wsgi.py index db46cd62..9e17fece 100644 --- a/instana/instrumentation/wsgi.py +++ b/instana/instrumentation/wsgi.py @@ -22,7 +22,7 @@ def __call__(self, environ, start_response): def new_start_response(status, headers, exc_info=None): """Modified start response with additional headers.""" - tracer.inject(self.scope.span.context, ot.Format.HTTP_HEADERS, headers, disable_w3c_trace_context=False) + tracer.inject(self.scope.span.context, ot.Format.HTTP_HEADERS, headers) headers.append(('Server-Timing', "intid;desc=%s" % self.scope.span.context.trace_id)) res = start_response(status, headers, exc_info) @@ -35,7 +35,7 @@ def new_start_response(status, headers, exc_info=None): self.scope.close() return res - ctx = tracer.extract(ot.Format.HTTP_HEADERS, env, disable_w3c_trace_context=False) + ctx = tracer.extract(ot.Format.HTTP_HEADERS, env) self.scope = tracer.start_active_span("wsgi", child_of=ctx) if agent.options.extra_http_headers is not None: