From 657c34e21a3cbf25e205c6ad81a09586dcdbab26 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 9 Jul 2019 10:15:24 -0700 Subject: [PATCH 001/105] add opencensus impl --- .../core/{trace => tracing}/abstract_span.py | 0 .../azure/core/tracing/ext/opencensus.py | 101 ++++++++++++++++++ 2 files changed, 101 insertions(+) rename sdk/core/azure-core/azure/core/{trace => tracing}/abstract_span.py (100%) create mode 100644 sdk/core/azure-core/azure/core/tracing/ext/opencensus.py diff --git a/sdk/core/azure-core/azure/core/trace/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py similarity index 100% rename from sdk/core/azure-core/azure/core/trace/abstract_span.py rename to sdk/core/azure-core/azure/core/tracing/abstract_span.py diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py new file mode 100644 index 000000000000..7e899e1de301 --- /dev/null +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py @@ -0,0 +1,101 @@ +import os + +from opencensus.trace import execution_context +from opencensus.trace import tracer as tracer_module, Span +from opencensus.trace.propagation import trace_context_http_header_format +from opencensus.trace.samplers import ProbabilitySampler +from opencensus.trace.tracers.noop_tracer import NoopTracer + + +class OpencensusWrapper: + def __init__(self, span=None, name="parent_span"): + # type: (Any) -> None + tracer = self.get_current_tracer() + self.was_created_by_azure_sdk = False + if span is None: + instrumentation_key = self._get_environ("APPINSIGHTS_INSTRUMENTATIONKEY") + prob = self._get_environ("AZURE_TRACING_SAMPLER") or 0.001 + if tracer is None or isinstance(tracer, NoopTracer): + if instrumentation_key is not None: + from opencensus.ext.azure.trace_exporter import AzureExporter + + tracer = tracer_module.Tracer( + exporter=AzureExporter(instrumentation_key=instrumentation_key), + sampler=ProbabilitySampler(prob), + ) + else: + tracer = tracer_module.Tracer(sampler=ProbabilitySampler(prob)) + self.was_created_by_azure_sdk = True + span = tracer.span(name=name) + + self.tracer = tracer + self.trace_id = None + if span.context_tracer: + self.trace_id = span.context_tracer.span_context.trace_id + self.span_instance = span + self.span_id = str(span.span_id) + self.children = [] + + def _get_environ(self, key): + # type: (str) -> str + if key in os.environ: + return os.environ[key] + return None + + def span(self, name="child_span"): + # type: (str) -> OpencensusWrapper + child = self.span_instance.span(name=name) + wrapped_child = OpencensusWrapper(child) + wrapped_child.trace_id = self.trace_id + self.children.append(wrapped_child) + return wrapped_child + + def start(self): + # type: () -> None + self.span_instance.start() + + def finish(self): + # type: () -> None + self.span_instance.finish() + if self.was_created_by_azure_sdk: + self.end_tracer(self.tracer) + + def to_header(self, headers): + # type: (Dict[str, str]) -> str + tracer_from_context = self.get_current_tracer() + temp_headers = {} + if tracer_from_context is not None: + ctx = tracer_from_context.span_context + temp_headers = tracer_from_context.propagator.to_headers(ctx) + return temp_headers + + def from_header(self, headers): + # type: (Dict[str, str]) -> Any + ctx = trace_context_http_header_format.TraceContextPropagator().from_headers(headers) + return tracer_module.Tracer(span_context=ctx) + + @classmethod + def end_tracer(cls, tracer): + # type: (Tracer) -> None + if tracer is not None: + tracer.end_span() + + @classmethod + def get_current_span(cls): + # type: () -> Span + return execution_context.get_current_span() + + @classmethod + def get_current_tracer(cls): + # type: () -> Tracer + return execution_context.get_opencensus_tracer() + + @classmethod + def set_current_span(cls, span): + # type: (Span) -> None + return execution_context.set_current_span(span) + + @classmethod + def set_current_tracer(cls, tracer): + # type: (Tracer) -> None + return execution_context.set_opencensus_tracer(tracer) From 8573e2312d61180ec42ec39107c6c34a46256ded Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 9 Jul 2019 10:18:13 -0700 Subject: [PATCH 002/105] dont need to check for noop tracer --- sdk/core/azure-core/azure/core/tracing/ext/opencensus.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py index 7e899e1de301..80d47c3e6d71 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py @@ -4,7 +4,6 @@ from opencensus.trace import tracer as tracer_module, Span from opencensus.trace.propagation import trace_context_http_header_format from opencensus.trace.samplers import ProbabilitySampler -from opencensus.trace.tracers.noop_tracer import NoopTracer class OpencensusWrapper: @@ -15,7 +14,7 @@ def __init__(self, span=None, name="parent_span"): if span is None: instrumentation_key = self._get_environ("APPINSIGHTS_INSTRUMENTATIONKEY") prob = self._get_environ("AZURE_TRACING_SAMPLER") or 0.001 - if tracer is None or isinstance(tracer, NoopTracer): + if tracer is None: if instrumentation_key is not None: from opencensus.ext.azure.trace_exporter import AzureExporter From 3f643254c2d34782ce84c0b5712bce7d9a0b5290 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 9 Jul 2019 13:57:35 -0700 Subject: [PATCH 003/105] get rid of span and trace id --- .../azure/core/tracing/ext/opencensus.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py index 80d47c3e6d71..7240c5c85757 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py @@ -1,12 +1,17 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ import os +from azure.core.tracing.abstract_span import AbstractSpan from opencensus.trace import execution_context from opencensus.trace import tracer as tracer_module, Span from opencensus.trace.propagation import trace_context_http_header_format from opencensus.trace.samplers import ProbabilitySampler -class OpencensusWrapper: +class OpencensusWrapper(AbstractSpan): def __init__(self, span=None, name="parent_span"): # type: (Any) -> None tracer = self.get_current_tracer() @@ -28,12 +33,7 @@ def __init__(self, span=None, name="parent_span"): span = tracer.span(name=name) self.tracer = tracer - self.trace_id = None - if span.context_tracer: - self.trace_id = span.context_tracer.span_context.trace_id - self.span_instance = span self.span_id = str(span.span_id) - self.children = [] def _get_environ(self, key): # type: (str) -> str @@ -45,8 +45,6 @@ def span(self, name="child_span"): # type: (str) -> OpencensusWrapper child = self.span_instance.span(name=name) wrapped_child = OpencensusWrapper(child) - wrapped_child.trace_id = self.trace_id - self.children.append(wrapped_child) return wrapped_child def start(self): From 0b04d805a2a0d92e07bcbc141460dc2d351b1f93 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 9 Jul 2019 13:58:31 -0700 Subject: [PATCH 004/105] fix span instance --- sdk/core/azure-core/azure/core/tracing/ext/opencensus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py index 7240c5c85757..9cf369052caf 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py @@ -33,7 +33,7 @@ def __init__(self, span=None, name="parent_span"): span = tracer.span(name=name) self.tracer = tracer - self.span_id = str(span.span_id) + self.span_instance = span def _get_environ(self, key): # type: (str) -> str From 7b728d8bf27fc187920f272a494f5de9d69e7fa6 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 9 Jul 2019 15:50:37 -0700 Subject: [PATCH 005/105] added documentation --- .../azure/core/tracing/ext/opencensus.py | 75 +++++++++++++++++-- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py index 9cf369052caf..a472946384b7 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py @@ -4,16 +4,31 @@ # ------------------------------------ import os -from azure.core.tracing.abstract_span import AbstractSpan +from azure.core.tracing import AbstractSpan from opencensus.trace import execution_context from opencensus.trace import tracer as tracer_module, Span from opencensus.trace.propagation import trace_context_http_header_format from opencensus.trace.samplers import ProbabilitySampler +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False + +if TYPE_CHECKING: + from typing import Any, Dict, Union + class OpencensusWrapper(AbstractSpan): + """Wraps a given Opencensus Span so that it implements azure.core.tracing.AbstractSpan""" + def __init__(self, span=None, name="parent_span"): - # type: (Any) -> None + # type: (Span, str) -> None + """ + :param span: The opencensus span to wrap + :param name: The name of the opencensus span to create if a new span is needed + """ + super(OpencensusWrapper, self).__init__(span, name) tracer = self.get_current_tracer() self.was_created_by_azure_sdk = False if span is None: @@ -33,32 +48,57 @@ def __init__(self, span=None, name="parent_span"): span = tracer.span(name=name) self.tracer = tracer - self.span_instance = span + self._span_instance = span + + @property + def span_instance(self): + # type: () -> Span + """ + :return: The openencensus span that is being wrapped. + """ + return self._span_instance def _get_environ(self, key): - # type: (str) -> str + # type: (str) -> Union[str, None] + """ + Gets the Environment Variable given. Will return None if no such environment variable is found. + :param key: The name of the environment variable + :return: The value of the variable or None if the variable does not exist. + """ if key in os.environ: return os.environ[key] return None def span(self, name="child_span"): # type: (str) -> OpencensusWrapper + """ + Create a child span for the current span and append it to the child spans list in the span instance. + :param name: Name of the child span + :return: The OpencensusWrapper that is wrapping the child span instance + """ child = self.span_instance.span(name=name) wrapped_child = OpencensusWrapper(child) return wrapped_child def start(self): # type: () -> None + """Set the start time for a span.""" self.span_instance.start() def finish(self): # type: () -> None + """Set the end time for a span.""" self.span_instance.finish() if self.was_created_by_azure_sdk: self.end_tracer(self.tracer) def to_header(self, headers): # type: (Dict[str, str]) -> str + """ + Returns a dictionary with the header labels and values. + :param headers: A key value pair dictionary + :return: A key value pair dictionary + """ tracer_from_context = self.get_current_tracer() temp_headers = {} if tracer_from_context is not None: @@ -68,31 +108,52 @@ def to_header(self, headers): def from_header(self, headers): # type: (Dict[str, str]) -> Any + """ + Given a dictionary returns a new tracer with the span context extracted from that dictionary. + :param headers: A key value pair dictionary + :return: A tracer initialized with the span context extraction from headers. + """ ctx = trace_context_http_header_format.TraceContextPropagator().from_headers(headers) return tracer_module.Tracer(span_context=ctx) @classmethod def end_tracer(cls, tracer): - # type: (Tracer) -> None + # type: (tracer_module.Tracer) -> None + """ + If a tracer exists, exports and ends the tracer. + :param tracer: The tracer to export and end + """ if tracer is not None: tracer.end_span() @classmethod def get_current_span(cls): # type: () -> Span + """ + Get the current span from the execution context. Return None otherwise. + """ return execution_context.get_current_span() @classmethod def get_current_tracer(cls): - # type: () -> Tracer + # type: () -> tracer_module.Tracer + """ + Get the current tracer from the execution context. Return None otherwise. + """ return execution_context.get_opencensus_tracer() @classmethod def set_current_span(cls, span): # type: (Span) -> None + """ + Set the given span as the current span in the execution context. + """ return execution_context.set_current_span(span) @classmethod def set_current_tracer(cls, tracer): - # type: (Tracer) -> None + # type: (tracer_module.Tracer) -> None + """ + Set the given tracer as the current tracer in the execution context. + """ return execution_context.set_opencensus_tracer(tracer) From bfd355c22aea01ca44d2b751cc136ae8280ac810 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 9 Jul 2019 16:03:22 -0700 Subject: [PATCH 006/105] added documentation and refactor names of a few variables --- .../azure-core/azure/core/tracing/ext/opencensus.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py index a472946384b7..ef2238857e9e 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py @@ -25,6 +25,9 @@ class OpencensusWrapper(AbstractSpan): def __init__(self, span=None, name="parent_span"): # type: (Span, str) -> None """ + If a span is not passed in, creates a new tracer. If the instrumentation key for Azure Exporter is given, will + configure the azure exporter else will just create a new tracer. + :param span: The opencensus span to wrap :param name: The name of the opencensus span to create if a new span is needed """ @@ -32,14 +35,14 @@ def __init__(self, span=None, name="parent_span"): tracer = self.get_current_tracer() self.was_created_by_azure_sdk = False if span is None: - instrumentation_key = self._get_environ("APPINSIGHTS_INSTRUMENTATIONKEY") - prob = self._get_environ("AZURE_TRACING_SAMPLER") or 0.001 if tracer is None: - if instrumentation_key is not None: + azure_exporter_instrumentation_key = self._get_environ("APPINSIGHTS_INSTRUMENTATIONKEY") + prob = self._get_environ("AZURE_TRACING_SAMPLER") or 0.001 + if azure_exporter_instrumentation_key is not None: from opencensus.ext.azure.trace_exporter import AzureExporter tracer = tracer_module.Tracer( - exporter=AzureExporter(instrumentation_key=instrumentation_key), + exporter=AzureExporter(instrumentation_key=azure_exporter_instrumentation_key), sampler=ProbabilitySampler(prob), ) else: From cd06c9772286bc3a60fa9a4ffea136021627fc99 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 04:20:47 -0700 Subject: [PATCH 007/105] write test for opencensus wrapper --- .../azure/core/tracing/ext/__init__.py | 0 .../{opencensus.py => opencensus_wrapper.py} | 45 ++++--- ...ace_context.py => test_tracing_context.py} | 4 + .../tests/test_tracing_implementations.py | 119 ++++++++++++++++++ 4 files changed, 149 insertions(+), 19 deletions(-) create mode 100644 sdk/core/azure-core/azure/core/tracing/ext/__init__.py rename sdk/core/azure-core/azure/core/tracing/ext/{opencensus.py => opencensus_wrapper.py} (81%) rename sdk/core/azure-core/tests/{test_trace_context.py => test_tracing_context.py} (97%) create mode 100644 sdk/core/azure-core/tests/test_tracing_implementations.py diff --git a/sdk/core/azure-core/azure/core/tracing/ext/__init__.py b/sdk/core/azure-core/azure/core/tracing/ext/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py similarity index 81% rename from sdk/core/azure-core/azure/core/tracing/ext/opencensus.py rename to sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index ef2238857e9e..e0135e166517 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -9,6 +9,11 @@ from opencensus.trace import tracer as tracer_module, Span from opencensus.trace.propagation import trace_context_http_header_format from opencensus.trace.samplers import ProbabilitySampler +from opencensus.trace.tracers.noop_tracer import NoopTracer +try: + from opencensus.ext.azure.trace_exporter import AzureExporter +except: + AzureExporter = False try: from typing import TYPE_CHECKING @@ -34,21 +39,22 @@ def __init__(self, span=None, name="parent_span"): super(OpencensusWrapper, self).__init__(span, name) tracer = self.get_current_tracer() self.was_created_by_azure_sdk = False - if span is None: - if tracer is None: - azure_exporter_instrumentation_key = self._get_environ("APPINSIGHTS_INSTRUMENTATIONKEY") - prob = self._get_environ("AZURE_TRACING_SAMPLER") or 0.001 - if azure_exporter_instrumentation_key is not None: - from opencensus.ext.azure.trace_exporter import AzureExporter - - tracer = tracer_module.Tracer( - exporter=AzureExporter(instrumentation_key=azure_exporter_instrumentation_key), - sampler=ProbabilitySampler(prob), - ) - else: - tracer = tracer_module.Tracer(sampler=ProbabilitySampler(prob)) - self.was_created_by_azure_sdk = True - span = tracer.span(name=name) + if span is None and (tracer is None or isinstance(tracer, NoopTracer)): + azure_exporter_instrumentation_key = self._get_environ( + "APPINSIGHTS_INSTRUMENTATIONKEY" + ) + prob = float(self._get_environ("AZURE_TRACING_SAMPLER") or 0) + if azure_exporter_instrumentation_key is not None and AzureExporter: + tracer = tracer_module.Tracer( + exporter=AzureExporter( + instrumentation_key=azure_exporter_instrumentation_key + ), + sampler=ProbabilitySampler(prob), + ) + else: + tracer = tracer or tracer_module.Tracer(sampler=ProbabilitySampler(prob)) + self.was_created_by_azure_sdk = True + span = span or tracer.span(name=name) self.tracer = tracer self._span_instance = span @@ -79,9 +85,7 @@ def span(self, name="child_span"): :param name: Name of the child span :return: The OpencensusWrapper that is wrapping the child span instance """ - child = self.span_instance.span(name=name) - wrapped_child = OpencensusWrapper(child) - return wrapped_child + return OpencensusWrapper(self.span_instance.span(name=name)) def start(self): # type: () -> None @@ -116,7 +120,9 @@ def from_header(self, headers): :param headers: A key value pair dictionary :return: A tracer initialized with the span context extraction from headers. """ - ctx = trace_context_http_header_format.TraceContextPropagator().from_headers(headers) + ctx = trace_context_http_header_format.TraceContextPropagator().from_headers( + headers + ) return tracer_module.Tracer(span_context=ctx) @classmethod @@ -128,6 +134,7 @@ def end_tracer(cls, tracer): """ if tracer is not None: tracer.end_span() + tracer.finish() @classmethod def get_current_span(cls): diff --git a/sdk/core/azure-core/tests/test_trace_context.py b/sdk/core/azure-core/tests/test_tracing_context.py similarity index 97% rename from sdk/core/azure-core/tests/test_trace_context.py rename to sdk/core/azure-core/tests/test_tracing_context.py index 2b8a32762e21..cd54fecde2c2 100644 --- a/sdk/core/azure-core/tests/test_trace_context.py +++ b/sdk/core/azure-core/tests/test_tracing_context.py @@ -49,3 +49,7 @@ def work(): span = tracing_context.current_span.get() assert span == current_span assert getattr(span, "in_worker", False) + + +if __name__ == "__main__": + unittest.main() diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py new file mode 100644 index 000000000000..2cceacc735f7 --- /dev/null +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -0,0 +1,119 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import unittest + +try: + from unittest import mock +except ImportError: + import mock + +from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper +from opencensus.trace import tracer as tracer_module +from opencensus.trace.samplers import AlwaysOnSampler +from opencensus.common.utils import timestamp_to_microseconds +import os +import time + + +class ContextHelper(object): + def __init__(self): + self.orig_tracer = OpencensusWrapper.get_current_tracer() + self.orig_current_span = OpencensusWrapper.get_current_span() + + def __enter__(self): + self.orig_tracer = OpencensusWrapper.get_current_tracer() + self.orig_current_span = OpencensusWrapper.get_current_span() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + OpencensusWrapper.set_current_tracer(self.orig_tracer) + OpencensusWrapper.set_current_span(self.orig_current_span) + + +class TestOpencensusWrapper(unittest.TestCase): + def test_span_passed_in(self): + with ContextHelper(): + tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) + with tracer.start_span(name="parent") as parent: + wrapped_span = OpencensusWrapper(parent) + assert wrapped_span.span_instance.name == "parent" + assert ( + wrapped_span.span_instance.context_tracer.trace_id + == tracer.span_context.trace_id + ) + wrapped_span.finish() + tracer.finish() + + def test_no_span_passed_in_with_no_environ(self): + with ContextHelper(): + tracer = OpencensusWrapper.get_current_tracer() + wrapped_span = OpencensusWrapper() + assert wrapped_span.span_instance.name == "parent_span" + assert ( + wrapped_span.span_instance.context_tracer.span_context.trace_id + == tracer.span_context.trace_id + ) + wrapped_span.finish() + + @mock.patch.dict(os.environ, {'APPINSIGHTS_INSTRUMENTATIONKEY':'some key'}) + def test_no_span_passed_in_with_environ(self): + with ContextHelper() as ctx: + wrapped_span = OpencensusWrapper() + assert wrapped_span.span_instance.name == "parent_span" + tracer = OpencensusWrapper.get_current_tracer() + assert wrapped_span.tracer.span_context.trace_id == tracer.span_context.trace_id + assert not wrapped_span.span_instance.context_tracer.span_context.trace_id == ctx.orig_tracer.span_context.trace_id + wrapped_span.finish() + + def test_no_span_but_in_trace(self): + with ContextHelper(): + tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) + wrapped_span = OpencensusWrapper() + assert wrapped_span.span_instance.name == "parent_span" + assert ( + wrapped_span.span_instance.context_tracer.trace_id + == tracer.span_context.trace_id + ) + wrapped_span.finish() + tracer.finish() + + def test_span(self): + with ContextHelper() as ctx: + tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) + wrapped_class = OpencensusWrapper() + child = wrapped_class.span() + assert child.span_instance.name == "child_span" + assert child.tracer.span_context.trace_id == tracer.span_context.trace_id + assert len(wrapped_class.span_instance.children) == 1 + assert wrapped_class.span_instance.children[0] == child.span_instance + + def test_start_finish(self): + with ContextHelper() as ctx: + tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) + wrapped_class = OpencensusWrapper() + wrapped_class.start() + time.sleep(1) + wrapped_class.finish() + latency = timestamp_to_microseconds(wrapped_class.span_instance.end_time) - timestamp_to_microseconds(wrapped_class.span_instance.start_time) + latency = int(latency/10000) + assert latency == 100 + + def test_to_and_from_header(self): + with ContextHelper() as ctx: + wrapped_class = OpencensusWrapper() + og_header = {'traceparent': '00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01'} + tracer = wrapped_class.from_header(og_header) + assert tracer.span_context.trace_id == "2578531519ed94423ceae67588eff2c9" + headers = wrapped_class.to_header({}) + assert headers == og_header + + def test_end_tracer(self): + with ContextHelper() as ctx: + tracer = mock.Mock(spec=tracer_module.Tracer) + OpencensusWrapper.end_tracer(tracer) + assert tracer.finish.called + +if __name__ == "__main__": + unittest.main() From a9f7a6f4ec7e7fd6ff0d7aed21f3f75a69c122b6 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 10:30:11 -0700 Subject: [PATCH 008/105] put opencensus in the dev requirements --- sdk/core/azure-core/dev_requirements.txt | 4 ++- .../tests/test_tracing_implementations.py | 29 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/sdk/core/azure-core/dev_requirements.txt b/sdk/core/azure-core/dev_requirements.txt index 77df372c00dc..9e04487e4fca 100644 --- a/sdk/core/azure-core/dev_requirements.txt +++ b/sdk/core/azure-core/dev_requirements.txt @@ -2,4 +2,6 @@ trio; python_version >= '3.5' aiohttp>=3.0; python_version >= '3.5' aiodns>=2.0; python_version >= '3.5' typing_extensions>=3.7.2 -mypy>=0.7; python_version >= '3.6' \ No newline at end of file +mypy>=0.7; python_version >= '3.6' +opencensus>=0.6.0; python_version >= '2.7' +opencensus-ext-azure>=0.3.1; python_version >= '2.7' \ No newline at end of file diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 2cceacc735f7..e2fb94e77baf 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -57,14 +57,20 @@ def test_no_span_passed_in_with_no_environ(self): ) wrapped_span.finish() - @mock.patch.dict(os.environ, {'APPINSIGHTS_INSTRUMENTATIONKEY':'some key'}) + @mock.patch.dict(os.environ, {"APPINSIGHTS_INSTRUMENTATIONKEY": "some key"}) def test_no_span_passed_in_with_environ(self): with ContextHelper() as ctx: wrapped_span = OpencensusWrapper() assert wrapped_span.span_instance.name == "parent_span" tracer = OpencensusWrapper.get_current_tracer() - assert wrapped_span.tracer.span_context.trace_id == tracer.span_context.trace_id - assert not wrapped_span.span_instance.context_tracer.span_context.trace_id == ctx.orig_tracer.span_context.trace_id + assert ( + wrapped_span.tracer.span_context.trace_id + == tracer.span_context.trace_id + ) + assert ( + not wrapped_span.span_instance.context_tracer.span_context.trace_id + == ctx.orig_tracer.span_context.trace_id + ) wrapped_span.finish() def test_no_span_but_in_trace(self): @@ -88,7 +94,7 @@ def test_span(self): assert child.tracer.span_context.trace_id == tracer.span_context.trace_id assert len(wrapped_class.span_instance.children) == 1 assert wrapped_class.span_instance.children[0] == child.span_instance - + def test_start_finish(self): with ContextHelper() as ctx: tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) @@ -96,24 +102,29 @@ def test_start_finish(self): wrapped_class.start() time.sleep(1) wrapped_class.finish() - latency = timestamp_to_microseconds(wrapped_class.span_instance.end_time) - timestamp_to_microseconds(wrapped_class.span_instance.start_time) - latency = int(latency/10000) + latency = timestamp_to_microseconds( + wrapped_class.span_instance.end_time + ) - timestamp_to_microseconds(wrapped_class.span_instance.start_time) + latency = int(latency / 10000) assert latency == 100 - + def test_to_and_from_header(self): with ContextHelper() as ctx: wrapped_class = OpencensusWrapper() - og_header = {'traceparent': '00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01'} + og_header = { + "traceparent": "00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01" + } tracer = wrapped_class.from_header(og_header) assert tracer.span_context.trace_id == "2578531519ed94423ceae67588eff2c9" headers = wrapped_class.to_header({}) assert headers == og_header - + def test_end_tracer(self): with ContextHelper() as ctx: tracer = mock.Mock(spec=tracer_module.Tracer) OpencensusWrapper.end_tracer(tracer) assert tracer.finish.called + if __name__ == "__main__": unittest.main() From b8f63df9ea275cc096d392f5105b2028be0f3af3 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 10:42:22 -0700 Subject: [PATCH 009/105] initial common --- .../azure-core/azure/core/tracing/common.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 sdk/core/azure-core/azure/core/tracing/common.py diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py new file mode 100644 index 000000000000..882c9ecbbb7a --- /dev/null +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -0,0 +1,64 @@ +import functools +from os import environ +import re + +from azure.core.trace.context import tracing_context +from azure.core.trace.abstract_span import AbstractSpan +from azure.core.settings import settings + + +def is_opencensus_installed(): + try: + import opencensus + + return True + except ImportError: + return False + + +def set_span_contexts(span, span_instance=None, wrapper_class=None): + # type: (AbstractSpan, AbstractSpan) -> None + tracing_context.current_span.set(span) + if span is not None or (span_instance is not None and wrapper_class is not None): + span_instance = span_instance or span.span_instance + span = wrapper_class or span + span.set_current_span(span_instance) + + +def get_parent(kwargs, *args): + # type: (Any) -> Tuple(Any, Any) + parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan + wrapper_class = tracing_context.convert_tracing_impl( + settings.tracing_implementation() + ) + orig_context = tracing_context.current_span.get() + + if parent_span is None: + parent_span = orig_context + else: + wrapper_class = wrapper_class + parent_span = wrapper_class(parent_span) + + if wrapper_class is None and is_opencensus_installed(): + # wrapper_class = OpencensusWrapper + pass + + if parent_span is None and wrapper_class is not None: + current_span = wrapper_class.get_current_span() + parent_span = ( + wrapper_class(span=current_span) + if current_span + else wrapper_class(name="azure-sdk-for-python-first_parent_span") + ) + + original_span_instance = None + if wrapper_class is not None: + original_span_instance = wrapper_class.get_current_span() + + return parent_span, orig_context, original_span_instance + + +def should_use_trace(parent_span): + # type: (AbstractSpan, List[str], str) + only_propagate = settings.tracing_should_only_propagate() + return parent_span and not only_propagate From 408c5ad23eee32b33954cb380bc91422e85ef9d3 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 11:19:35 -0700 Subject: [PATCH 010/105] only import the wrapper when necessary --- sdk/core/azure-core/azure/core/tracing/common.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 882c9ecbbb7a..9dd0bad2ed19 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -16,6 +16,12 @@ def is_opencensus_installed(): return False +def get_opencensus_wrapper(): + from azure.core.tracing.ext.opencensus import OpencensusWrapper + + return OpencensusWrapper + + def set_span_contexts(span, span_instance=None, wrapper_class=None): # type: (AbstractSpan, AbstractSpan) -> None tracing_context.current_span.set(span) @@ -36,12 +42,11 @@ def get_parent(kwargs, *args): if parent_span is None: parent_span = orig_context else: - wrapper_class = wrapper_class + wrapper_class = wrapper_class or get_opencensus_wrapper() parent_span = wrapper_class(parent_span) if wrapper_class is None and is_opencensus_installed(): - # wrapper_class = OpencensusWrapper - pass + wrapper_class = get_opencensus_wrapper() if parent_span is None and wrapper_class is not None: current_span = wrapper_class.get_current_span() From 9baf366a77a3e6f020e26162352b7cf74c60c064 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 11:39:27 -0700 Subject: [PATCH 011/105] add check for the exporter --- sdk/core/azure-core/tests/test_tracing_implementations.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index e2fb94e77baf..47273b2ecda8 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -12,6 +12,7 @@ from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler +from opencensus.ext.azure.trace_exporter import AzureExporter from opencensus.common.utils import timestamp_to_microseconds import os import time @@ -71,6 +72,7 @@ def test_no_span_passed_in_with_environ(self): not wrapped_span.span_instance.context_tracer.span_context.trace_id == ctx.orig_tracer.span_context.trace_id ) + assert isinstance(tracer.exporter, AzureExporter) wrapped_span.finish() def test_no_span_but_in_trace(self): From f4c52fddde4bf52ac40556df597650de2ba1c6a0 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 13:48:58 -0700 Subject: [PATCH 012/105] rework logic and fix some settings --- sdk/core/azure-core/azure/core/settings.py | 58 ++++++++++++++++++ .../azure-core/azure/core/tracing/common.py | 60 +++++++++++-------- .../azure-core/azure/core/tracing/context.py | 3 +- 3 files changed, 94 insertions(+), 27 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 566a15b03ab9..6be6b6e1264e 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -33,6 +33,8 @@ import os from typing import Any, Union +from azure.core.tracing import AbstractSpan + __all__ = ("settings",) @@ -110,6 +112,48 @@ def convert_logging(value): return level +def get_opencensus_wrapper(): + # type: () -> OpencensusWrapper + """Returns the OpencensusWrapper if opencensus is installed else returns None""" + if is_opencensus_installed(): + from azure.core.tracing.ext.opencensus import OpencensusWrapper + + return OpencensusWrapper + else: + return None + + +def convert_tracing_impl(value): + # type: (Union[str, AbstractSpan]) -> AbstractSpan + """Convert a string to AbstractSpan + + If a AbstractSpan is passed in, it is returned as-is. Otherwise the function + understands the following strings, ignoring case: + + * "opencensus" + + :param value: the value to convert + :type value: string + :returns: AbstractSpan + :raises ValueError: If conversion to AbstractSpan fails + + """ + if isinstance(value, AbstractSpan): + return value + + _impl_dict = {"opencensus": get_opencensus_wrapper()} + wrapper_class = _impl_dict.get(value, None) + + if wrapper_class is None: + raise ValueError( + "Cannot convert {} to AbstractSpan, valid values are: {}".format( + value, ", ".join(_impl_dict) + ) + ) + + return wrapper_class + + class PrioritizedSetting(object): """Return a value for a global setting according to configuration precedence. @@ -361,5 +405,19 @@ def _config(self, props): # pylint: disable=no-self-use default=False, ) + tracing_implementation = PrioritizedSetting( + "tracing_implementation", + env_var="AZURE_SDK_TRACING_IMPLEMENTATION", + convert=convert_tracing_impl, + default=None, + ) + + tracing_should_only_propagate = PrioritizedSetting( + "tracing_should_only_propagate", + env_var="AZURE_TRACING_ONLY_PROPAGATE", + convert=convert_bool, + default=False, + ) + settings = Settings() diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 9dd0bad2ed19..9899664968fc 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -1,4 +1,3 @@ -import functools from os import environ import re @@ -8,6 +7,8 @@ def is_opencensus_installed(): + # type: () -> bool + """Returns true if opencensus is installed else returns false""" try: import opencensus @@ -17,38 +18,48 @@ def is_opencensus_installed(): def get_opencensus_wrapper(): - from azure.core.tracing.ext.opencensus import OpencensusWrapper + # type: () -> OpencensusWrapper + """Returns the OpencensusWrapper if opencensus is installed else returns None""" + if is_opencensus_installed(): + from azure.core.tracing.ext.opencensus import OpencensusWrapper - return OpencensusWrapper + return OpencensusWrapper + else: + return None -def set_span_contexts(span, span_instance=None, wrapper_class=None): +def set_span_contexts(wrapped_span, span_instance=None, impl_wrapper=None): # type: (AbstractSpan, AbstractSpan) -> None - tracing_context.current_span.set(span) - if span is not None or (span_instance is not None and wrapper_class is not None): - span_instance = span_instance or span.span_instance - span = wrapper_class or span - span.set_current_span(span_instance) + tracing_context.current_span.set(wrapped_span) + impl_wrapper = impl_wrapper or wrapped_span + tracing_context.tracing_impl.set(impl_wrapper) + if wrapped_span is not None or ( + span_instance is not None and impl_wrapper is not None + ): + span_instance = span_instance or wrapped_span.span_instance + impl_wrapper.set_current_span(span_instance) def get_parent(kwargs, *args): # type: (Any) -> Tuple(Any, Any) + """""" parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan - wrapper_class = tracing_context.convert_tracing_impl( - settings.tracing_implementation() + orig_wrapped_span = tracing_context.current_span.get() + + # wrapper class get from tracing_context, settings or assume OpencensusWrapper if opencesus is installed + wrapper_class = ( + tracing_context.tracing_impl.get() + or settings.tracing_implementation() + or get_opencensus_wrapper() ) - orig_context = tracing_context.current_span.get() + if wrapper_class is None: + return None, orig_wrapped_span, None + # parent span is given, get from my context, get from the implementation context or make our own + parent_span = ( + orig_wrapped_span if parent_span is None else wrapper_class(parent_span) + ) if parent_span is None: - parent_span = orig_context - else: - wrapper_class = wrapper_class or get_opencensus_wrapper() - parent_span = wrapper_class(parent_span) - - if wrapper_class is None and is_opencensus_installed(): - wrapper_class = get_opencensus_wrapper() - - if parent_span is None and wrapper_class is not None: current_span = wrapper_class.get_current_span() parent_span = ( wrapper_class(span=current_span) @@ -56,14 +67,11 @@ def get_parent(kwargs, *args): else wrapper_class(name="azure-sdk-for-python-first_parent_span") ) - original_span_instance = None - if wrapper_class is not None: - original_span_instance = wrapper_class.get_current_span() - - return parent_span, orig_context, original_span_instance + return parent_span, orig_wrapped_span, wrapper_class.get_current_span() def should_use_trace(parent_span): # type: (AbstractSpan, List[str], str) + """Given Parent Span Returns whether the function should be traced""" only_propagate = settings.tracing_should_only_propagate() return parent_span and not only_propagate diff --git a/sdk/core/azure-core/azure/core/tracing/context.py b/sdk/core/azure-core/azure/core/tracing/context.py index 4409ce1f6756..13c44244fb73 100644 --- a/sdk/core/azure-core/azure/core/tracing/context.py +++ b/sdk/core/azure-core/azure/core/tracing/context.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. # ------------------------------------ import threading +from azure.core.settings import settings try: from typing import TYPE_CHECKING @@ -129,7 +130,7 @@ def with_current_context(self, func): :return: The target the pass in instead of the function """ wrapped_span = tracing_context.current_span.get() - wrapper_class = self.tracing_impl.get() + wrapper_class = self.tracing_impl.get() or settings.tracing_implementation() if wrapper_class is not None: current_impl_span = wrapper_class.get_current_span() current_impl_tracer = wrapper_class.get_current_tracer() From d439df96b1bd40ee1d9178b0df6407ebf736d94f Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 13:53:43 -0700 Subject: [PATCH 013/105] added initial decorator --- sdk/core/azure-core/azure/core/settings.py | 8 +-- .../azure-core/azure/core/tracing/base.py | 58 +++++++++++++++++++ .../azure-core/azure/core/tracing/common.py | 44 +++++++++----- 3 files changed, 91 insertions(+), 19 deletions(-) create mode 100644 sdk/core/azure-core/azure/core/tracing/base.py diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 6be6b6e1264e..6ad3f5d820f8 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -112,14 +112,14 @@ def convert_logging(value): return level -def get_opencensus_wrapper(): +def _get_opencensus_wrapper(): # type: () -> OpencensusWrapper """Returns the OpencensusWrapper if opencensus is installed else returns None""" - if is_opencensus_installed(): + try: from azure.core.tracing.ext.opencensus import OpencensusWrapper return OpencensusWrapper - else: + except ImportError: return None @@ -141,7 +141,7 @@ def convert_tracing_impl(value): if isinstance(value, AbstractSpan): return value - _impl_dict = {"opencensus": get_opencensus_wrapper()} + _impl_dict = {"opencensus": _get_opencensus_wrapper()} wrapper_class = _impl_dict.get(value, None) if wrapper_class is None: diff --git a/sdk/core/azure-core/azure/core/tracing/base.py b/sdk/core/azure-core/azure/core/tracing/base.py new file mode 100644 index 000000000000..71e326207ae1 --- /dev/null +++ b/sdk/core/azure-core/azure/core/tracing/base.py @@ -0,0 +1,58 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import functools +import azure.core.tracing.common as common + +def use_distributed_traces(func): + # type: (Callable[[Any], Any]) -> Callable[[Any], Any] + @functools.wraps(func) + def wrapper_use_tracer(self, *args, **kwargs): + # type: (Any) -> Any + parent_span, original_span_from_sdk_context, original_span_instance = common.get_parent( + kwargs + ) + ans = None + if common.should_use_trace(parent_span, func.__name__): + common.set_span_contexts(parent_span) + name = self.__class__.__name__ + "." + func.__name__ + child = parent_span.span(name=name) + child.start() + common.set_span_contexts(child) + ans = func(self, *args, **kwargs) + child.finish() + common.set_span_contexts(parent_span) + if getattr(parent_span, "was_created_by_azure_sdk", False): + parent_span.finish() + common.set_span_contexts( + original_span_from_sdk_context, + span_instance=original_span_instance, + wrapper_class=parent_span, + ) + else: + ans = func(self, *args, **kwargs) + return ans + + return wrapper_use_tracer \ No newline at end of file diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 9899664968fc..626bdaa78ef1 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -1,3 +1,28 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# -------------------------------------------------------------------------- from os import environ import re @@ -6,25 +31,14 @@ from azure.core.settings import settings -def is_opencensus_installed(): - # type: () -> bool - """Returns true if opencensus is installed else returns false""" - try: - import opencensus - - return True - except ImportError: - return False - - -def get_opencensus_wrapper(): +def _get_opencensus_wrapper(): # type: () -> OpencensusWrapper """Returns the OpencensusWrapper if opencensus is installed else returns None""" - if is_opencensus_installed(): + try: from azure.core.tracing.ext.opencensus import OpencensusWrapper return OpencensusWrapper - else: + except ImportError: return None @@ -50,7 +64,7 @@ def get_parent(kwargs, *args): wrapper_class = ( tracing_context.tracing_impl.get() or settings.tracing_implementation() - or get_opencensus_wrapper() + or _get_opencensus_wrapper() ) if wrapper_class is None: return None, orig_wrapped_span, None From f256d626c9925a63ec4d915e37e38ebac0344944 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 13:54:42 -0700 Subject: [PATCH 014/105] some mroe documentation --- sdk/core/azure-core/azure/core/tracing/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 626bdaa78ef1..faada025faa6 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -56,7 +56,7 @@ def set_span_contexts(wrapped_span, span_instance=None, impl_wrapper=None): def get_parent(kwargs, *args): # type: (Any) -> Tuple(Any, Any) - """""" + """Returns the parent span that of the span that represents the function and the spans before that parent span""" parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan orig_wrapped_span = tracing_context.current_span.get() From 9067279f8d320f391e3a640bc4e749babe1f405d Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 14:43:00 -0700 Subject: [PATCH 015/105] added decorators --- sdk/core/azure-core/azure/core/settings.py | 1 - .../azure-core/azure/core/tracing/__init__.py | 12 +++- .../azure-core/azure/core/tracing/common.py | 6 +- .../core/tracing/{base.py => decorator.py} | 2 +- .../azure/core/tracing/decorator_async.py | 58 +++++++++++++++++++ 5 files changed, 73 insertions(+), 6 deletions(-) rename sdk/core/azure-core/azure/core/tracing/{base.py => decorator.py} (98%) create mode 100644 sdk/core/azure-core/azure/core/tracing/decorator_async.py diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 6ad3f5d820f8..311e1bb9aea1 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -143,7 +143,6 @@ def convert_tracing_impl(value): _impl_dict = {"opencensus": _get_opencensus_wrapper()} wrapper_class = _impl_dict.get(value, None) - if wrapper_class is None: raise ValueError( "Cannot convert {} to AbstractSpan, valid values are: {}".format( diff --git a/sdk/core/azure-core/azure/core/tracing/__init__.py b/sdk/core/azure-core/azure/core/tracing/__init__.py index b2c68383597d..bb0fd5cdfc99 100644 --- a/sdk/core/azure-core/azure/core/tracing/__init__.py +++ b/sdk/core/azure-core/azure/core/tracing/__init__.py @@ -4,6 +4,16 @@ # ------------------------------------ from azure.core.tracing.abstract_span import AbstractSpan from azure.core.tracing.context import tracing_context +from azure.core.tracing.decorator import distributed_tracing_decorator -__all__ = ["tracing_context", "AbstractSpan"] +try: + from azure.core.tracing.decorator_async import distributed_tracing_decorator_async +except ImportError: + distributed_tracing_decorator_async = lambda x: x +__all__ = [ + "tracing_context", + "AbstractSpan", + "distributed_tracing_decorator", + "distributed_tracing_decorator_async", +] diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index faada025faa6..bd4cb8ed256d 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -26,8 +26,8 @@ from os import environ import re -from azure.core.trace.context import tracing_context -from azure.core.trace.abstract_span import AbstractSpan +from azure.core.tracing.context import tracing_context +from azure.core.tracing.abstract_span import AbstractSpan from azure.core.settings import settings @@ -55,7 +55,7 @@ def set_span_contexts(wrapped_span, span_instance=None, impl_wrapper=None): def get_parent(kwargs, *args): - # type: (Any) -> Tuple(Any, Any) + # type: (Any) -> Tuple(Any, Any, Any) """Returns the parent span that of the span that represents the function and the spans before that parent span""" parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan orig_wrapped_span = tracing_context.current_span.get() diff --git a/sdk/core/azure-core/azure/core/tracing/base.py b/sdk/core/azure-core/azure/core/tracing/decorator.py similarity index 98% rename from sdk/core/azure-core/azure/core/tracing/base.py rename to sdk/core/azure-core/azure/core/tracing/decorator.py index 71e326207ae1..807604e5f076 100644 --- a/sdk/core/azure-core/azure/core/tracing/base.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator.py @@ -26,7 +26,7 @@ import functools import azure.core.tracing.common as common -def use_distributed_traces(func): +def distributed_tracing_decorator(func): # type: (Callable[[Any], Any]) -> Callable[[Any], Any] @functools.wraps(func) def wrapper_use_tracer(self, *args, **kwargs): diff --git a/sdk/core/azure-core/azure/core/tracing/decorator_async.py b/sdk/core/azure-core/azure/core/tracing/decorator_async.py new file mode 100644 index 000000000000..2c9e3510ae7d --- /dev/null +++ b/sdk/core/azure-core/azure/core/tracing/decorator_async.py @@ -0,0 +1,58 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# -------------------------------------------------------------------------- +import functools +import azure.core.tracing.common as common + +def distributed_tracing_decorator_async(func): + # type: (Callable[[Any], Any]) -> Callable[[Any], Any] + @functools.wraps(func) + async def wrapper_use_tracer(self, *args, **kwargs): + # type: (Any) -> Any + parent_span, original_span_from_sdk_context, original_span_instance = common.get_parent( + kwargs + ) + ans = None + if common.should_use_trace(parent_span, func.__name__): + common.set_span_contexts(parent_span) + name = self.__class__.__name__ + "." + func.__name__ + child = parent_span.span(name=name) + child.start() + common.set_span_contexts(child) + ans = await func(self, *args, **kwargs) + child.finish() + common.set_span_contexts(parent_span) + if getattr(parent_span, "was_created_by_azure_sdk", False): + parent_span.finish() + common.set_span_contexts( + original_span_from_sdk_context, + span_instance=original_span_instance, + wrapper_class=parent_span, + ) + else: + ans = await func(self, *args, **kwargs) + return ans + + return wrapper_use_tracer \ No newline at end of file From eee5e09b1d9fd5f5861d9ec5793c8725a25a639c Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 15:14:03 -0700 Subject: [PATCH 016/105] small change --- .../azure-core/azure/core/tracing/ext/opencensus_wrapper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index e0135e166517..7af49664642a 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -85,7 +85,7 @@ def span(self, name="child_span"): :param name: Name of the child span :return: The OpencensusWrapper that is wrapping the child span instance """ - return OpencensusWrapper(self.span_instance.span(name=name)) + return self.__class__(self.span_instance.span(name=name)) def start(self): # type: () -> None From 6050bd7d80d4eccc4c763c19c3a356877406b855 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 15:16:32 -0700 Subject: [PATCH 017/105] some minor fixes --- sdk/core/azure-core/azure/core/settings.py | 4 ++-- sdk/core/azure-core/azure/core/tracing/common.py | 4 ++-- sdk/core/azure-core/azure/core/tracing/decorator.py | 2 +- sdk/core/azure-core/azure/core/tracing/decorator_async.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 311e1bb9aea1..1c4c906aa6aa 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -116,7 +116,7 @@ def _get_opencensus_wrapper(): # type: () -> OpencensusWrapper """Returns the OpencensusWrapper if opencensus is installed else returns None""" try: - from azure.core.tracing.ext.opencensus import OpencensusWrapper + from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper return OpencensusWrapper except ImportError: @@ -138,7 +138,7 @@ def convert_tracing_impl(value): :raises ValueError: If conversion to AbstractSpan fails """ - if isinstance(value, AbstractSpan): + if issubclass(value.__class__, AbstractSpan): return value _impl_dict = {"opencensus": _get_opencensus_wrapper()} diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index bd4cb8ed256d..64ae72f9d37c 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -35,7 +35,7 @@ def _get_opencensus_wrapper(): # type: () -> OpencensusWrapper """Returns the OpencensusWrapper if opencensus is installed else returns None""" try: - from azure.core.tracing.ext.opencensus import OpencensusWrapper + from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper return OpencensusWrapper except ImportError: @@ -46,7 +46,7 @@ def set_span_contexts(wrapped_span, span_instance=None, impl_wrapper=None): # type: (AbstractSpan, AbstractSpan) -> None tracing_context.current_span.set(wrapped_span) impl_wrapper = impl_wrapper or wrapped_span - tracing_context.tracing_impl.set(impl_wrapper) + tracing_context.tracing_impl.set(impl_wrapper.__class__) if wrapped_span is not None or ( span_instance is not None and impl_wrapper is not None ): diff --git a/sdk/core/azure-core/azure/core/tracing/decorator.py b/sdk/core/azure-core/azure/core/tracing/decorator.py index 807604e5f076..0982bff4636b 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator.py @@ -49,7 +49,7 @@ def wrapper_use_tracer(self, *args, **kwargs): common.set_span_contexts( original_span_from_sdk_context, span_instance=original_span_instance, - wrapper_class=parent_span, + impl_wrapper=parent_span, ) else: ans = func(self, *args, **kwargs) diff --git a/sdk/core/azure-core/azure/core/tracing/decorator_async.py b/sdk/core/azure-core/azure/core/tracing/decorator_async.py index 2c9e3510ae7d..c38c16f680ed 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator_async.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator_async.py @@ -49,7 +49,7 @@ async def wrapper_use_tracer(self, *args, **kwargs): common.set_span_contexts( original_span_from_sdk_context, span_instance=original_span_instance, - wrapper_class=parent_span, + impl_wrapper=parent_span, ) else: ans = await func(self, *args, **kwargs) From c9faa4bc8aca93cd27f2d5009bfe2fa2930d36ac Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 15:25:25 -0700 Subject: [PATCH 018/105] test patch happening --- sdk/core/azure-core/tests/test_tracing_implementations.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 47273b2ecda8..ccaff84a3861 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -61,6 +61,7 @@ def test_no_span_passed_in_with_no_environ(self): @mock.patch.dict(os.environ, {"APPINSIGHTS_INSTRUMENTATIONKEY": "some key"}) def test_no_span_passed_in_with_environ(self): with ContextHelper() as ctx: + assert os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] == "some key" wrapped_span = OpencensusWrapper() assert wrapped_span.span_instance.name == "parent_span" tracer = OpencensusWrapper.get_current_tracer() From 6d355579ce818f852a0914c39a73a7a919ef7068 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 15:41:02 -0700 Subject: [PATCH 019/105] fix space --- sdk/core/azure-core/azure/core/tracing/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 64ae72f9d37c..42d057cbc0ec 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -35,7 +35,7 @@ def _get_opencensus_wrapper(): # type: () -> OpencensusWrapper """Returns the OpencensusWrapper if opencensus is installed else returns None""" try: - from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper + from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper return OpencensusWrapper except ImportError: From 0f6c1f711e689998f756b17808c257b7ebed4729 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 15:46:56 -0700 Subject: [PATCH 020/105] share a function --- sdk/core/azure-core/azure/core/settings.py | 4 ++-- sdk/core/azure-core/azure/core/tracing/common.py | 15 ++------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 1c4c906aa6aa..d63c318a112f 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -112,7 +112,7 @@ def convert_logging(value): return level -def _get_opencensus_wrapper(): +def get_opencensus_wrapper(): # type: () -> OpencensusWrapper """Returns the OpencensusWrapper if opencensus is installed else returns None""" try: @@ -141,7 +141,7 @@ def convert_tracing_impl(value): if issubclass(value.__class__, AbstractSpan): return value - _impl_dict = {"opencensus": _get_opencensus_wrapper()} + _impl_dict = {"opencensus": get_opencensus_wrapper()} wrapper_class = _impl_dict.get(value, None) if wrapper_class is None: raise ValueError( diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 42d057cbc0ec..f37dde45f1ed 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -28,18 +28,7 @@ from azure.core.tracing.context import tracing_context from azure.core.tracing.abstract_span import AbstractSpan -from azure.core.settings import settings - - -def _get_opencensus_wrapper(): - # type: () -> OpencensusWrapper - """Returns the OpencensusWrapper if opencensus is installed else returns None""" - try: - from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper - - return OpencensusWrapper - except ImportError: - return None +from azure.core.settings import settings, get_opencensus_wrapper def set_span_contexts(wrapped_span, span_instance=None, impl_wrapper=None): @@ -64,7 +53,7 @@ def get_parent(kwargs, *args): wrapper_class = ( tracing_context.tracing_impl.get() or settings.tracing_implementation() - or _get_opencensus_wrapper() + or get_opencensus_wrapper() ) if wrapper_class is None: return None, orig_wrapped_span, None From 2bfffe5f09e272b2054a59e7eb72556c9ac0ad6b Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 16:55:16 -0700 Subject: [PATCH 021/105] clearer logic for setting span context --- sdk/core/azure-core/azure/core/tracing/common.py | 12 ++++++------ sdk/core/azure-core/azure/core/tracing/decorator.py | 9 ++++----- .../azure-core/azure/core/tracing/decorator_async.py | 9 ++++----- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index f37dde45f1ed..067adb6cd2f2 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -31,15 +31,15 @@ from azure.core.settings import settings, get_opencensus_wrapper -def set_span_contexts(wrapped_span, span_instance=None, impl_wrapper=None): +def set_span_contexts(wrapped_span, span_instance=None): # type: (AbstractSpan, AbstractSpan) -> None tracing_context.current_span.set(wrapped_span) - impl_wrapper = impl_wrapper or wrapped_span + impl_wrapper = settings.tracing_implementation() tracing_context.tracing_impl.set(impl_wrapper.__class__) - if wrapped_span is not None or ( - span_instance is not None and impl_wrapper is not None - ): - span_instance = span_instance or wrapped_span.span_instance + if impl_wrapper is not None: + span_instance = ( + span_instance if wrapped_span is None else wrapped_span.span_instance + ) impl_wrapper.set_current_span(span_instance) diff --git a/sdk/core/azure-core/azure/core/tracing/decorator.py b/sdk/core/azure-core/azure/core/tracing/decorator.py index 0982bff4636b..223ed68cf5f2 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator.py @@ -26,6 +26,7 @@ import functools import azure.core.tracing.common as common + def distributed_tracing_decorator(func): # type: (Callable[[Any], Any]) -> Callable[[Any], Any] @functools.wraps(func) @@ -35,7 +36,7 @@ def wrapper_use_tracer(self, *args, **kwargs): kwargs ) ans = None - if common.should_use_trace(parent_span, func.__name__): + if common.should_use_trace(parent_span): common.set_span_contexts(parent_span) name = self.__class__.__name__ + "." + func.__name__ child = parent_span.span(name=name) @@ -47,12 +48,10 @@ def wrapper_use_tracer(self, *args, **kwargs): if getattr(parent_span, "was_created_by_azure_sdk", False): parent_span.finish() common.set_span_contexts( - original_span_from_sdk_context, - span_instance=original_span_instance, - impl_wrapper=parent_span, + original_span_from_sdk_context, span_instance=original_span_instance ) else: ans = func(self, *args, **kwargs) return ans - return wrapper_use_tracer \ No newline at end of file + return wrapper_use_tracer diff --git a/sdk/core/azure-core/azure/core/tracing/decorator_async.py b/sdk/core/azure-core/azure/core/tracing/decorator_async.py index c38c16f680ed..b1324b002d36 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator_async.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator_async.py @@ -26,6 +26,7 @@ import functools import azure.core.tracing.common as common + def distributed_tracing_decorator_async(func): # type: (Callable[[Any], Any]) -> Callable[[Any], Any] @functools.wraps(func) @@ -35,7 +36,7 @@ async def wrapper_use_tracer(self, *args, **kwargs): kwargs ) ans = None - if common.should_use_trace(parent_span, func.__name__): + if common.should_use_trace(parent_span): common.set_span_contexts(parent_span) name = self.__class__.__name__ + "." + func.__name__ child = parent_span.span(name=name) @@ -47,12 +48,10 @@ async def wrapper_use_tracer(self, *args, **kwargs): if getattr(parent_span, "was_created_by_azure_sdk", False): parent_span.finish() common.set_span_contexts( - original_span_from_sdk_context, - span_instance=original_span_instance, - impl_wrapper=parent_span, + original_span_from_sdk_context, span_instance=original_span_instance ) else: ans = await func(self, *args, **kwargs) return ans - return wrapper_use_tracer \ No newline at end of file + return wrapper_use_tracer From c1f6d698ae1bce9bebb9de37670258ad1a5e21d5 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 16:56:44 -0700 Subject: [PATCH 022/105] better logic --- sdk/core/azure-core/azure/core/tracing/common.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 067adb6cd2f2..db605c87ae9d 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -37,9 +37,7 @@ def set_span_contexts(wrapped_span, span_instance=None): impl_wrapper = settings.tracing_implementation() tracing_context.tracing_impl.set(impl_wrapper.__class__) if impl_wrapper is not None: - span_instance = ( - span_instance if wrapped_span is None else wrapped_span.span_instance - ) + span_instance = wrapped_span.span_instance if wrapped_span is not None impl_wrapper.set_current_span(span_instance) From 18890ea52b508630c9ca75657e8f88ad2acaacee Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 16:57:23 -0700 Subject: [PATCH 023/105] better logic --- sdk/core/azure-core/azure/core/tracing/common.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index db605c87ae9d..e906d9a2c436 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -36,8 +36,9 @@ def set_span_contexts(wrapped_span, span_instance=None): tracing_context.current_span.set(wrapped_span) impl_wrapper = settings.tracing_implementation() tracing_context.tracing_impl.set(impl_wrapper.__class__) + if wrapped_span is not None: + span_instance = wrapped_span.span_instance if impl_wrapper is not None: - span_instance = wrapped_span.span_instance if wrapped_span is not None impl_wrapper.set_current_span(span_instance) From 6a4a545f3239949c54bc217cdc91de7a6109f2ff Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 17:04:59 -0700 Subject: [PATCH 024/105] fix environ variable --- .../azure-core/tests/test_tracing_implementations.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index ccaff84a3861..5e9d1ccae1f0 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -19,18 +19,21 @@ class ContextHelper(object): - def __init__(self): + def __init__(self, environ={}): self.orig_tracer = OpencensusWrapper.get_current_tracer() self.orig_current_span = OpencensusWrapper.get_current_span() + self.os_env = mock.patch.dict(os.environ, environ) def __enter__(self): self.orig_tracer = OpencensusWrapper.get_current_tracer() self.orig_current_span = OpencensusWrapper.get_current_span() + self.os_env.start() return self def __exit__(self, exc_type, exc_val, exc_tb): OpencensusWrapper.set_current_tracer(self.orig_tracer) OpencensusWrapper.set_current_span(self.orig_current_span) + self.os_env.stop() class TestOpencensusWrapper(unittest.TestCase): @@ -58,10 +61,9 @@ def test_no_span_passed_in_with_no_environ(self): ) wrapped_span.finish() - @mock.patch.dict(os.environ, {"APPINSIGHTS_INSTRUMENTATIONKEY": "some key"}) def test_no_span_passed_in_with_environ(self): - with ContextHelper() as ctx: - assert os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] == "some key" + with ContextHelper(environ={"APPINSIGHTS_INSTRUMENTATIONKEY": "instrumentation_key"}) as ctx: + assert os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] == "instrumentation_key" wrapped_span = OpencensusWrapper() assert wrapped_span.span_instance.name == "parent_span" tracer = OpencensusWrapper.get_current_tracer() From e7b57070295a425a525178054cc4f2d0cb0a5037 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 10 Jul 2019 18:24:31 -0700 Subject: [PATCH 025/105] test the way opencensus does it --- .../tests/test_tracing_implementations.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 5e9d1ccae1f0..84a675dbfc41 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -13,9 +13,7 @@ from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler from opencensus.ext.azure.trace_exporter import AzureExporter -from opencensus.common.utils import timestamp_to_microseconds import os -import time class ContextHelper(object): @@ -103,16 +101,16 @@ def test_span(self): def test_start_finish(self): with ContextHelper() as ctx: tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - wrapped_class = OpencensusWrapper() + parent = OpencensusWrapper() + wrapped_class = parent.span() + assert wrapped_class.span_instance.start_time is None + assert wrapped_class.span_instance.end_time is None wrapped_class.start() - time.sleep(1) wrapped_class.finish() - latency = timestamp_to_microseconds( - wrapped_class.span_instance.end_time - ) - timestamp_to_microseconds(wrapped_class.span_instance.start_time) - latency = int(latency / 10000) - assert latency == 100 - + assert wrapped_class.span_instance.start_time is not None + assert wrapped_class.span_instance.end_time is not None + parent.finish() + def test_to_and_from_header(self): with ContextHelper() as ctx: wrapped_class = OpencensusWrapper() From aaf2152806214d848ab5efad1c2c3394a63a9801 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 09:52:00 -0700 Subject: [PATCH 026/105] middle of tests --- sdk/core/azure-core/azure/core/settings.py | 2 +- .../azure-core/azure/core/tracing/common.py | 2 +- .../tests/test_tracing_decorator.py | 34 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 sdk/core/azure-core/tests/test_tracing_decorator.py diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index d63c318a112f..b1e6381e913d 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -138,7 +138,7 @@ def convert_tracing_impl(value): :raises ValueError: If conversion to AbstractSpan fails """ - if issubclass(value.__class__, AbstractSpan): + if issubclass(value.__class__, AbstractSpan) or value is None: return value _impl_dict = {"opencensus": get_opencensus_wrapper()} diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index e906d9a2c436..ecd6673fe791 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -42,7 +42,7 @@ def set_span_contexts(wrapped_span, span_instance=None): impl_wrapper.set_current_span(span_instance) -def get_parent(kwargs, *args): +def get_parent(*args, **kwargs): # type: (Any) -> Tuple(Any, Any, Any) """Returns the parent span that of the span that represents the function and the spans before that parent span""" parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py new file mode 100644 index 000000000000..d88f0f9f1cc4 --- /dev/null +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -0,0 +1,34 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import unittest + +try: + from unittest import mock +except ImportError: + import mock + +from azure.core.tracing import common +from azure.core.tracing import tracing_context, AbstractSpan +from azure.core.settings import settings +from opencensus.trace import tracer as tracer_module + + +class TestCommon(unittest.TestCase): + def test_set_span_context(self): + wrapper = settings.tracing_implementation() + assert tracing_context.current_span.get() is None + assert wrapper.get_current_span() is None + parent = OpencenusWrapper() + common.set_span_contexts(parent) + assert parent.span_instance == wrapper.get_current_span() + assert tracing_context.current_span.get() == parent + + def test_get_parent(self): + parent, orig_tracing_context, orig_span_inst = common.get_parent() + assert orig_span_inst is None + + +if __name__ == "__main__": + unittest.main() From 7ccf041f020de5d23291bf7a8ba1159e4a0d1352 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 10:04:05 -0700 Subject: [PATCH 027/105] only load if opencensus has already been imported --- sdk/core/azure-core/azure/core/tracing/common.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index ecd6673fe791..cdbe757c1eee 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -25,12 +25,19 @@ # -------------------------------------------------------------------------- from os import environ import re +import sys from azure.core.tracing.context import tracing_context from azure.core.tracing.abstract_span import AbstractSpan from azure.core.settings import settings, get_opencensus_wrapper +def get_opencensus_wrapper_if_opencensus_is_inported(): + if "opencensus" not in sys.modules: + return None + return get_opencensus_wrapper() + + def set_span_contexts(wrapped_span, span_instance=None): # type: (AbstractSpan, AbstractSpan) -> None tracing_context.current_span.set(wrapped_span) @@ -52,7 +59,7 @@ def get_parent(*args, **kwargs): wrapper_class = ( tracing_context.tracing_impl.get() or settings.tracing_implementation() - or get_opencensus_wrapper() + or get_opencensus_wrapper_if_opencensus_is_inported() ) if wrapper_class is None: return None, orig_wrapped_span, None From 6a01918bd89a7a363357531cb823ac0170df9d2a Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 10:06:47 -0700 Subject: [PATCH 028/105] fix spelling mistake --- sdk/core/azure-core/azure/core/tracing/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index cdbe757c1eee..1cb2948c0486 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -32,7 +32,7 @@ from azure.core.settings import settings, get_opencensus_wrapper -def get_opencensus_wrapper_if_opencensus_is_inported(): +def get_opencensus_wrapper_if_opencensus_is_imported(): if "opencensus" not in sys.modules: return None return get_opencensus_wrapper() @@ -59,7 +59,7 @@ def get_parent(*args, **kwargs): wrapper_class = ( tracing_context.tracing_impl.get() or settings.tracing_implementation() - or get_opencensus_wrapper_if_opencensus_is_inported() + or get_opencensus_wrapper_if_opencensus_is_imported() ) if wrapper_class is None: return None, orig_wrapped_span, None From 36572c54f8d4ca6ff2f7bfca015b11cf15a8750b Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 10:12:31 -0700 Subject: [PATCH 029/105] temp --- sdk/core/azure-core/tests/test_tracing_decorator.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index d88f0f9f1cc4..bc21dff1e134 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -9,13 +9,21 @@ except ImportError: import mock +import sys from azure.core.tracing import common from azure.core.tracing import tracing_context, AbstractSpan from azure.core.settings import settings -from opencensus.trace import tracer as tracer_module +from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper class TestCommon(unittest.TestCase): + def test_get_opencensus_wrapper_if_opencensus_is_imported(self): + opencensus = sys.modules['opencensus'] + del sys.modules["opencensus"] + assert common.get_opencensus_wrapper_if_opencensus_is_imported() == None + sys.modules['opencensus'] = opencensus + assert common.get_opencensus_wrapper_if_opencensus_is_imported() is OpencensusWrapper + def test_set_span_context(self): wrapper = settings.tracing_implementation() assert tracing_context.current_span.get() is None From 280173f17e34a68931f231ff4f07ca148c54289e Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 10:36:10 -0700 Subject: [PATCH 030/105] finish writing tests for common --- .../azure-core/azure/core/tracing/common.py | 8 +- .../tests/test_tracing_decorator.py | 90 ++++++++++++++++--- 2 files changed, 82 insertions(+), 16 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 1cb2948c0486..315afd2120d3 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -50,7 +50,7 @@ def set_span_contexts(wrapped_span, span_instance=None): def get_parent(*args, **kwargs): - # type: (Any) -> Tuple(Any, Any, Any) + # type: (Any) -> Tuple(AbstractSpan, AbstractSpan, Any) """Returns the parent span that of the span that represents the function and the spans before that parent span""" parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan orig_wrapped_span = tracing_context.current_span.get() @@ -63,7 +63,7 @@ def get_parent(*args, **kwargs): ) if wrapper_class is None: return None, orig_wrapped_span, None - + original_wrapper_span_instance = wrapper_class.get_current_span() # parent span is given, get from my context, get from the implementation context or make our own parent_span = ( orig_wrapped_span if parent_span is None else wrapper_class(parent_span) @@ -76,11 +76,11 @@ def get_parent(*args, **kwargs): else wrapper_class(name="azure-sdk-for-python-first_parent_span") ) - return parent_span, orig_wrapped_span, wrapper_class.get_current_span() + return parent_span, orig_wrapped_span, original_wrapper_span_instance def should_use_trace(parent_span): # type: (AbstractSpan, List[str], str) """Given Parent Span Returns whether the function should be traced""" only_propagate = settings.tracing_should_only_propagate() - return parent_span and not only_propagate + return bool(parent_span and not only_propagate) diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index bc21dff1e134..b7047492d1ff 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -10,32 +10,98 @@ import mock import sys +import os from azure.core.tracing import common from azure.core.tracing import tracing_context, AbstractSpan from azure.core.settings import settings from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper +from opencensus.trace import tracer as tracer_module +from opencensus.trace.samplers import AlwaysOnSampler + + +class ContextHelper(object): + def __init__(self, environ={}): + self.orig_tracer = OpencensusWrapper.get_current_tracer() + self.orig_current_span = OpencensusWrapper.get_current_span() + self.os_env = mock.patch.dict(os.environ, environ) + + def __enter__(self): + self.orig_tracer = OpencensusWrapper.get_current_tracer() + self.orig_current_span = OpencensusWrapper.get_current_span() + self.os_env.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + OpencensusWrapper.set_current_tracer(self.orig_tracer) + OpencensusWrapper.set_current_span(self.orig_current_span) + self.os_env.stop() class TestCommon(unittest.TestCase): def test_get_opencensus_wrapper_if_opencensus_is_imported(self): - opencensus = sys.modules['opencensus'] + opencensus = sys.modules["opencensus"] del sys.modules["opencensus"] assert common.get_opencensus_wrapper_if_opencensus_is_imported() == None - sys.modules['opencensus'] = opencensus - assert common.get_opencensus_wrapper_if_opencensus_is_imported() is OpencensusWrapper + sys.modules["opencensus"] = opencensus + assert ( + common.get_opencensus_wrapper_if_opencensus_is_imported() + is OpencensusWrapper + ) def test_set_span_context(self): - wrapper = settings.tracing_implementation() - assert tracing_context.current_span.get() is None - assert wrapper.get_current_span() is None - parent = OpencenusWrapper() - common.set_span_contexts(parent) - assert parent.span_instance == wrapper.get_current_span() - assert tracing_context.current_span.get() == parent + with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): + wrapper = settings.tracing_implementation() + assert tracing_context.current_span.get() is None + assert wrapper.get_current_span() is None + parent = OpencensusWrapper() + common.set_span_contexts(parent) + assert parent.span_instance == wrapper.get_current_span() + assert tracing_context.current_span.get() == parent def test_get_parent(self): - parent, orig_tracing_context, orig_span_inst = common.get_parent() - assert orig_span_inst is None + with ContextHelper(): + opencensus = sys.modules["opencensus"] + del sys.modules["opencensus"] + + parent, orig_tracing_context, orig_span_inst = common.get_parent() + assert orig_span_inst is None + assert parent is None + assert orig_tracing_context is None + + sys.modules["opencensus"] = opencensus + parent, orig_tracing_context, orig_span_inst = common.get_parent() + assert orig_span_inst is None + assert parent.span_instance.name == "azure-sdk-for-python-first_parent_span" + assert orig_tracing_context is None + + tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) + parent, orig_tracing_context, orig_span_inst = common.get_parent() + assert orig_span_inst is None + assert parent.span_instance.name == "azure-sdk-for-python-first_parent_span" + assert orig_tracing_context is None + parent.finish() + + some_span = tracer.start_span(name="some_span") + new_parent, orig_tracing_context, orig_span_inst = common.get_parent() + assert orig_span_inst == some_span + assert new_parent.span_instance.name == "some_span" + assert orig_tracing_context is None + + should_be_old_parent, orig_tracing_context, orig_span_inst = common.get_parent( + parent_span=parent.span_instance + ) + assert orig_span_inst == some_span + assert should_be_old_parent.span_instance == parent.span_instance + assert orig_tracing_context is None + + def test_should_use_trace(self): + with ContextHelper(environ={"AZURE_TRACING_ONLY_PROPAGATE": "yes"}): + parent_span = OpencensusWrapper() + assert common.should_use_trace(parent_span) == False + assert common.should_use_trace(None) == False + parent_span = OpencensusWrapper() + assert common.should_use_trace(parent_span) + assert common.should_use_trace(None) == False if __name__ == "__main__": From b3f44fb41d8cdbd8738e975e1367d615341fb26d Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 10:46:49 -0700 Subject: [PATCH 031/105] charles fixes --- .../core/tracing/ext/opencensus_wrapper.py | 28 ++++++--------- sdk/core/azure-core/dev_requirements.txt | 4 +-- .../tests/test_tracing_implementations.py | 34 +++++++++---------- 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index 7af49664642a..44238899ab6f 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -10,6 +10,7 @@ from opencensus.trace.propagation import trace_context_http_header_format from opencensus.trace.samplers import ProbabilitySampler from opencensus.trace.tracers.noop_tracer import NoopTracer + try: from opencensus.ext.azure.trace_exporter import AzureExporter except: @@ -24,7 +25,7 @@ from typing import Any, Dict, Union -class OpencensusWrapper(AbstractSpan): +class OpencensusSpanWrapper(AbstractSpan): """Wraps a given Opencensus Span so that it implements azure.core.tracing.AbstractSpan""" def __init__(self, span=None, name="parent_span"): @@ -36,14 +37,14 @@ def __init__(self, span=None, name="parent_span"): :param span: The opencensus span to wrap :param name: The name of the opencensus span to create if a new span is needed """ - super(OpencensusWrapper, self).__init__(span, name) + super(OpencensusSpanWrapper, self).__init__(span, name) tracer = self.get_current_tracer() self.was_created_by_azure_sdk = False if span is None and (tracer is None or isinstance(tracer, NoopTracer)): - azure_exporter_instrumentation_key = self._get_environ( - "APPINSIGHTS_INSTRUMENTATIONKEY" + azure_exporter_instrumentation_key = os.environ.get( + "APPINSIGHTS_INSTRUMENTATIONKEY", None ) - prob = float(self._get_environ("AZURE_TRACING_SAMPLER") or 0) + prob = float(os.environ.get("AZURE_TRACING_SAMPLER", None) or 0) if azure_exporter_instrumentation_key is not None and AzureExporter: tracer = tracer_module.Tracer( exporter=AzureExporter( @@ -52,7 +53,9 @@ def __init__(self, span=None, name="parent_span"): sampler=ProbabilitySampler(prob), ) else: - tracer = tracer or tracer_module.Tracer(sampler=ProbabilitySampler(prob)) + tracer = tracer or tracer_module.Tracer( + sampler=ProbabilitySampler(prob) + ) self.was_created_by_azure_sdk = True span = span or tracer.span(name=name) @@ -67,19 +70,8 @@ def span_instance(self): """ return self._span_instance - def _get_environ(self, key): - # type: (str) -> Union[str, None] - """ - Gets the Environment Variable given. Will return None if no such environment variable is found. - :param key: The name of the environment variable - :return: The value of the variable or None if the variable does not exist. - """ - if key in os.environ: - return os.environ[key] - return None - def span(self, name="child_span"): - # type: (str) -> OpencensusWrapper + # type: (str) -> OpencensusSpanWrapper """ Create a child span for the current span and append it to the child spans list in the span instance. :param name: Name of the child span diff --git a/sdk/core/azure-core/dev_requirements.txt b/sdk/core/azure-core/dev_requirements.txt index 9e04487e4fca..566468fc811a 100644 --- a/sdk/core/azure-core/dev_requirements.txt +++ b/sdk/core/azure-core/dev_requirements.txt @@ -3,5 +3,5 @@ aiohttp>=3.0; python_version >= '3.5' aiodns>=2.0; python_version >= '3.5' typing_extensions>=3.7.2 mypy>=0.7; python_version >= '3.6' -opencensus>=0.6.0; python_version >= '2.7' -opencensus-ext-azure>=0.3.1; python_version >= '2.7' \ No newline at end of file +opencensus>=0.6.0 +opencensus-ext-azure>=0.3.1 \ No newline at end of file diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 84a675dbfc41..c9a8d975b593 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -9,7 +9,7 @@ except ImportError: import mock -from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper +from azure.core.tracing.ext.opencensus_wrapper import OpencensusSpanWrapper from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler from opencensus.ext.azure.trace_exporter import AzureExporter @@ -18,19 +18,19 @@ class ContextHelper(object): def __init__(self, environ={}): - self.orig_tracer = OpencensusWrapper.get_current_tracer() - self.orig_current_span = OpencensusWrapper.get_current_span() + self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() + self.orig_current_span = OpencensusSpanWrapper.get_current_span() self.os_env = mock.patch.dict(os.environ, environ) def __enter__(self): - self.orig_tracer = OpencensusWrapper.get_current_tracer() - self.orig_current_span = OpencensusWrapper.get_current_span() + self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() + self.orig_current_span = OpencensusSpanWrapper.get_current_span() self.os_env.start() return self def __exit__(self, exc_type, exc_val, exc_tb): - OpencensusWrapper.set_current_tracer(self.orig_tracer) - OpencensusWrapper.set_current_span(self.orig_current_span) + OpencensusSpanWrapper.set_current_tracer(self.orig_tracer) + OpencensusSpanWrapper.set_current_span(self.orig_current_span) self.os_env.stop() @@ -39,7 +39,7 @@ def test_span_passed_in(self): with ContextHelper(): tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) with tracer.start_span(name="parent") as parent: - wrapped_span = OpencensusWrapper(parent) + wrapped_span = OpencensusSpanWrapper(parent) assert wrapped_span.span_instance.name == "parent" assert ( wrapped_span.span_instance.context_tracer.trace_id @@ -50,8 +50,8 @@ def test_span_passed_in(self): def test_no_span_passed_in_with_no_environ(self): with ContextHelper(): - tracer = OpencensusWrapper.get_current_tracer() - wrapped_span = OpencensusWrapper() + tracer = OpencensusSpanWrapper.get_current_tracer() + wrapped_span = OpencensusSpanWrapper() assert wrapped_span.span_instance.name == "parent_span" assert ( wrapped_span.span_instance.context_tracer.span_context.trace_id @@ -62,9 +62,9 @@ def test_no_span_passed_in_with_no_environ(self): def test_no_span_passed_in_with_environ(self): with ContextHelper(environ={"APPINSIGHTS_INSTRUMENTATIONKEY": "instrumentation_key"}) as ctx: assert os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] == "instrumentation_key" - wrapped_span = OpencensusWrapper() + wrapped_span = OpencensusSpanWrapper() assert wrapped_span.span_instance.name == "parent_span" - tracer = OpencensusWrapper.get_current_tracer() + tracer = OpencensusSpanWrapper.get_current_tracer() assert ( wrapped_span.tracer.span_context.trace_id == tracer.span_context.trace_id @@ -79,7 +79,7 @@ def test_no_span_passed_in_with_environ(self): def test_no_span_but_in_trace(self): with ContextHelper(): tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - wrapped_span = OpencensusWrapper() + wrapped_span = OpencensusSpanWrapper() assert wrapped_span.span_instance.name == "parent_span" assert ( wrapped_span.span_instance.context_tracer.trace_id @@ -91,7 +91,7 @@ def test_no_span_but_in_trace(self): def test_span(self): with ContextHelper() as ctx: tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - wrapped_class = OpencensusWrapper() + wrapped_class = OpencensusSpanWrapper() child = wrapped_class.span() assert child.span_instance.name == "child_span" assert child.tracer.span_context.trace_id == tracer.span_context.trace_id @@ -101,7 +101,7 @@ def test_span(self): def test_start_finish(self): with ContextHelper() as ctx: tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - parent = OpencensusWrapper() + parent = OpencensusSpanWrapper() wrapped_class = parent.span() assert wrapped_class.span_instance.start_time is None assert wrapped_class.span_instance.end_time is None @@ -113,7 +113,7 @@ def test_start_finish(self): def test_to_and_from_header(self): with ContextHelper() as ctx: - wrapped_class = OpencensusWrapper() + wrapped_class = OpencensusSpanWrapper() og_header = { "traceparent": "00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01" } @@ -125,7 +125,7 @@ def test_to_and_from_header(self): def test_end_tracer(self): with ContextHelper() as ctx: tracer = mock.Mock(spec=tracer_module.Tracer) - OpencensusWrapper.end_tracer(tracer) + OpencensusSpanWrapper.end_tracer(tracer) assert tracer.finish.called From 77e0ccf87e72a6afc89e0536572f1294d667108c Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 11:25:57 -0700 Subject: [PATCH 032/105] fix tests --- sdk/core/azure-core/azure/core/settings.py | 8 +- .../azure-core/azure/core/tracing/__init__.py | 10 --- .../azure-core/tests/test_tracing_context.py | 81 ++++++++++++------- .../tests/test_tracing_decorator.py | 32 +++++--- 4 files changed, 76 insertions(+), 55 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index b1e6381e913d..97bf961942e5 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -113,12 +113,12 @@ def convert_logging(value): def get_opencensus_wrapper(): - # type: () -> OpencensusWrapper - """Returns the OpencensusWrapper if opencensus is installed else returns None""" + # type: () -> OpencensusSpanWrapper + """Returns the OpencensusSpanWrapper if opencensus is installed else returns None""" try: - from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper + from azure.core.tracing.ext.opencensus_wrapper import OpencensusSpanWrapper - return OpencensusWrapper + return OpencensusSpanWrapper except ImportError: return None diff --git a/sdk/core/azure-core/azure/core/tracing/__init__.py b/sdk/core/azure-core/azure/core/tracing/__init__.py index bb0fd5cdfc99..3c4ae3058244 100644 --- a/sdk/core/azure-core/azure/core/tracing/__init__.py +++ b/sdk/core/azure-core/azure/core/tracing/__init__.py @@ -3,17 +3,7 @@ # Licensed under the MIT License. # ------------------------------------ from azure.core.tracing.abstract_span import AbstractSpan -from azure.core.tracing.context import tracing_context -from azure.core.tracing.decorator import distributed_tracing_decorator - -try: - from azure.core.tracing.decorator_async import distributed_tracing_decorator_async -except ImportError: - distributed_tracing_decorator_async = lambda x: x __all__ = [ - "tracing_context", "AbstractSpan", - "distributed_tracing_decorator", - "distributed_tracing_decorator_async", ] diff --git a/sdk/core/azure-core/tests/test_tracing_context.py b/sdk/core/azure-core/tests/test_tracing_context.py index cd54fecde2c2..7c6f7fbd5172 100644 --- a/sdk/core/azure-core/tests/test_tracing_context.py +++ b/sdk/core/azure-core/tests/test_tracing_context.py @@ -3,52 +3,77 @@ # Licensed under the MIT License. # ------------------------------------ import unittest + try: from unittest import mock except ImportError: import mock -from azure.core.tracing import tracing_context +from azure.core.tracing.context import tracing_context from azure.core.tracing import AbstractSpan +import os + + +class ContextHelper(object): + def __init__(self, environ={}): + self.orig_sdk_context_span = tracing_context.current_span.get() + self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() + self.os_env = mock.patch.dict(os.environ, environ) + + def __enter__(self): + self.orig_sdk_context_span = tracing_context.current_span.get() + self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() + self.os_env.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + tracing_context.current_span.set(self.orig_sdk_context_span) + tracing_context.tracing_impl.set(self.orig_sdk_context_tracing_impl) + self.os_env.stop() class TestContext(unittest.TestCase): def test_get_context_class(self): - slot = tracing_context._get_context_class("temp", 1) - assert slot.get() == 1 - slot.set(2) - assert slot.get() == 2 + with ContextHelper(): + slot = tracing_context._get_context_class("temp", 1) + assert slot.get() == 1 + slot.set(2) + assert slot.get() == 2 def test_current_span(self): - assert tracing_context.current_span.get() is None - val = mock.Mock(spec=AbstractSpan) - tracing_context.current_span.set(val) - assert tracing_context.current_span.get() == val + with ContextHelper(): + assert tracing_context.current_span.get() is None + val = mock.Mock(spec=AbstractSpan) + tracing_context.current_span.set(val) + assert tracing_context.current_span.get() == val def test_tracing_impl(self): - assert tracing_context.tracing_impl.get() is None - val = AbstractSpan - tracing_context.tracing_impl.set(val) - assert tracing_context.tracing_impl.get() == val + with ContextHelper(): + assert tracing_context.tracing_impl.get() is None + val = AbstractSpan + tracing_context.tracing_impl.set(val) + assert tracing_context.tracing_impl.get() == val def test_with_current_context(self): - from threading import Thread - mock_impl = AbstractSpan - tracing_context.tracing_impl.set(mock_impl) - current_span = mock.Mock(spec=AbstractSpan) - tracing_context.current_span.set(current_span) + with ContextHelper(): + from threading import Thread - def work(): - span = tracing_context.current_span.get() - assert span == current_span - setattr(span, "in_worker", True) + mock_impl = AbstractSpan + tracing_context.tracing_impl.set(mock_impl) + current_span = mock.Mock(spec=AbstractSpan) + tracing_context.current_span.set(current_span) + + def work(): + span = tracing_context.current_span.get() + assert span == current_span + setattr(span, "in_worker", True) - thread = Thread(target=tracing_context.with_current_context(work)) - thread.start() - thread.join() + thread = Thread(target=tracing_context.with_current_context(work)) + thread.start() + thread.join() - span = tracing_context.current_span.get() - assert span == current_span - assert getattr(span, "in_worker", False) + span = tracing_context.current_span.get() + assert span == current_span + assert getattr(span, "in_worker", False) if __name__ == "__main__": diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index b7047492d1ff..0a1abf5a9100 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -11,29 +11,35 @@ import sys import os -from azure.core.tracing import common -from azure.core.tracing import tracing_context, AbstractSpan +from azure.core.tracing import common, AbstractSpan +from azure.core.tracing.context import tracing_context from azure.core.settings import settings -from azure.core.tracing.ext.opencensus_wrapper import OpencensusWrapper +from azure.core.tracing.ext.opencensus_wrapper import OpencensusSpanWrapper from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler class ContextHelper(object): def __init__(self, environ={}): - self.orig_tracer = OpencensusWrapper.get_current_tracer() - self.orig_current_span = OpencensusWrapper.get_current_span() + self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() + self.orig_current_span = OpencensusSpanWrapper.get_current_span() + self.orig_sdk_context_span = tracing_context.current_span.get() + self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() self.os_env = mock.patch.dict(os.environ, environ) def __enter__(self): - self.orig_tracer = OpencensusWrapper.get_current_tracer() - self.orig_current_span = OpencensusWrapper.get_current_span() + self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() + self.orig_current_span = OpencensusSpanWrapper.get_current_span() + self.orig_sdk_context_span = tracing_context.current_span.get() + self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() self.os_env.start() return self def __exit__(self, exc_type, exc_val, exc_tb): - OpencensusWrapper.set_current_tracer(self.orig_tracer) - OpencensusWrapper.set_current_span(self.orig_current_span) + OpencensusSpanWrapper.set_current_tracer(self.orig_tracer) + OpencensusSpanWrapper.set_current_span(self.orig_current_span) + tracing_context.current_span.set(self.orig_sdk_context_span) + tracing_context.tracing_impl.set(self.orig_sdk_context_tracing_impl) self.os_env.stop() @@ -45,7 +51,7 @@ def test_get_opencensus_wrapper_if_opencensus_is_imported(self): sys.modules["opencensus"] = opencensus assert ( common.get_opencensus_wrapper_if_opencensus_is_imported() - is OpencensusWrapper + is OpencensusSpanWrapper ) def test_set_span_context(self): @@ -53,7 +59,7 @@ def test_set_span_context(self): wrapper = settings.tracing_implementation() assert tracing_context.current_span.get() is None assert wrapper.get_current_span() is None - parent = OpencensusWrapper() + parent = OpencensusSpanWrapper() common.set_span_contexts(parent) assert parent.span_instance == wrapper.get_current_span() assert tracing_context.current_span.get() == parent @@ -96,10 +102,10 @@ def test_get_parent(self): def test_should_use_trace(self): with ContextHelper(environ={"AZURE_TRACING_ONLY_PROPAGATE": "yes"}): - parent_span = OpencensusWrapper() + parent_span = OpencensusSpanWrapper() assert common.should_use_trace(parent_span) == False assert common.should_use_trace(None) == False - parent_span = OpencensusWrapper() + parent_span = OpencensusSpanWrapper() assert common.should_use_trace(parent_span) assert common.should_use_trace(None) == False From 1e9c7dc08b2fa51eaa101111a462bc62524a0d2e Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 12:29:58 -0700 Subject: [PATCH 033/105] fix test settings --- sdk/core/azure-core/azure/core/settings.py | 4 +-- sdk/core/azure-core/tests/test_settings.py | 34 +++++++++++++++---- .../tests/test_tracing_decorator.py | 2 ++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 97bf961942e5..49eea0abb0c9 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -63,9 +63,9 @@ def convert_bool(value): return value # type: ignore val = value.lower() # type: ignore - if val in ["yes", "1", "on"]: + if val in ["yes", "1", "on", "true", "True"]: return True - if val in ["no", "0", "off"]: + if val in ["no", "0", "off", "false", "False"]: return False raise ValueError("Cannot convert {} to boolean value".format(value)) diff --git a/sdk/core/azure-core/tests/test_settings.py b/sdk/core/azure-core/tests/test_settings.py index a735ff2bfe37..e487e9e64592 100644 --- a/sdk/core/azure-core/tests/test_settings.py +++ b/sdk/core/azure-core/tests/test_settings.py @@ -74,7 +74,7 @@ def test_user_set(self): ps = m.PrioritizedSetting("foo") ps.set_value(40) assert ps() == 40 - + def test_user_unset(self): ps = m.PrioritizedSetting("foo", default=2) ps.set_value(40) @@ -137,11 +137,15 @@ class FakeSettings(object): class TestConverters(object): - @pytest.mark.parametrize("value", ["Yes", "YES", "yes", "1", "ON", "on"]) + @pytest.mark.parametrize( + "value", ["Yes", "YES", "yes", "1", "ON", "on", "true", "True", True] + ) def test_convert_bool(self, value): assert m.convert_bool(value) - @pytest.mark.parametrize("value", ["No", "NO", "no", "0", "OFF", "off"]) + @pytest.mark.parametrize( + "value", ["No", "NO", "no", "0", "OFF", "off", "false", "False", False] + ) def test_convert_bool_false(self, value): assert not m.convert_bool(value) @@ -205,10 +209,28 @@ def test_config(self): def test_defaults(self): val = m.settings.defaults - assert isinstance(val, tuple) - assert val == m.settings.config(log_level=20, tracing_enabled=False) + # assert isinstance(val, tuple) + defaults = m.settings.config( + log_level=20, + tracing_enabled=False, + tracing_implementation=None, + tracing_should_only_propagate=False, + ) + assert val.log_level == defaults.log_level + assert val.tracing_enabled == defaults.tracing_enabled + assert val.tracing_implementation == defaults.tracing_implementation + assert val.tracing_should_only_propagate == defaults.tracing_should_only_propagate os.environ["AZURE_LOG_LEVEL"] = "debug" - assert val == m.settings.config(log_level=20, tracing_enabled=False) + defaults = m.settings.config( + log_level=20, + tracing_enabled=False, + tracing_implementation=None, + tracing_should_only_propagate=False, + ) + assert val.log_level == defaults.log_level + assert val.tracing_enabled == defaults.tracing_enabled + assert val.tracing_implementation == defaults.tracing_implementation + assert val.tracing_should_only_propagate == defaults.tracing_should_only_propagate del os.environ["AZURE_LOG_LEVEL"] def test_current(self): diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 0a1abf5a9100..14b0f88b274c 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -109,6 +109,8 @@ def test_should_use_trace(self): assert common.should_use_trace(parent_span) assert common.should_use_trace(None) == False +class TestDecorator(unittest.TestCase): + pass if __name__ == "__main__": unittest.main() From d72248e9f824411808e60ca1c289903ded17916a Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 12:33:58 -0700 Subject: [PATCH 034/105] to header should not take a dict --- sdk/core/azure-core/azure/core/tracing/abstract_span.py | 2 +- .../azure-core/azure/core/tracing/ext/opencensus_wrapper.py | 2 +- sdk/core/azure-core/tests/test_tracing_implementations.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index 2826b567ca24..99d09f578284 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -43,7 +43,7 @@ def finish(self): pass @abstractmethod - def to_header(self, headers): + def to_header(self): # type: (Dict[str, str]) -> Dict[str, str] """ Returns a dictionary with the header labels and values. diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index 44238899ab6f..e21778b279b7 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -91,7 +91,7 @@ def finish(self): if self.was_created_by_azure_sdk: self.end_tracer(self.tracer) - def to_header(self, headers): + def to_header(self): # type: (Dict[str, str]) -> str """ Returns a dictionary with the header labels and values. diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index c9a8d975b593..db74edbbc6e3 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -119,7 +119,7 @@ def test_to_and_from_header(self): } tracer = wrapped_class.from_header(og_header) assert tracer.span_context.trace_id == "2578531519ed94423ceae67588eff2c9" - headers = wrapped_class.to_header({}) + headers = wrapped_class.to_header() assert headers == og_header def test_end_tracer(self): From 135d1c088400179a4685448668e05040f4e83f4c Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 12:41:18 -0700 Subject: [PATCH 035/105] from header should be class method --- .../azure/core/tracing/abstract_span.py | 17 +++++++++-------- .../core/tracing/ext/opencensus_wrapper.py | 3 ++- .../tests/test_tracing_implementations.py | 17 ++++++++++++----- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index 99d09f578284..fe3e0117fd8e 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -50,21 +50,22 @@ def to_header(self): """ pass + @property @abstractmethod - def from_header(self, headers): - # type: (Dict[str, str]) -> Any + def span_instance(self): + # type: () -> Any """ - Given a dictionary returns a new tracer with the span context - extracted from that dictionary. + Returns the span the class is wrapping. """ pass - @property + @classmethod @abstractmethod - def span_instance(self): - # type: () -> Any + def from_header(cls, headers): + # type: (Dict[str, str]) -> Any """ - Returns the span the class is wrapping. + Given a dictionary returns a new tracer with the span context + extracted from that dictionary. """ pass diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index e21778b279b7..8808d2d1cf07 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -105,7 +105,8 @@ def to_header(self): temp_headers = tracer_from_context.propagator.to_headers(ctx) return temp_headers - def from_header(self, headers): + @classmethod + def from_header(cls, headers): # type: (Dict[str, str]) -> Any """ Given a dictionary returns a new tracer with the span context extracted from that dictionary. diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index db74edbbc6e3..43ecd74f1963 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -60,7 +60,9 @@ def test_no_span_passed_in_with_no_environ(self): wrapped_span.finish() def test_no_span_passed_in_with_environ(self): - with ContextHelper(environ={"APPINSIGHTS_INSTRUMENTATIONKEY": "instrumentation_key"}) as ctx: + with ContextHelper( + environ={"APPINSIGHTS_INSTRUMENTATIONKEY": "instrumentation_key"} + ) as ctx: assert os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] == "instrumentation_key" wrapped_span = OpencensusSpanWrapper() assert wrapped_span.span_instance.name == "parent_span" @@ -110,17 +112,22 @@ def test_start_finish(self): assert wrapped_class.span_instance.start_time is not None assert wrapped_class.span_instance.end_time is not None parent.finish() - + def test_to_and_from_header(self): with ContextHelper() as ctx: - wrapped_class = OpencensusSpanWrapper() og_header = { "traceparent": "00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01" } - tracer = wrapped_class.from_header(og_header) + tracer = OpencensusSpanWrapper.from_header(og_header) assert tracer.span_context.trace_id == "2578531519ed94423ceae67588eff2c9" + wrapped_class = OpencensusSpanWrapper() headers = wrapped_class.to_header() - assert headers == og_header + new_header = { + "traceparent": "00-2578531519ed94423ceae67588eff2c9-{}-01".format( + wrapped_class.span_instance.span_id + ) + } + assert headers == new_header def test_end_tracer(self): with ContextHelper() as ctx: From 73203c26edf216d9eb02a6d4919918f52a980800 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 14:16:14 -0700 Subject: [PATCH 036/105] initial tests --- .../tests/test_tracing_decorator.py | 63 ++++++++++++++++++- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 14b0f88b274c..fe254fea985f 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -11,27 +11,35 @@ import sys import os +from azure.core import HttpRequest +from azure.core.pipeline import Pipeline, PipelineResponse +from azure.core.pipeline.policies import HTTPPolicy +from azure.core.pipeline.transport import HttpTransport from azure.core.tracing import common, AbstractSpan from azure.core.tracing.context import tracing_context +from azure.core.tracing.decorator import distributed_tracing_decorator from azure.core.settings import settings from azure.core.tracing.ext.opencensus_wrapper import OpencensusSpanWrapper from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler +import pytest class ContextHelper(object): - def __init__(self, environ={}): + def __init__(self, environ={}, tracer_to_use=None): self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() self.orig_current_span = OpencensusSpanWrapper.get_current_span() self.orig_sdk_context_span = tracing_context.current_span.get() self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() self.os_env = mock.patch.dict(os.environ, environ) + self.tracer_to_use = tracer_to_use def __enter__(self): self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() self.orig_current_span = OpencensusSpanWrapper.get_current_span() self.orig_sdk_context_span = tracing_context.current_span.get() self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() + settings.tracing_implementation.set_value(self.tracer_to_use) self.os_env.start() return self @@ -39,10 +47,45 @@ def __exit__(self, exc_type, exc_val, exc_tb): OpencensusSpanWrapper.set_current_tracer(self.orig_tracer) OpencensusSpanWrapper.set_current_span(self.orig_current_span) tracing_context.current_span.set(self.orig_sdk_context_span) - tracing_context.tracing_impl.set(self.orig_sdk_context_tracing_impl) + tracing_context.tracing_impl.set(self.orig_sdk_context_tracin + settings.tracing_implementation.unset_value() self.os_env.stop() +class MockClient: + @distributed_tracing_decorator + def __init__(self, policies=None, assert_current_span=False): + self.request = HttpRequest("GET", "https://bing.com") + if policies is None: + policies = [] + policies.append(mock.Mock(spec=HTTPPolicy, send=self.verify_request)) + self.policies = policies + self.transport = mock.Mock(spec=HttpTransport) + self.pipeline = Pipeline(self.transport, policies=policies) + + self.expected_response = mock.Mock(spec=PipelineResponse) + self.assert_current_span = assert_current_span + + def verify_request(self, request): + current_span = tracing_context.current_span.get() + if self.assert_current_span: + assert current_span is not None + return self.expected_response + + @distributed_tracing_decorator + def make_request(self, numb_times, **kwargs): + if numb_times < 1: + return None + response = self.pipeline.run(self.request, **kwargs) + self.get_foo() + self.make_request(numb_times - 1, **kwargs) + return response + + @distributed_tracing_decorator + def get_foo(self): + return 5 + + class TestCommon(unittest.TestCase): def test_get_opencensus_wrapper_if_opencensus_is_imported(self): opencensus = sys.modules["opencensus"] @@ -109,8 +152,22 @@ def test_should_use_trace(self): assert common.should_use_trace(parent_span) assert common.should_use_trace(None) == False + class TestDecorator(unittest.TestCase): - pass + def test_with_nothing_imported(self): + with ContextHelper(): + opencensus = sys.modules["opencensus"] + del sys.modules["opencensus"] + client = MockClient(assert_current_span=True) + with pytest.raises(AssertionError): + client.make_request(3) + sys.modules["opencensus"] = opencensus + + def test_with_nothing_imported(self): + with ContextHelper(): + client = MockClient(assert_current_span=True) + with pytest.raises(AssertionError): + client.make_request(3) if __name__ == "__main__": unittest.main() From e304d408e6f993f3d2a0c1f42ae1f5f8c29db1f3 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 14:21:00 -0700 Subject: [PATCH 037/105] dont create trace and get rid of end_tracer --- .../azure/core/tracing/abstract_span.py | 9 ------ .../core/tracing/ext/opencensus_wrapper.py | 31 ------------------- .../tests/test_tracing_implementations.py | 28 ++--------------- 3 files changed, 2 insertions(+), 66 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index fe3e0117fd8e..7d029e00bbb8 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -69,15 +69,6 @@ def from_header(cls, headers): """ pass - @classmethod - @abstractmethod - def end_tracer(cls, tracer): - # type: (Any) -> None - """ - If a tracer exists, exports and ends the tracer. - """ - pass - @classmethod @abstractmethod def get_current_span(cls): diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index 8808d2d1cf07..3ea58d5d46d5 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -39,24 +39,6 @@ def __init__(self, span=None, name="parent_span"): """ super(OpencensusSpanWrapper, self).__init__(span, name) tracer = self.get_current_tracer() - self.was_created_by_azure_sdk = False - if span is None and (tracer is None or isinstance(tracer, NoopTracer)): - azure_exporter_instrumentation_key = os.environ.get( - "APPINSIGHTS_INSTRUMENTATIONKEY", None - ) - prob = float(os.environ.get("AZURE_TRACING_SAMPLER", None) or 0) - if azure_exporter_instrumentation_key is not None and AzureExporter: - tracer = tracer_module.Tracer( - exporter=AzureExporter( - instrumentation_key=azure_exporter_instrumentation_key - ), - sampler=ProbabilitySampler(prob), - ) - else: - tracer = tracer or tracer_module.Tracer( - sampler=ProbabilitySampler(prob) - ) - self.was_created_by_azure_sdk = True span = span or tracer.span(name=name) self.tracer = tracer @@ -88,8 +70,6 @@ def finish(self): # type: () -> None """Set the end time for a span.""" self.span_instance.finish() - if self.was_created_by_azure_sdk: - self.end_tracer(self.tracer) def to_header(self): # type: (Dict[str, str]) -> str @@ -118,17 +98,6 @@ def from_header(cls, headers): ) return tracer_module.Tracer(span_context=ctx) - @classmethod - def end_tracer(cls, tracer): - # type: (tracer_module.Tracer) -> None - """ - If a tracer exists, exports and ends the tracer. - :param tracer: The tracer to export and end - """ - if tracer is not None: - tracer.end_span() - tracer.finish() - @classmethod def get_current_span(cls): # type: () -> Span diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 43ecd74f1963..d057c68f1e34 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -49,7 +49,7 @@ def test_span_passed_in(self): tracer.finish() def test_no_span_passed_in_with_no_environ(self): - with ContextHelper(): + with ContextHelper() as ctx: tracer = OpencensusSpanWrapper.get_current_tracer() wrapped_span = OpencensusSpanWrapper() assert wrapped_span.span_instance.name == "parent_span" @@ -57,25 +57,7 @@ def test_no_span_passed_in_with_no_environ(self): wrapped_span.span_instance.context_tracer.span_context.trace_id == tracer.span_context.trace_id ) - wrapped_span.finish() - - def test_no_span_passed_in_with_environ(self): - with ContextHelper( - environ={"APPINSIGHTS_INSTRUMENTATIONKEY": "instrumentation_key"} - ) as ctx: - assert os.environ["APPINSIGHTS_INSTRUMENTATIONKEY"] == "instrumentation_key" - wrapped_span = OpencensusSpanWrapper() - assert wrapped_span.span_instance.name == "parent_span" - tracer = OpencensusSpanWrapper.get_current_tracer() - assert ( - wrapped_span.tracer.span_context.trace_id - == tracer.span_context.trace_id - ) - assert ( - not wrapped_span.span_instance.context_tracer.span_context.trace_id - == ctx.orig_tracer.span_context.trace_id - ) - assert isinstance(tracer.exporter, AzureExporter) + assert ctx.orig_tracer == tracer wrapped_span.finish() def test_no_span_but_in_trace(self): @@ -129,12 +111,6 @@ def test_to_and_from_header(self): } assert headers == new_header - def test_end_tracer(self): - with ContextHelper() as ctx: - tracer = mock.Mock(spec=tracer_module.Tracer) - OpencensusSpanWrapper.end_tracer(tracer) - assert tracer.finish.called - if __name__ == "__main__": unittest.main() From ff5627d8c5a1d1ef27b08daee1f72313c58beb12 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 14:31:47 -0700 Subject: [PATCH 038/105] dont need to save the trace --- .../azure-core/azure/core/tracing/ext/opencensus_wrapper.py | 2 -- sdk/core/azure-core/tests/test_tracing_implementations.py | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index 3ea58d5d46d5..971e8662371a 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -40,8 +40,6 @@ def __init__(self, span=None, name="parent_span"): super(OpencensusSpanWrapper, self).__init__(span, name) tracer = self.get_current_tracer() span = span or tracer.span(name=name) - - self.tracer = tracer self._span_instance = span @property diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index d057c68f1e34..bcb28e2b0736 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -78,7 +78,10 @@ def test_span(self): wrapped_class = OpencensusSpanWrapper() child = wrapped_class.span() assert child.span_instance.name == "child_span" - assert child.tracer.span_context.trace_id == tracer.span_context.trace_id + assert ( + child.span_instance.parent_span.context_tracer.trace_id + == tracer.span_context.trace_id + ) assert len(wrapped_class.span_instance.children) == 1 assert wrapped_class.span_instance.children[0] == child.span_instance From 1e4b03ace33d9652cd10a93a555bcea66be045ff Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 15:27:23 -0700 Subject: [PATCH 039/105] more little fixes --- .../azure/core/tracing/abstract_span.py | 19 ++++++++-- .../core/tracing/ext/opencensus_wrapper.py | 38 +++++++++---------- .../tests/test_tracing_implementations.py | 34 ++++++++--------- 3 files changed, 49 insertions(+), 42 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index 7d029e00bbb8..f41027d55328 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -10,11 +10,19 @@ except AttributeError: # Python 2.7, abc exists, but not ABC ABC = abc.ABCMeta("ABC", (object,), {"__slots__": ()}) # type: ignore +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False + +if TYPE_CHECKING: + from typing import Any, Dict, Optional + class AbstractSpan(ABC): @abstractmethod def __init__(self, span=None, name=None): - # type: (Any, str) -> None + # type: (Optional[Any], Optional[str]) -> None """ If a span is given wraps the span. Else a new span is created. The optional arguement name is given to the new span. @@ -23,11 +31,11 @@ def __init__(self, span=None, name=None): @abstractmethod def span(self, name="child_span"): - # type: (str) -> AbstractSpan + # type: (Optional[str]) -> AbstractSpan """ Create a child span for the current span and append it to the child spans list. The child span must be wrapped by an implementation of AbstractSpan - """ + """ pass @abstractmethod @@ -44,7 +52,7 @@ def finish(self): @abstractmethod def to_header(self): - # type: (Dict[str, str]) -> Dict[str, str] + # type: () -> Dict[str, str] """ Returns a dictionary with the header labels and values. """ @@ -66,6 +74,9 @@ def from_header(cls, headers): """ Given a dictionary returns a new tracer with the span context extracted from that dictionary. + + :param headers: A dictionary of the request header as key value pairs. + :type headers: Dict[str, str] """ pass diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index 971e8662371a..4ef4e41049cf 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -2,19 +2,10 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -import os - from azure.core.tracing import AbstractSpan from opencensus.trace import execution_context from opencensus.trace import tracer as tracer_module, Span from opencensus.trace.propagation import trace_context_http_header_format -from opencensus.trace.samplers import ProbabilitySampler -from opencensus.trace.tracers.noop_tracer import NoopTracer - -try: - from opencensus.ext.azure.trace_exporter import AzureExporter -except: - AzureExporter = False try: from typing import TYPE_CHECKING @@ -22,22 +13,24 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Any, Dict, Union + from typing import Any, Dict, Optional -class OpencensusSpanWrapper(AbstractSpan): - """Wraps a given Opencensus Span so that it implements azure.core.tracing.AbstractSpan""" +class OpenCensusSpan(AbstractSpan): + """Wraps a given OpenCensus Span so that it implements azure.core.tracing.AbstractSpan""" def __init__(self, span=None, name="parent_span"): - # type: (Span, str) -> None + # type: (Optional[Span], Optional[str]) -> None """ If a span is not passed in, creates a new tracer. If the instrumentation key for Azure Exporter is given, will configure the azure exporter else will just create a new tracer. - :param span: The opencensus span to wrap - :param name: The name of the opencensus span to create if a new span is needed + :param span: The OpenCensus span to wrap + :type span: Span + :param name: The name of the OpenCensus span to create if a new span is needed + :type name: str """ - super(OpencensusSpanWrapper, self).__init__(span, name) + super(OpenCensusSpan, self).__init__(span, name) tracer = self.get_current_tracer() span = span or tracer.span(name=name) self._span_instance = span @@ -51,11 +44,12 @@ def span_instance(self): return self._span_instance def span(self, name="child_span"): - # type: (str) -> OpencensusSpanWrapper + # type: (Optional[str]) -> OpenCensusSpan """ Create a child span for the current span and append it to the child spans list in the span instance. :param name: Name of the child span - :return: The OpencensusWrapper that is wrapping the child span instance + :type name: str + :return: The OpenCensusWrapper that is wrapping the child span instance """ return self.__class__(self.span_instance.span(name=name)) @@ -70,7 +64,7 @@ def finish(self): self.span_instance.finish() def to_header(self): - # type: (Dict[str, str]) -> str + # type: () -> Dict[str, str] """ Returns a dictionary with the header labels and values. :param headers: A key value pair dictionary @@ -89,6 +83,7 @@ def from_header(cls, headers): """ Given a dictionary returns a new tracer with the span context extracted from that dictionary. :param headers: A key value pair dictionary + :type headers: Dict[str, str] :return: A tracer initialized with the span context extraction from headers. """ ctx = trace_context_http_header_format.TraceContextPropagator().from_headers( @@ -117,6 +112,9 @@ def set_current_span(cls, span): # type: (Span) -> None """ Set the given span as the current span in the execution context. + + :param span: The span to set the current span as + :type span: Span """ return execution_context.set_current_span(span) @@ -125,5 +123,7 @@ def set_current_tracer(cls, tracer): # type: (tracer_module.Tracer) -> None """ Set the given tracer as the current tracer in the execution context. + :param tracer: The tracer to set the current tracer as + :type tracer: Tracer """ return execution_context.set_opencensus_tracer(tracer) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index bcb28e2b0736..f923b7844e75 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -9,7 +9,7 @@ except ImportError: import mock -from azure.core.tracing.ext.opencensus_wrapper import OpencensusSpanWrapper +from azure.core.tracing.ext.opencensus_wrapper import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler from opencensus.ext.azure.trace_exporter import AzureExporter @@ -18,19 +18,19 @@ class ContextHelper(object): def __init__(self, environ={}): - self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() - self.orig_current_span = OpencensusSpanWrapper.get_current_span() + self.orig_tracer = OpenCensusSpan.get_current_tracer() + self.orig_current_span = OpenCensusSpan.get_current_span() self.os_env = mock.patch.dict(os.environ, environ) def __enter__(self): - self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() - self.orig_current_span = OpencensusSpanWrapper.get_current_span() + self.orig_tracer = OpenCensusSpan.get_current_tracer() + self.orig_current_span = OpenCensusSpan.get_current_span() self.os_env.start() return self def __exit__(self, exc_type, exc_val, exc_tb): - OpencensusSpanWrapper.set_current_tracer(self.orig_tracer) - OpencensusSpanWrapper.set_current_span(self.orig_current_span) + OpenCensusSpan.set_current_tracer(self.orig_tracer) + OpenCensusSpan.set_current_span(self.orig_current_span) self.os_env.stop() @@ -39,7 +39,7 @@ def test_span_passed_in(self): with ContextHelper(): tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) with tracer.start_span(name="parent") as parent: - wrapped_span = OpencensusSpanWrapper(parent) + wrapped_span = OpenCensusSpan(parent) assert wrapped_span.span_instance.name == "parent" assert ( wrapped_span.span_instance.context_tracer.trace_id @@ -50,8 +50,8 @@ def test_span_passed_in(self): def test_no_span_passed_in_with_no_environ(self): with ContextHelper() as ctx: - tracer = OpencensusSpanWrapper.get_current_tracer() - wrapped_span = OpencensusSpanWrapper() + tracer = OpenCensusSpan.get_current_tracer() + wrapped_span = OpenCensusSpan() assert wrapped_span.span_instance.name == "parent_span" assert ( wrapped_span.span_instance.context_tracer.span_context.trace_id @@ -63,7 +63,7 @@ def test_no_span_passed_in_with_no_environ(self): def test_no_span_but_in_trace(self): with ContextHelper(): tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - wrapped_span = OpencensusSpanWrapper() + wrapped_span = OpenCensusSpan() assert wrapped_span.span_instance.name == "parent_span" assert ( wrapped_span.span_instance.context_tracer.trace_id @@ -75,7 +75,7 @@ def test_no_span_but_in_trace(self): def test_span(self): with ContextHelper() as ctx: tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - wrapped_class = OpencensusSpanWrapper() + wrapped_class = OpenCensusSpan() child = wrapped_class.span() assert child.span_instance.name == "child_span" assert ( @@ -88,7 +88,7 @@ def test_span(self): def test_start_finish(self): with ContextHelper() as ctx: tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - parent = OpencensusSpanWrapper() + parent = OpenCensusSpan() wrapped_class = parent.span() assert wrapped_class.span_instance.start_time is None assert wrapped_class.span_instance.end_time is None @@ -103,9 +103,9 @@ def test_to_and_from_header(self): og_header = { "traceparent": "00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01" } - tracer = OpencensusSpanWrapper.from_header(og_header) + tracer = OpenCensusSpan.from_header(og_header) assert tracer.span_context.trace_id == "2578531519ed94423ceae67588eff2c9" - wrapped_class = OpencensusSpanWrapper() + wrapped_class = OpenCensusSpan() headers = wrapped_class.to_header() new_header = { "traceparent": "00-2578531519ed94423ceae67588eff2c9-{}-01".format( @@ -113,7 +113,3 @@ def test_to_and_from_header(self): ) } assert headers == new_header - - -if __name__ == "__main__": - unittest.main() From 72e0dd465684bc914f422759ad7536e87122d3ab Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 15:57:36 -0700 Subject: [PATCH 040/105] some intermediatary changes --- sdk/core/azure-core/azure/core/settings.py | 8 +- .../azure-core/azure/core/tracing/common.py | 4 +- .../azure/core/tracing/decorator.py | 1 + .../tests/test_tracing_decorator.py | 73 ++++++++++--------- 4 files changed, 44 insertions(+), 42 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 49eea0abb0c9..69865b04ce31 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -113,12 +113,12 @@ def convert_logging(value): def get_opencensus_wrapper(): - # type: () -> OpencensusSpanWrapper - """Returns the OpencensusSpanWrapper if opencensus is installed else returns None""" + # type: () -> OpenCensusSpan + """Returns the OpenCensusSpan if opencensus is installed else returns None""" try: - from azure.core.tracing.ext.opencensus_wrapper import OpencensusSpanWrapper + from azure.core.tracing.ext.opencensus_wrapper import OpenCensusSpan - return OpencensusSpanWrapper + return OpenCensusSpan except ImportError: return None diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 315afd2120d3..e4197194d8cc 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -49,7 +49,7 @@ def set_span_contexts(wrapped_span, span_instance=None): impl_wrapper.set_current_span(span_instance) -def get_parent(*args, **kwargs): +def get_parent(kwargs): # type: (Any) -> Tuple(AbstractSpan, AbstractSpan, Any) """Returns the parent span that of the span that represents the function and the spans before that parent span""" parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan @@ -80,7 +80,7 @@ def get_parent(*args, **kwargs): def should_use_trace(parent_span): - # type: (AbstractSpan, List[str], str) + # type: (AbstractSpan) -> bool """Given Parent Span Returns whether the function should be traced""" only_propagate = settings.tracing_should_only_propagate() return bool(parent_span and not only_propagate) diff --git a/sdk/core/azure-core/azure/core/tracing/decorator.py b/sdk/core/azure-core/azure/core/tracing/decorator.py index 223ed68cf5f2..bfe866396bc7 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator.py @@ -24,6 +24,7 @@ # # -------------------------------------------------------------------------- import functools + import azure.core.tracing.common as common diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index fe254fea985f..d27139cf343c 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -15,11 +15,11 @@ from azure.core.pipeline import Pipeline, PipelineResponse from azure.core.pipeline.policies import HTTPPolicy from azure.core.pipeline.transport import HttpTransport -from azure.core.tracing import common, AbstractSpan +from azure.core.tracing import common from azure.core.tracing.context import tracing_context from azure.core.tracing.decorator import distributed_tracing_decorator from azure.core.settings import settings -from azure.core.tracing.ext.opencensus_wrapper import OpencensusSpanWrapper +from azure.core.tracing.ext.opencensus_wrapper import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler import pytest @@ -27,16 +27,16 @@ class ContextHelper(object): def __init__(self, environ={}, tracer_to_use=None): - self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() - self.orig_current_span = OpencensusSpanWrapper.get_current_span() + self.orig_tracer = OpenCensusSpan.get_current_tracer() + self.orig_current_span = OpenCensusSpan.get_current_span() self.orig_sdk_context_span = tracing_context.current_span.get() self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() self.os_env = mock.patch.dict(os.environ, environ) self.tracer_to_use = tracer_to_use def __enter__(self): - self.orig_tracer = OpencensusSpanWrapper.get_current_tracer() - self.orig_current_span = OpencensusSpanWrapper.get_current_span() + self.orig_tracer = OpenCensusSpan.get_current_tracer() + self.orig_current_span = OpenCensusSpan.get_current_span() self.orig_sdk_context_span = tracing_context.current_span.get() self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() settings.tracing_implementation.set_value(self.tracer_to_use) @@ -44,10 +44,10 @@ def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): - OpencensusSpanWrapper.set_current_tracer(self.orig_tracer) - OpencensusSpanWrapper.set_current_span(self.orig_current_span) + OpenCensusSpan.set_current_tracer(self.orig_tracer) + OpenCensusSpan.set_current_span(self.orig_current_span) tracing_context.current_span.set(self.orig_sdk_context_span) - tracing_context.tracing_impl.set(self.orig_sdk_context_tracin + tracing_context.tracing_impl.set(self.orig_sdk_context_tracing_impl) settings.tracing_implementation.unset_value() self.os_env.stop() @@ -94,15 +94,16 @@ def test_get_opencensus_wrapper_if_opencensus_is_imported(self): sys.modules["opencensus"] = opencensus assert ( common.get_opencensus_wrapper_if_opencensus_is_imported() - is OpencensusSpanWrapper + is OpenCensusSpan ) def test_set_span_context(self): with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): wrapper = settings.tracing_implementation() + assert wrapper == OpenCensusSpan assert tracing_context.current_span.get() is None assert wrapper.get_current_span() is None - parent = OpencensusSpanWrapper() + parent = OpenCensusSpan() common.set_span_contexts(parent) assert parent.span_instance == wrapper.get_current_span() assert tracing_context.current_span.get() == parent @@ -112,62 +113,62 @@ def test_get_parent(self): opencensus = sys.modules["opencensus"] del sys.modules["opencensus"] - parent, orig_tracing_context, orig_span_inst = common.get_parent() + parent, orig_tracing_context, orig_span_inst = common.get_parent({}) assert orig_span_inst is None assert parent is None assert orig_tracing_context is None sys.modules["opencensus"] = opencensus - parent, orig_tracing_context, orig_span_inst = common.get_parent() + parent, orig_tracing_context, orig_span_inst = common.get_parent({}) assert orig_span_inst is None assert parent.span_instance.name == "azure-sdk-for-python-first_parent_span" assert orig_tracing_context is None tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - parent, orig_tracing_context, orig_span_inst = common.get_parent() + parent, orig_tracing_context, orig_span_inst = common.get_parent({}) assert orig_span_inst is None assert parent.span_instance.name == "azure-sdk-for-python-first_parent_span" assert orig_tracing_context is None parent.finish() some_span = tracer.start_span(name="some_span") - new_parent, orig_tracing_context, orig_span_inst = common.get_parent() + new_parent, orig_tracing_context, orig_span_inst = common.get_parent({}) assert orig_span_inst == some_span assert new_parent.span_instance.name == "some_span" assert orig_tracing_context is None - should_be_old_parent, orig_tracing_context, orig_span_inst = common.get_parent( - parent_span=parent.span_instance - ) + kwarg = {"parent_span": parent.span_instance} + should_be_old_parent, orig_tracing_context, orig_span_inst = common.get_parent(kwarg) + assert kwarg.get("parent_span") is None assert orig_span_inst == some_span assert should_be_old_parent.span_instance == parent.span_instance assert orig_tracing_context is None def test_should_use_trace(self): with ContextHelper(environ={"AZURE_TRACING_ONLY_PROPAGATE": "yes"}): - parent_span = OpencensusSpanWrapper() + parent_span = OpenCensusSpan() assert common.should_use_trace(parent_span) == False assert common.should_use_trace(None) == False - parent_span = OpencensusSpanWrapper() + parent_span = OpenCensusSpan() assert common.should_use_trace(parent_span) assert common.should_use_trace(None) == False - -class TestDecorator(unittest.TestCase): - def test_with_nothing_imported(self): - with ContextHelper(): - opencensus = sys.modules["opencensus"] - del sys.modules["opencensus"] - client = MockClient(assert_current_span=True) - with pytest.raises(AssertionError): - client.make_request(3) - sys.modules["opencensus"] = opencensus - - def test_with_nothing_imported(self): - with ContextHelper(): - client = MockClient(assert_current_span=True) - with pytest.raises(AssertionError): - client.make_request(3) +# +# class TestDecorator(unittest.TestCase): +# def test_with_nothing_imported(self): +# with ContextHelper(): +# opencensus = sys.modules["opencensus"] +# del sys.modules["opencensus"] +# client = MockClient(assert_current_span=True) +# with pytest.raises(AssertionError): +# client.make_request(3) +# sys.modules["opencensus"] = opencensus +# +# def test_with_nothing_imported(self): +# with ContextHelper(): +# client = MockClient(assert_current_span=True) +# with pytest.raises(AssertionError): +# client.make_request(3) if __name__ == "__main__": unittest.main() From aa5b671e53d25c21830be00705920c3501d28e81 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 16:04:59 -0700 Subject: [PATCH 041/105] fix type annotations --- .../azure-core/azure/core/tracing/abstract_span.py | 2 +- .../azure/core/tracing/ext/opencensus_wrapper.py | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index f41027d55328..a1e72f9d1eb9 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -76,7 +76,7 @@ def from_header(cls, headers): extracted from that dictionary. :param headers: A dictionary of the request header as key value pairs. - :type headers: Dict[str, str] + :type headers: Dict """ pass diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index 4ef4e41049cf..fc339b22346b 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -26,7 +26,7 @@ def __init__(self, span=None, name="parent_span"): configure the azure exporter else will just create a new tracer. :param span: The OpenCensus span to wrap - :type span: Span + :type span: opencensus.trace.Span :param name: The name of the OpenCensus span to create if a new span is needed :type name: str """ @@ -49,7 +49,7 @@ def span(self, name="child_span"): Create a child span for the current span and append it to the child spans list in the span instance. :param name: Name of the child span :type name: str - :return: The OpenCensusWrapper that is wrapping the child span instance + :return: The that is wrapping the child span instance """ return self.__class__(self.span_instance.span(name=name)) @@ -67,7 +67,6 @@ def to_header(self): # type: () -> Dict[str, str] """ Returns a dictionary with the header labels and values. - :param headers: A key value pair dictionary :return: A key value pair dictionary """ tracer_from_context = self.get_current_tracer() @@ -83,7 +82,7 @@ def from_header(cls, headers): """ Given a dictionary returns a new tracer with the span context extracted from that dictionary. :param headers: A key value pair dictionary - :type headers: Dict[str, str] + :type headers: Dict :return: A tracer initialized with the span context extraction from headers. """ ctx = trace_context_http_header_format.TraceContextPropagator().from_headers( @@ -114,7 +113,7 @@ def set_current_span(cls, span): Set the given span as the current span in the execution context. :param span: The span to set the current span as - :type span: Span + :type span: opencensus.trace.Span """ return execution_context.set_current_span(span) @@ -124,6 +123,6 @@ def set_current_tracer(cls, tracer): """ Set the given tracer as the current tracer in the execution context. :param tracer: The tracer to set the current tracer as - :type tracer: Tracer + :type tracer: opencensus.trace.Tracer """ return execution_context.set_opencensus_tracer(tracer) From 0ce1a2279f79a48a384543f355d4736de8450098 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 16:06:29 -0700 Subject: [PATCH 042/105] rst fix types --- sdk/core/azure-core/azure/core/tracing/abstract_span.py | 2 +- .../azure-core/azure/core/tracing/ext/opencensus_wrapper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index a1e72f9d1eb9..c90e68eb666b 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -76,7 +76,7 @@ def from_header(cls, headers): extracted from that dictionary. :param headers: A dictionary of the request header as key value pairs. - :type headers: Dict + :type headers: dict """ pass diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index fc339b22346b..7fad259b8afd 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -82,7 +82,7 @@ def from_header(cls, headers): """ Given a dictionary returns a new tracer with the span context extracted from that dictionary. :param headers: A key value pair dictionary - :type headers: Dict + :type headers: dict :return: A tracer initialized with the span context extraction from headers. """ ctx = trace_context_http_header_format.TraceContextPropagator().from_headers( From b9c7d72a58f9606cbbe3a7541bd1d5a04eeacd3b Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 16:11:31 -0700 Subject: [PATCH 043/105] add :class:annotations --- .../azure-core/azure/core/tracing/ext/opencensus_wrapper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py index 7fad259b8afd..c68cb0b751c1 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py @@ -26,7 +26,7 @@ def __init__(self, span=None, name="parent_span"): configure the azure exporter else will just create a new tracer. :param span: The OpenCensus span to wrap - :type span: opencensus.trace.Span + :type span: :class: opencensus.trace.Span :param name: The name of the OpenCensus span to create if a new span is needed :type name: str """ @@ -113,7 +113,7 @@ def set_current_span(cls, span): Set the given span as the current span in the execution context. :param span: The span to set the current span as - :type span: opencensus.trace.Span + :type span: :class: opencensus.trace.Span """ return execution_context.set_current_span(span) @@ -123,6 +123,6 @@ def set_current_tracer(cls, tracer): """ Set the given tracer as the current tracer in the execution context. :param tracer: The tracer to set the current tracer as - :type tracer: opencensus.trace.Tracer + :type tracer: :class: opencensus.trace.Tracer """ return execution_context.set_opencensus_tracer(tracer) From 57b2a57d2cf50212645d79a761f4111acc89d842 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 16:15:17 -0700 Subject: [PATCH 044/105] fix line wrapping --- .../tests/test_tracing_implementations.py | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index f923b7844e75..a1f58c28ed3e 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -41,10 +41,7 @@ def test_span_passed_in(self): with tracer.start_span(name="parent") as parent: wrapped_span = OpenCensusSpan(parent) assert wrapped_span.span_instance.name == "parent" - assert ( - wrapped_span.span_instance.context_tracer.trace_id - == tracer.span_context.trace_id - ) + assert wrapped_span.span_instance.context_tracer.trace_id == tracer.span_context.trace_id wrapped_span.finish() tracer.finish() @@ -53,10 +50,7 @@ def test_no_span_passed_in_with_no_environ(self): tracer = OpenCensusSpan.get_current_tracer() wrapped_span = OpenCensusSpan() assert wrapped_span.span_instance.name == "parent_span" - assert ( - wrapped_span.span_instance.context_tracer.span_context.trace_id - == tracer.span_context.trace_id - ) + assert wrapped_span.span_instance.context_tracer.span_context.trace_id == tracer.span_context.trace_id assert ctx.orig_tracer == tracer wrapped_span.finish() @@ -65,10 +59,7 @@ def test_no_span_but_in_trace(self): tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) wrapped_span = OpenCensusSpan() assert wrapped_span.span_instance.name == "parent_span" - assert ( - wrapped_span.span_instance.context_tracer.trace_id - == tracer.span_context.trace_id - ) + assert wrapped_span.span_instance.context_tracer.trace_id == tracer.span_context.trace_id wrapped_span.finish() tracer.finish() @@ -78,10 +69,7 @@ def test_span(self): wrapped_class = OpenCensusSpan() child = wrapped_class.span() assert child.span_instance.name == "child_span" - assert ( - child.span_instance.parent_span.context_tracer.trace_id - == tracer.span_context.trace_id - ) + assert child.span_instance.parent_span.context_tracer.trace_id == tracer.span_context.trace_id assert len(wrapped_class.span_instance.children) == 1 assert wrapped_class.span_instance.children[0] == child.span_instance From 8b1ed8f0496d6eb32b9f05ba9efd91057c499ad2 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Thu, 11 Jul 2019 17:48:31 -0700 Subject: [PATCH 045/105] added tests for decorator --- .../azure-core/azure/core/tracing/common.py | 11 +-- .../azure-core/azure/core/tracing/context.py | 4 +- .../azure-core/tests/test_tracing_context.py | 20 ++--- .../tests/test_tracing_decorator.py | 85 +++++++++++++------ 4 files changed, 69 insertions(+), 51 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index e4197194d8cc..9b30b07f6a24 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -42,7 +42,6 @@ def set_span_contexts(wrapped_span, span_instance=None): # type: (AbstractSpan, AbstractSpan) -> None tracing_context.current_span.set(wrapped_span) impl_wrapper = settings.tracing_implementation() - tracing_context.tracing_impl.set(impl_wrapper.__class__) if wrapped_span is not None: span_instance = wrapped_span.span_instance if impl_wrapper is not None: @@ -56,18 +55,12 @@ def get_parent(kwargs): orig_wrapped_span = tracing_context.current_span.get() # wrapper class get from tracing_context, settings or assume OpencensusWrapper if opencesus is installed - wrapper_class = ( - tracing_context.tracing_impl.get() - or settings.tracing_implementation() - or get_opencensus_wrapper_if_opencensus_is_imported() - ) + wrapper_class = settings.tracing_implementation() or get_opencensus_wrapper_if_opencensus_is_imported() if wrapper_class is None: return None, orig_wrapped_span, None original_wrapper_span_instance = wrapper_class.get_current_span() # parent span is given, get from my context, get from the implementation context or make our own - parent_span = ( - orig_wrapped_span if parent_span is None else wrapper_class(parent_span) - ) + parent_span = orig_wrapped_span if parent_span is None else wrapper_class(parent_span) if parent_span is None: current_span = wrapper_class.get_current_span() parent_span = ( diff --git a/sdk/core/azure-core/azure/core/tracing/context.py b/sdk/core/azure-core/azure/core/tracing/context.py index 13c44244fb73..3884ef067247 100644 --- a/sdk/core/azure-core/azure/core/tracing/context.py +++ b/sdk/core/azure-core/azure/core/tracing/context.py @@ -120,7 +120,6 @@ class TracingContext: def __init__(self): # type: () -> None self.current_span = TracingContext._get_context_class("current_span", None) - self.tracing_impl = TracingContext._get_context_class("tracing_impl", None) def with_current_context(self, func): # type: (Callable[[Any], Any]) -> Any @@ -130,7 +129,7 @@ def with_current_context(self, func): :return: The target the pass in instead of the function """ wrapped_span = tracing_context.current_span.get() - wrapper_class = self.tracing_impl.get() or settings.tracing_implementation() + wrapper_class = settings.tracing_implementation() if wrapper_class is not None: current_impl_span = wrapper_class.get_current_span() current_impl_tracer = wrapper_class.get_current_tracer() @@ -141,7 +140,6 @@ def call_with_current_context(*args, **kwargs): wrapper_class.set_current_tracer(current_impl_tracer) current_span = wrapped_span or wrapper_class(current_impl_span) self.current_span.set(current_span) - self.tracing_impl.set(wrapper_class) return func(*args, **kwargs) return call_with_current_context diff --git a/sdk/core/azure-core/tests/test_tracing_context.py b/sdk/core/azure-core/tests/test_tracing_context.py index 7c6f7fbd5172..9c1efe39f1b9 100644 --- a/sdk/core/azure-core/tests/test_tracing_context.py +++ b/sdk/core/azure-core/tests/test_tracing_context.py @@ -10,24 +10,25 @@ import mock from azure.core.tracing.context import tracing_context from azure.core.tracing import AbstractSpan +from azure.core.settings import settings import os class ContextHelper(object): - def __init__(self, environ={}): + def __init__(self, environ={}, tracer_to_use=None): self.orig_sdk_context_span = tracing_context.current_span.get() - self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() self.os_env = mock.patch.dict(os.environ, environ) + self.tracer_to_use = tracer_to_use def __enter__(self): self.orig_sdk_context_span = tracing_context.current_span.get() - self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() + settings.tracing_implementation.set_value(self.tracer_to_use) self.os_env.start() return self def __exit__(self, exc_type, exc_val, exc_tb): tracing_context.current_span.set(self.orig_sdk_context_span) - tracing_context.tracing_impl.set(self.orig_sdk_context_tracing_impl) + settings.tracing_implementation.unset_value() self.os_env.stop() @@ -46,19 +47,10 @@ def test_current_span(self): tracing_context.current_span.set(val) assert tracing_context.current_span.get() == val - def test_tracing_impl(self): - with ContextHelper(): - assert tracing_context.tracing_impl.get() is None - val = AbstractSpan - tracing_context.tracing_impl.set(val) - assert tracing_context.tracing_impl.get() == val - def test_with_current_context(self): - with ContextHelper(): + with ContextHelper(tracer_to_use=mock.Mock(AbstractSpan)): from threading import Thread - mock_impl = AbstractSpan - tracing_context.tracing_impl.set(mock_impl) current_span = mock.Mock(spec=AbstractSpan) tracing_context.current_span.set(current_span) diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index d27139cf343c..67c5e957713c 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -22,6 +22,7 @@ from azure.core.tracing.ext.opencensus_wrapper import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler +from opencensus.trace.base_exporter import Exporter import pytest @@ -30,7 +31,6 @@ def __init__(self, environ={}, tracer_to_use=None): self.orig_tracer = OpenCensusSpan.get_current_tracer() self.orig_current_span = OpenCensusSpan.get_current_span() self.orig_sdk_context_span = tracing_context.current_span.get() - self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() self.os_env = mock.patch.dict(os.environ, environ) self.tracer_to_use = tracer_to_use @@ -38,8 +38,8 @@ def __enter__(self): self.orig_tracer = OpenCensusSpan.get_current_tracer() self.orig_current_span = OpenCensusSpan.get_current_span() self.orig_sdk_context_span = tracing_context.current_span.get() - self.orig_sdk_context_tracing_impl = tracing_context.tracing_impl.get() - settings.tracing_implementation.set_value(self.tracer_to_use) + if self.tracer_to_use is not None: + settings.tracing_implementation.set_value(self.tracer_to_use) self.os_env.start() return self @@ -47,7 +47,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): OpenCensusSpan.set_current_tracer(self.orig_tracer) OpenCensusSpan.set_current_span(self.orig_current_span) tracing_context.current_span.set(self.orig_sdk_context_span) - tracing_context.tracing_impl.set(self.orig_sdk_context_tracing_impl) settings.tracing_implementation.unset_value() self.os_env.stop() @@ -92,15 +91,12 @@ def test_get_opencensus_wrapper_if_opencensus_is_imported(self): del sys.modules["opencensus"] assert common.get_opencensus_wrapper_if_opencensus_is_imported() == None sys.modules["opencensus"] = opencensus - assert ( - common.get_opencensus_wrapper_if_opencensus_is_imported() - is OpenCensusSpan - ) + assert common.get_opencensus_wrapper_if_opencensus_is_imported() is OpenCensusSpan def test_set_span_context(self): with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): wrapper = settings.tracing_implementation() - assert wrapper == OpenCensusSpan + assert wrapper is OpenCensusSpan assert tracing_context.current_span.get() is None assert wrapper.get_current_span() is None parent = OpenCensusSpan() @@ -153,22 +149,61 @@ def test_should_use_trace(self): assert common.should_use_trace(parent_span) assert common.should_use_trace(None) == False -# -# class TestDecorator(unittest.TestCase): -# def test_with_nothing_imported(self): -# with ContextHelper(): -# opencensus = sys.modules["opencensus"] -# del sys.modules["opencensus"] -# client = MockClient(assert_current_span=True) -# with pytest.raises(AssertionError): -# client.make_request(3) -# sys.modules["opencensus"] = opencensus -# -# def test_with_nothing_imported(self): -# with ContextHelper(): -# client = MockClient(assert_current_span=True) -# with pytest.raises(AssertionError): -# client.make_request(3) + +class TestDecorator(unittest.TestCase): + def test_with_nothing_imported(self): + with ContextHelper(): + opencensus = sys.modules["opencensus"] + del sys.modules["opencensus"] + client = MockClient(assert_current_span=True) + with pytest.raises(AssertionError): + client.make_request(3) + sys.modules["opencensus"] = opencensus + + def test_with_opencensus_imported_but_not_used(self): + with ContextHelper(): + client = MockClient(assert_current_span=True) + client.make_request(3) + + def test_with_opencencus_used(self): + with ContextHelper(): + trace = tracer_module.Tracer(sampler=AlwaysOnSampler()) + parent = trace.start_span(name="OverAll") + client = MockClient(policies=[]) + client.get_foo(parent_span=parent) + client.get_foo() + assert len(parent.children) == 3 + assert parent.children[0].name == "MockClient.__init__" + assert not parent.children[0].children + assert parent.children[1].name == "MockClient.get_foo" + assert not parent.children[1].children + parent.finish() + trace.finish() + + def for_test_different_settings(self): + with ContextHelper(): + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=mock.Mock(Exporter)) + with trace.start_span(name="OverAll") as parent: + client = MockClient() + client.make_request(2) + with parent.span("child") as child: + client.make_request(2, parent_span=child) + client.make_request(2) + assert len(parent.children) == 4 + assert parent.children[0].name == "MockClient.__init__" + assert parent.children[1].name == "MockClient.make_request" + assert parent.children[2].children[0].name == "MockClient.make_request" + children = parent.children[1].children + assert len(children) == 2 + trace.finish() + + def test_span_with_opencensus_complicated(self): + self.for_test_different_settings() + + def test_span_with_opencensus_passed_in_complicated(self): + with ContextHelper(tracer_to_use="opencensus"): + self.for_test_different_settings() + if __name__ == "__main__": unittest.main() From c041818ea065b58420d7e545a33f31726ac7d84a Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 03:38:39 -0700 Subject: [PATCH 046/105] rename opencensus wrapper --- .../tracing/ext/{opencensus_wrapper.py => opencensus_span.py} | 0 sdk/core/azure-core/tests/test_tracing_implementations.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename sdk/core/azure-core/azure/core/tracing/ext/{opencensus_wrapper.py => opencensus_span.py} (100%) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py similarity index 100% rename from sdk/core/azure-core/azure/core/tracing/ext/opencensus_wrapper.py rename to sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index a1f58c28ed3e..88aa3544c2dc 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -9,7 +9,7 @@ except ImportError: import mock -from azure.core.tracing.ext.opencensus_wrapper import OpenCensusSpan +from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler from opencensus.ext.azure.trace_exporter import AzureExporter From 56d2e7d53c7fccc3b0bd2d2a68fd0d4fda4a3a2d Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 03:56:33 -0700 Subject: [PATCH 047/105] intermediate changes --- sdk/core/azure-core/azure/core/settings.py | 8 +++---- .../azure-core/azure/core/tracing/common.py | 8 +++---- .../tests/test_tracing_decorator.py | 23 +++++++++++-------- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 69865b04ce31..0041351273ab 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -112,11 +112,11 @@ def convert_logging(value): return level -def get_opencensus_wrapper(): +def get_opencensus_span(): # type: () -> OpenCensusSpan """Returns the OpenCensusSpan if opencensus is installed else returns None""" try: - from azure.core.tracing.ext.opencensus_wrapper import OpenCensusSpan + from azure.core.tracing.ext.opencensus_span import OpenCensusSpan return OpenCensusSpan except ImportError: @@ -141,8 +141,8 @@ def convert_tracing_impl(value): if issubclass(value.__class__, AbstractSpan) or value is None: return value - _impl_dict = {"opencensus": get_opencensus_wrapper()} - wrapper_class = _impl_dict.get(value, None) + _impl_dict = {"opencensus": get_opencensus_span()} + wrapper_class = _impl_dict.get(value.lower(), None) if wrapper_class is None: raise ValueError( "Cannot convert {} to AbstractSpan, valid values are: {}".format( diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 9b30b07f6a24..971b56cd3a86 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -29,13 +29,13 @@ from azure.core.tracing.context import tracing_context from azure.core.tracing.abstract_span import AbstractSpan -from azure.core.settings import settings, get_opencensus_wrapper +from azure.core.settings import settings, get_opencensus_span -def get_opencensus_wrapper_if_opencensus_is_imported(): +def get_opencensus_span_if_opencensus_is_imported(): if "opencensus" not in sys.modules: return None - return get_opencensus_wrapper() + return get_opencensus_span() def set_span_contexts(wrapped_span, span_instance=None): @@ -55,7 +55,7 @@ def get_parent(kwargs): orig_wrapped_span = tracing_context.current_span.get() # wrapper class get from tracing_context, settings or assume OpencensusWrapper if opencesus is installed - wrapper_class = settings.tracing_implementation() or get_opencensus_wrapper_if_opencensus_is_imported() + wrapper_class = settings.tracing_implementation() or get_opencensus_span_if_opencensus_is_imported() if wrapper_class is None: return None, orig_wrapped_span, None original_wrapper_span_instance = wrapper_class.get_current_span() diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 67c5e957713c..4cf495624669 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -19,7 +19,7 @@ from azure.core.tracing.context import tracing_context from azure.core.tracing.decorator import distributed_tracing_decorator from azure.core.settings import settings -from azure.core.tracing.ext.opencensus_wrapper import OpenCensusSpan +from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler from opencensus.trace.base_exporter import Exporter @@ -86,12 +86,12 @@ def get_foo(self): class TestCommon(unittest.TestCase): - def test_get_opencensus_wrapper_if_opencensus_is_imported(self): + def test_get_opencensus_span_if_opencensus_is_imported(self): opencensus = sys.modules["opencensus"] del sys.modules["opencensus"] - assert common.get_opencensus_wrapper_if_opencensus_is_imported() == None + assert common.get_opencensus_span_if_opencensus_is_imported() == None sys.modules["opencensus"] = opencensus - assert common.get_opencensus_wrapper_if_opencensus_is_imported() is OpenCensusSpan + assert common.get_opencensus_span_if_opencensus_is_imported() is OpenCensusSpan def test_set_span_context(self): with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): @@ -186,13 +186,18 @@ def for_test_different_settings(self): with trace.start_span(name="OverAll") as parent: client = MockClient() client.make_request(2) - with parent.span("child") as child: - client.make_request(2, parent_span=child) - client.make_request(2) - assert len(parent.children) == 4 + with trace.span("child") as child: + client.make_request(2, parent_span=parent) + assert OpenCensusSpan.get_current_span() == child + client.make_request(2) + assert len(parent.children) == 3 assert parent.children[0].name == "MockClient.__init__" assert parent.children[1].name == "MockClient.make_request" - assert parent.children[2].children[0].name == "MockClient.make_request" + assert parent.children[1].children[0].name == "MockClient.get_foo" + assert parent.children[1].children[1].name == "MockClient.make_request" + assert parent.children[2].name == "MockClient.make_request" + assert parent.children[2].children[0].name == "MockClient.get_foo" + assert parent.children[2].children[1].name == "MockClient.make_request" children = parent.children[1].children assert len(children) == 2 trace.finish() From de6f552eb452e4ab302f485eb4d0fd08192ab161 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 10:42:26 -0700 Subject: [PATCH 048/105] use spans the right way --- .../azure/core/tracing/ext/opencensus_span.py | 18 +++++++++++------- .../tests/test_tracing_implementations.py | 13 ++++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index c68cb0b751c1..e17b783e6ca0 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -19,7 +19,7 @@ class OpenCensusSpan(AbstractSpan): """Wraps a given OpenCensus Span so that it implements azure.core.tracing.AbstractSpan""" - def __init__(self, span=None, name="parent_span"): + def __init__(self, span=None, name="span"): # type: (Optional[Span], Optional[str]) -> None """ If a span is not passed in, creates a new tracer. If the instrumentation key for Azure Exporter is given, will @@ -32,7 +32,13 @@ def __init__(self, span=None, name="parent_span"): """ super(OpenCensusSpan, self).__init__(span, name) tracer = self.get_current_tracer() - span = span or tracer.span(name=name) + if not span: + current_span = self.get_current_span() + span = tracer.span(name=name) + # The logic is needed until opencensus fixes their bug + # https://github.com/census-instrumentation/opencensus-python/issues/466 + if current_span and span not in current_span.children: + current_span._child_spans.append(span) self._span_instance = span @property @@ -43,7 +49,7 @@ def span_instance(self): """ return self._span_instance - def span(self, name="child_span"): + def span(self, name="span"): # type: (Optional[str]) -> OpenCensusSpan """ Create a child span for the current span and append it to the child spans list in the span instance. @@ -51,7 +57,7 @@ def span(self, name="child_span"): :type name: str :return: The that is wrapping the child span instance """ - return self.__class__(self.span_instance.span(name=name)) + return self.__class__(name=name) def start(self): # type: () -> None @@ -85,9 +91,7 @@ def from_header(cls, headers): :type headers: dict :return: A tracer initialized with the span context extraction from headers. """ - ctx = trace_context_http_header_format.TraceContextPropagator().from_headers( - headers - ) + ctx = trace_context_http_header_format.TraceContextPropagator().from_headers(headers) return tracer_module.Tracer(span_context=ctx) @classmethod diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 88aa3544c2dc..6274b505a436 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -49,7 +49,7 @@ def test_no_span_passed_in_with_no_environ(self): with ContextHelper() as ctx: tracer = OpenCensusSpan.get_current_tracer() wrapped_span = OpenCensusSpan() - assert wrapped_span.span_instance.name == "parent_span" + assert wrapped_span.span_instance.name == "span" assert wrapped_span.span_instance.context_tracer.span_context.trace_id == tracer.span_context.trace_id assert ctx.orig_tracer == tracer wrapped_span.finish() @@ -58,7 +58,7 @@ def test_no_span_but_in_trace(self): with ContextHelper(): tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) wrapped_span = OpenCensusSpan() - assert wrapped_span.span_instance.name == "parent_span" + assert wrapped_span.span_instance.name == "span" assert wrapped_span.span_instance.context_tracer.trace_id == tracer.span_context.trace_id wrapped_span.finish() tracer.finish() @@ -66,10 +66,14 @@ def test_no_span_but_in_trace(self): def test_span(self): with ContextHelper() as ctx: tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) + assert OpenCensusSpan.get_current_tracer() is tracer wrapped_class = OpenCensusSpan() + assert tracer.current_span() == wrapped_class.span_instance child = wrapped_class.span() - assert child.span_instance.name == "child_span" - assert child.span_instance.parent_span.context_tracer.trace_id == tracer.span_context.trace_id + assert tracer.current_span() == child.span_instance + assert child.span_instance.name == "span" + assert child.span_instance.context_tracer.trace_id == tracer.span_context.trace_id + assert child.span_instance.parent_span is wrapped_class.span_instance assert len(wrapped_class.span_instance.children) == 1 assert wrapped_class.span_instance.children[0] == child.span_instance @@ -78,7 +82,6 @@ def test_start_finish(self): tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) parent = OpenCensusSpan() wrapped_class = parent.span() - assert wrapped_class.span_instance.start_time is None assert wrapped_class.span_instance.end_time is None wrapped_class.start() wrapped_class.finish() From 691eb3d171afe398e0df823503bc923284853189 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 10:44:59 -0700 Subject: [PATCH 049/105] some formatting --- sdk/core/azure-core/tests/test_tracing_implementations.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 6274b505a436..b3c553327424 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -91,16 +91,12 @@ def test_start_finish(self): def test_to_and_from_header(self): with ContextHelper() as ctx: - og_header = { - "traceparent": "00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01" - } + og_header = {"traceparent": "00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01"} tracer = OpenCensusSpan.from_header(og_header) assert tracer.span_context.trace_id == "2578531519ed94423ceae67588eff2c9" wrapped_class = OpenCensusSpan() headers = wrapped_class.to_header() new_header = { - "traceparent": "00-2578531519ed94423ceae67588eff2c9-{}-01".format( - wrapped_class.span_instance.span_id - ) + "traceparent": "00-2578531519ed94423ceae67588eff2c9-{}-01".format(wrapped_class.span_instance.span_id) } assert headers == new_header From 20d5afe355266ecbce57fdf388291a1f4a7a31c7 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 11:14:09 -0700 Subject: [PATCH 050/105] some grammar --- sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index e17b783e6ca0..2db4b5f19605 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -55,7 +55,7 @@ def span(self, name="span"): Create a child span for the current span and append it to the child spans list in the span instance. :param name: Name of the child span :type name: str - :return: The that is wrapping the child span instance + :return: The OpenCensusSpan that is wrapping the child span instance """ return self.__class__(name=name) From 3e80b1e46f5ce974bace815fb69287ad57be01b9 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 11:45:49 -0700 Subject: [PATCH 051/105] restructure settings and make tests pass --- sdk/core/azure-core/azure/core/settings.py | 62 +++++++------------ .../azure-core/azure/core/tracing/common.py | 10 +-- sdk/core/azure-core/tests/test_settings.py | 30 ++++----- .../tests/test_tracing_decorator.py | 7 --- 4 files changed, 37 insertions(+), 72 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 0041351273ab..61e6d5ad60fb 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -32,6 +32,7 @@ import logging import os from typing import Any, Union +import sys from azure.core.tracing import AbstractSpan @@ -104,11 +105,7 @@ def convert_logging(value): val = value.upper() # type: ignore level = _levels.get(val) if not level: - raise ValueError( - "Cannot convert {} to log level, valid values are: {}".format( - value, ", ".join(_levels) - ) - ) + raise ValueError("Cannot convert {} to log level, valid values are: {}".format(value, ", ".join(_levels))) return level @@ -123,6 +120,15 @@ def get_opencensus_span(): return None +def get_opencensus_span_if_opencensus_is_imported(): + if "opencensus" not in sys.modules: + return None + return get_opencensus_span() + + +_tracing_implementation_dict = {"opencensus": get_opencensus_span()} + + def convert_tracing_impl(value): # type: (Union[str, AbstractSpan]) -> AbstractSpan """Convert a string to AbstractSpan @@ -138,15 +144,17 @@ def convert_tracing_impl(value): :raises ValueError: If conversion to AbstractSpan fails """ - if issubclass(value.__class__, AbstractSpan) or value is None: + if issubclass(value.__class__, AbstractSpan): return value - _impl_dict = {"opencensus": get_opencensus_span()} - wrapper_class = _impl_dict.get(value.lower(), None) + if value is None: + return get_opencensus_span_if_opencensus_is_imported() + + wrapper_class = _tracing_implementation_dict.get(value.lower(), None) if wrapper_class is None: raise ValueError( "Cannot convert {} to AbstractSpan, valid values are: {}".format( - value, ", ".join(_impl_dict) + value, ", ".join(_tracing_implementation_dict) ) ) @@ -181,9 +189,7 @@ class PrioritizedSetting(object): """ - def __init__( - self, name, env_var=None, system_hook=None, default=_Unset, convert=None - ): + def __init__(self, name, env_var=None, system_hook=None, default=_Unset, convert=None): self._name = name self._env_var = env_var @@ -354,11 +360,7 @@ def defaults(self): """ Return implicit default values for all settings, ignoring environment and system. """ - props = { - k: v.default - for (k, v) in self.__class__.__dict__.items() - if isinstance(v, PrioritizedSetting) - } + props = {k: v.default for (k, v) in self.__class__.__dict__.items() if isinstance(v, PrioritizedSetting)} return self._config(props) @property @@ -378,11 +380,7 @@ def config(self, **kwargs): settings.config(log_level=logging.DEBUG) """ - props = { - k: v() - for (k, v) in self.__class__.__dict__.items() - if isinstance(v, PrioritizedSetting) - } + props = {k: v() for (k, v) in self.__class__.__dict__.items() if isinstance(v, PrioritizedSetting)} props.update(kwargs) return self._config(props) @@ -391,31 +389,19 @@ def _config(self, props): # pylint: disable=no-self-use return Config(**props) log_level = PrioritizedSetting( - "log_level", - env_var="AZURE_LOG_LEVEL", - convert=convert_logging, - default=logging.INFO, + "log_level", env_var="AZURE_LOG_LEVEL", convert=convert_logging, default=logging.INFO ) tracing_enabled = PrioritizedSetting( - "tracing_enbled", - env_var="AZURE_TRACING_ENABLED", - convert=convert_bool, - default=False, + "tracing_enbled", env_var="AZURE_TRACING_ENABLED", convert=convert_bool, default=False ) tracing_implementation = PrioritizedSetting( - "tracing_implementation", - env_var="AZURE_SDK_TRACING_IMPLEMENTATION", - convert=convert_tracing_impl, - default=None, + "tracing_implementation", env_var="AZURE_SDK_TRACING_IMPLEMENTATION", convert=convert_tracing_impl, default=None ) tracing_should_only_propagate = PrioritizedSetting( - "tracing_should_only_propagate", - env_var="AZURE_TRACING_ONLY_PROPAGATE", - convert=convert_bool, - default=False, + "tracing_should_only_propagate", env_var="AZURE_TRACING_ONLY_PROPAGATE", convert=convert_bool, default=False ) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 971b56cd3a86..8412185dff49 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -25,19 +25,12 @@ # -------------------------------------------------------------------------- from os import environ import re -import sys from azure.core.tracing.context import tracing_context from azure.core.tracing.abstract_span import AbstractSpan from azure.core.settings import settings, get_opencensus_span -def get_opencensus_span_if_opencensus_is_imported(): - if "opencensus" not in sys.modules: - return None - return get_opencensus_span() - - def set_span_contexts(wrapped_span, span_instance=None): # type: (AbstractSpan, AbstractSpan) -> None tracing_context.current_span.set(wrapped_span) @@ -54,8 +47,7 @@ def get_parent(kwargs): parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan orig_wrapped_span = tracing_context.current_span.get() - # wrapper class get from tracing_context, settings or assume OpencensusWrapper if opencesus is installed - wrapper_class = settings.tracing_implementation() or get_opencensus_span_if_opencensus_is_imported() + wrapper_class = settings.tracing_implementation() if wrapper_class is None: return None, orig_wrapped_span, None original_wrapper_span_instance = wrapper_class.get_current_span() diff --git a/sdk/core/azure-core/tests/test_settings.py b/sdk/core/azure-core/tests/test_settings.py index e487e9e64592..99bbb6144d41 100644 --- a/sdk/core/azure-core/tests/test_settings.py +++ b/sdk/core/azure-core/tests/test_settings.py @@ -25,7 +25,7 @@ # -------------------------------------------------------------------------- import logging import os - +import sys import pytest # module under test @@ -101,9 +101,7 @@ def test_precedence(self): assert ps() == 10 # 1. system value - ps = m.PrioritizedSetting( - "foo", env_var="AZURE_FOO", convert=int, default=10, system_hook=lambda: 20 - ) + ps = m.PrioritizedSetting("foo", env_var="AZURE_FOO", convert=int, default=10, system_hook=lambda: 20) assert ps() == 20 # 2. environment variable @@ -137,15 +135,11 @@ class FakeSettings(object): class TestConverters(object): - @pytest.mark.parametrize( - "value", ["Yes", "YES", "yes", "1", "ON", "on", "true", "True", True] - ) + @pytest.mark.parametrize("value", ["Yes", "YES", "yes", "1", "ON", "on", "true", "True", True]) def test_convert_bool(self, value): assert m.convert_bool(value) - @pytest.mark.parametrize( - "value", ["No", "NO", "no", "0", "OFF", "off", "false", "False", False] - ) + @pytest.mark.parametrize("value", ["No", "NO", "no", "0", "OFF", "off", "false", "False", False]) def test_convert_bool_false(self, value): assert not m.convert_bool(value) @@ -173,6 +167,12 @@ def test_convert_logging_bad(self): with pytest.raises(ValueError): m.convert_logging("junk") + def test_get_opencensus_span_if_opencensus_is_imported(self): + del sys.modules['opencensus'] + assert m.get_opencensus_span_if_opencensus_is_imported() == None + import opencensus + assert m.get_opencensus_span_if_opencensus_is_imported() is not None + _standard_settings = ["log_level", "tracing_enabled"] @@ -211,10 +211,7 @@ def test_defaults(self): val = m.settings.defaults # assert isinstance(val, tuple) defaults = m.settings.config( - log_level=20, - tracing_enabled=False, - tracing_implementation=None, - tracing_should_only_propagate=False, + log_level=20, tracing_enabled=False, tracing_implementation=None, tracing_should_only_propagate=False ) assert val.log_level == defaults.log_level assert val.tracing_enabled == defaults.tracing_enabled @@ -222,10 +219,7 @@ def test_defaults(self): assert val.tracing_should_only_propagate == defaults.tracing_should_only_propagate os.environ["AZURE_LOG_LEVEL"] = "debug" defaults = m.settings.config( - log_level=20, - tracing_enabled=False, - tracing_implementation=None, - tracing_should_only_propagate=False, + log_level=20, tracing_enabled=False, tracing_implementation=None, tracing_should_only_propagate=False ) assert val.log_level == defaults.log_level assert val.tracing_enabled == defaults.tracing_enabled diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 4cf495624669..12f0b08d0654 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -86,13 +86,6 @@ def get_foo(self): class TestCommon(unittest.TestCase): - def test_get_opencensus_span_if_opencensus_is_imported(self): - opencensus = sys.modules["opencensus"] - del sys.modules["opencensus"] - assert common.get_opencensus_span_if_opencensus_is_imported() == None - sys.modules["opencensus"] = opencensus - assert common.get_opencensus_span_if_opencensus_is_imported() is OpenCensusSpan - def test_set_span_context(self): with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): wrapper = settings.tracing_implementation() From 3f9f4aec9045aef6f7da6bbc43b025ca33651368 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 12:27:14 -0700 Subject: [PATCH 052/105] rename get_parent --- sdk/core/azure-core/azure/core/tracing/common.py | 2 +- sdk/core/azure-core/azure/core/tracing/decorator.py | 6 ++---- .../azure-core/azure/core/tracing/decorator_async.py | 9 ++++----- sdk/core/azure-core/tests/test_tracing_decorator.py | 12 ++++++------ 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 8412185dff49..8777e0a4c9eb 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -41,7 +41,7 @@ def set_span_contexts(wrapped_span, span_instance=None): impl_wrapper.set_current_span(span_instance) -def get_parent(kwargs): +def get_parent_and_original_contexts(kwargs): # type: (Any) -> Tuple(AbstractSpan, AbstractSpan, Any) """Returns the parent span that of the span that represents the function and the spans before that parent span""" parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan diff --git a/sdk/core/azure-core/azure/core/tracing/decorator.py b/sdk/core/azure-core/azure/core/tracing/decorator.py index bfe866396bc7..1d2a483fb8ef 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator.py @@ -33,7 +33,7 @@ def distributed_tracing_decorator(func): @functools.wraps(func) def wrapper_use_tracer(self, *args, **kwargs): # type: (Any) -> Any - parent_span, original_span_from_sdk_context, original_span_instance = common.get_parent( + parent_span, original_span_from_sdk_context, original_span_instance = common.get_parent_and_original_contexts( kwargs ) ans = None @@ -48,9 +48,7 @@ def wrapper_use_tracer(self, *args, **kwargs): common.set_span_contexts(parent_span) if getattr(parent_span, "was_created_by_azure_sdk", False): parent_span.finish() - common.set_span_contexts( - original_span_from_sdk_context, span_instance=original_span_instance - ) + common.set_span_contexts(original_span_from_sdk_context, span_instance=original_span_instance) else: ans = func(self, *args, **kwargs) return ans diff --git a/sdk/core/azure-core/azure/core/tracing/decorator_async.py b/sdk/core/azure-core/azure/core/tracing/decorator_async.py index b1324b002d36..a12949e80251 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator_async.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator_async.py @@ -24,15 +24,16 @@ # # -------------------------------------------------------------------------- import functools + import azure.core.tracing.common as common -def distributed_tracing_decorator_async(func): +def distributed_tracing_decorator(func): # type: (Callable[[Any], Any]) -> Callable[[Any], Any] @functools.wraps(func) async def wrapper_use_tracer(self, *args, **kwargs): # type: (Any) -> Any - parent_span, original_span_from_sdk_context, original_span_instance = common.get_parent( + parent_span, original_span_from_sdk_context, original_span_instance = common.get_parent_and_original_contexts( kwargs ) ans = None @@ -47,9 +48,7 @@ async def wrapper_use_tracer(self, *args, **kwargs): common.set_span_contexts(parent_span) if getattr(parent_span, "was_created_by_azure_sdk", False): parent_span.finish() - common.set_span_contexts( - original_span_from_sdk_context, span_instance=original_span_instance - ) + common.set_span_contexts(original_span_from_sdk_context, span_instance=original_span_instance) else: ans = await func(self, *args, **kwargs) return ans diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 12f0b08d0654..801c8ce8c67b 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -97,37 +97,37 @@ def test_set_span_context(self): assert parent.span_instance == wrapper.get_current_span() assert tracing_context.current_span.get() == parent - def test_get_parent(self): + def test_get_parent_and_original_contexts(self): with ContextHelper(): opencensus = sys.modules["opencensus"] del sys.modules["opencensus"] - parent, orig_tracing_context, orig_span_inst = common.get_parent({}) + parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts({}) assert orig_span_inst is None assert parent is None assert orig_tracing_context is None sys.modules["opencensus"] = opencensus - parent, orig_tracing_context, orig_span_inst = common.get_parent({}) + parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts({}) assert orig_span_inst is None assert parent.span_instance.name == "azure-sdk-for-python-first_parent_span" assert orig_tracing_context is None tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - parent, orig_tracing_context, orig_span_inst = common.get_parent({}) + parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts({}) assert orig_span_inst is None assert parent.span_instance.name == "azure-sdk-for-python-first_parent_span" assert orig_tracing_context is None parent.finish() some_span = tracer.start_span(name="some_span") - new_parent, orig_tracing_context, orig_span_inst = common.get_parent({}) + new_parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts({}) assert orig_span_inst == some_span assert new_parent.span_instance.name == "some_span" assert orig_tracing_context is None kwarg = {"parent_span": parent.span_instance} - should_be_old_parent, orig_tracing_context, orig_span_inst = common.get_parent(kwarg) + should_be_old_parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts(kwarg) assert kwarg.get("parent_span") is None assert orig_span_inst == some_span assert should_be_old_parent.span_instance == parent.span_instance From 412bf7a01a53cfa49731193aaf7aafcf4192e123 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 12:29:40 -0700 Subject: [PATCH 053/105] fix typings --- sdk/core/azure-core/azure/core/settings.py | 10 +++++++++- sdk/core/azure-core/azure/core/tracing/common.py | 11 ++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 61e6d5ad60fb..fd3a22c8877f 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -31,9 +31,17 @@ from collections import namedtuple import logging import os -from typing import Any, Union import sys +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False + +if TYPE_CHECKING: + from typing import Any, Union + + from azure.core.tracing import AbstractSpan diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 8777e0a4c9eb..754467a0aced 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -31,8 +31,17 @@ from azure.core.settings import settings, get_opencensus_span +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False + +if TYPE_CHECKING: + from typing import Any, Optional + + def set_span_contexts(wrapped_span, span_instance=None): - # type: (AbstractSpan, AbstractSpan) -> None + # type: (AbstractSpan, Optional[AbstractSpan]) -> None tracing_context.current_span.set(wrapped_span) impl_wrapper = settings.tracing_implementation() if wrapped_span is not None: From 3c6ab1cd37ff30141fe38924a4632e35d44e1ca6 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 14:03:22 -0700 Subject: [PATCH 054/105] use protocol and from_headers becomes links --- .../azure/core/tracing/abstract_span.py | 35 ++++++------------- .../azure/core/tracing/ext/opencensus_span.py | 19 +++++----- .../tests/test_tracing_implementations.py | 6 ++-- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index c90e68eb666b..3ced8a060ad6 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -2,14 +2,6 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -import abc -from abc import abstractmethod - -try: - ABC = abc.ABC -except AttributeError: # Python 2.7, abc exists, but not ABC - ABC = abc.ABCMeta("ABC", (object,), {"__slots__": ()}) # type: ignore - try: from typing import TYPE_CHECKING except ImportError: @@ -18,9 +10,15 @@ if TYPE_CHECKING: from typing import Any, Dict, Optional +try: + from typing_extensions import Protocol +except ImportError: + Protocol = object + + +class AbstractSpan(Protocol): + """Wraps a span from a distributed tracing implementation.""" -class AbstractSpan(ABC): - @abstractmethod def __init__(self, span=None, name=None): # type: (Optional[Any], Optional[str]) -> None """ @@ -29,7 +27,6 @@ def __init__(self, span=None, name=None): """ pass - @abstractmethod def span(self, name="child_span"): # type: (Optional[str]) -> AbstractSpan """ @@ -38,19 +35,16 @@ def span(self, name="child_span"): """ pass - @abstractmethod def start(self): # type: () -> None """Set the start time for a span.""" pass - @abstractmethod def finish(self): # type: () -> None """Set the end time for a span.""" pass - @abstractmethod def to_header(self): # type: () -> Dict[str, str] """ @@ -59,7 +53,6 @@ def to_header(self): pass @property - @abstractmethod def span_instance(self): # type: () -> Any """ @@ -68,12 +61,10 @@ def span_instance(self): pass @classmethod - @abstractmethod - def from_header(cls, headers): - # type: (Dict[str, str]) -> Any + def link(cls, headers): + # type: (Dict[str, str]) -> None """ - Given a dictionary returns a new tracer with the span context - extracted from that dictionary. + Given a dictionary, extracts the context and links the context to the current tracer. :param headers: A dictionary of the request header as key value pairs. :type headers: dict @@ -81,7 +72,6 @@ def from_header(cls, headers): pass @classmethod - @abstractmethod def get_current_span(cls): # type: () -> Any """ @@ -90,7 +80,6 @@ def get_current_span(cls): pass @classmethod - @abstractmethod def get_current_tracer(cls): # type: () -> Any """ @@ -99,7 +88,6 @@ def get_current_tracer(cls): pass @classmethod - @abstractmethod def set_current_span(cls, span): # type: (Any) -> None """ @@ -108,7 +96,6 @@ def set_current_span(cls, span): pass @classmethod - @abstractmethod def set_current_tracer(cls, tracer): # type: (Any) -> None """ diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 2db4b5f19605..1602c4d05923 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -2,9 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -from azure.core.tracing import AbstractSpan -from opencensus.trace import execution_context -from opencensus.trace import tracer as tracer_module, Span +from opencensus.trace import tracer as tracer_module, Span, execution_context +from opencensus.trace.link import Link from opencensus.trace.propagation import trace_context_http_header_format try: @@ -16,7 +15,7 @@ from typing import Any, Dict, Optional -class OpenCensusSpan(AbstractSpan): +class OpenCensusSpan(object): """Wraps a given OpenCensus Span so that it implements azure.core.tracing.AbstractSpan""" def __init__(self, span=None, name="span"): @@ -30,7 +29,6 @@ def __init__(self, span=None, name="span"): :param name: The name of the OpenCensus span to create if a new span is needed :type name: str """ - super(OpenCensusSpan, self).__init__(span, name) tracer = self.get_current_tracer() if not span: current_span = self.get_current_span() @@ -83,16 +81,17 @@ def to_header(self): return temp_headers @classmethod - def from_header(cls, headers): - # type: (Dict[str, str]) -> Any + def link(cls, headers): + # type: (Dict[str, str]) -> None """ - Given a dictionary returns a new tracer with the span context extracted from that dictionary. + Given a dictionary, extracts the context and links the context to the current tracer. + :param headers: A key value pair dictionary :type headers: dict - :return: A tracer initialized with the span context extraction from headers. """ ctx = trace_context_http_header_format.TraceContextPropagator().from_headers(headers) - return tracer_module.Tracer(span_context=ctx) + current_span = cls.get_current_span() + current_span.add_link(Link(ctx.trace_id, ctx.span_id)) @classmethod def get_current_span(cls): diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index b3c553327424..5f086b5e5000 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -89,11 +89,11 @@ def test_start_finish(self): assert wrapped_class.span_instance.end_time is not None parent.finish() - def test_to_and_from_header(self): + def test_to_header(self): with ContextHelper() as ctx: og_header = {"traceparent": "00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01"} - tracer = OpenCensusSpan.from_header(og_header) - assert tracer.span_context.trace_id == "2578531519ed94423ceae67588eff2c9" + ctx = tracer_module.trace_context_http_header_format.TraceContextPropagator().from_headers(og_header) + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), span_context=ctx) wrapped_class = OpenCensusSpan() headers = wrapped_class.to_header() new_header = { From de41353039be82000c58a6aa0053156b73449450 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 14:08:45 -0700 Subject: [PATCH 055/105] ramifications of opencensus wrapper being a protocol --- sdk/core/azure-core/azure/core/settings.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index fd3a22c8877f..0a32748d4be1 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -31,6 +31,7 @@ from collections import namedtuple import logging import os +import six import sys try: @@ -152,21 +153,11 @@ def convert_tracing_impl(value): :raises ValueError: If conversion to AbstractSpan fails """ - if issubclass(value.__class__, AbstractSpan): - return value - if value is None: return get_opencensus_span_if_opencensus_is_imported() - wrapper_class = _tracing_implementation_dict.get(value.lower(), None) - if wrapper_class is None: - raise ValueError( - "Cannot convert {} to AbstractSpan, valid values are: {}".format( - value, ", ".join(_tracing_implementation_dict) - ) - ) - - return wrapper_class + value = value.lower() if isinstance(value, six.string_types) else value + return _tracing_implementation_dict.get(value, value) class PrioritizedSetting(object): From db2f86573f57dcca11bd605103b4b246da7f8e57 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 14:15:43 -0700 Subject: [PATCH 056/105] add tests for link --- .../azure-core/tests/test_tracing_implementations.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 5f086b5e5000..ac395ebd04b8 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -100,3 +100,14 @@ def test_to_header(self): "traceparent": "00-2578531519ed94423ceae67588eff2c9-{}-01".format(wrapped_class.span_instance.span_id) } assert headers == new_header + + def test_links(self): + with ContextHelper() as ctx: + trace = tracer_module.Tracer(sampler=AlwaysOnSampler()) + og_header = {"traceparent": "00-2578531519ed94423ceae67588eff2c9-231ebdc614cb9ddd-01"} + wrapped_class = OpenCensusSpan() + OpenCensusSpan.link(og_header) + assert len(wrapped_class.span_instance.links) == 1 + link = wrapped_class.span_instance.links[0] + assert link.trace_id == "2578531519ed94423ceae67588eff2c9" + assert link.span_id == "231ebdc614cb9ddd" From 27c987605e0897ff3d2fa0f179f7ff351dc93384 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 15:03:27 -0700 Subject: [PATCH 057/105] added async tests --- .../azure/core/tracing/decorator_async.py | 2 +- .../test_tracing_decorator_async.py | 149 ++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py diff --git a/sdk/core/azure-core/azure/core/tracing/decorator_async.py b/sdk/core/azure-core/azure/core/tracing/decorator_async.py index a12949e80251..c7fbb714084d 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator_async.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator_async.py @@ -28,7 +28,7 @@ import azure.core.tracing.common as common -def distributed_tracing_decorator(func): +def distributed_tracing_decorator_async(func): # type: (Callable[[Any], Any]) -> Callable[[Any], Any] @functools.wraps(func) async def wrapper_use_tracer(self, *args, **kwargs): diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py new file mode 100644 index 000000000000..d533516bdbcd --- /dev/null +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -0,0 +1,149 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +import unittest + +try: + from unittest import mock +except ImportError: + import mock + +import sys +import os +from azure.core import HttpRequest +from azure.core.pipeline import Pipeline, PipelineResponse +from azure.core.pipeline.policies import HTTPPolicy +from azure.core.pipeline.transport import HttpTransport +from azure.core.tracing.context import tracing_context +from azure.core.tracing.decorator import distributed_tracing_decorator +from azure.core.tracing.decorator_async import distributed_tracing_decorator_async +from azure.core.settings import settings +from azure.core.tracing.ext.opencensus_span import OpenCensusSpan +from opencensus.trace import tracer as tracer_module +from opencensus.trace.samplers import AlwaysOnSampler +from opencensus.trace.base_exporter import Exporter +import pytest + + +class ContextHelper(object): + def __init__(self, environ={}, tracer_to_use=None): + self.orig_tracer = OpenCensusSpan.get_current_tracer() + self.orig_current_span = OpenCensusSpan.get_current_span() + self.orig_sdk_context_span = tracing_context.current_span.get() + self.os_env = mock.patch.dict(os.environ, environ) + self.tracer_to_use = tracer_to_use + + def __enter__(self): + self.orig_tracer = OpenCensusSpan.get_current_tracer() + self.orig_current_span = OpenCensusSpan.get_current_span() + self.orig_sdk_context_span = tracing_context.current_span.get() + if self.tracer_to_use is not None: + settings.tracing_implementation.set_value(self.tracer_to_use) + self.os_env.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + OpenCensusSpan.set_current_tracer(self.orig_tracer) + OpenCensusSpan.set_current_span(self.orig_current_span) + tracing_context.current_span.set(self.orig_sdk_context_span) + settings.tracing_implementation.unset_value() + self.os_env.stop() + + +class MockClient: + @distributed_tracing_decorator + def __init__(self, policies=None, assert_current_span=False): + self.request = HttpRequest("GET", "https://bing.com") + if policies is None: + policies = [] + policies.append(mock.Mock(spec=HTTPPolicy, send=self.verify_request)) + self.policies = policies + self.transport = mock.Mock(spec=HttpTransport) + self.pipeline = Pipeline(self.transport, policies=policies) + + self.expected_response = mock.Mock(spec=PipelineResponse) + self.assert_current_span = assert_current_span + + def verify_request(self, request): + current_span = tracing_context.current_span.get() + if self.assert_current_span: + assert current_span is not None + return self.expected_response + + @distributed_tracing_decorator_async + async def make_request(self, numb_times, **kwargs): + if numb_times < 1: + return None + response = self.pipeline.run(self.request, **kwargs) + await self.get_foo() + await self.make_request(numb_times - 1, **kwargs) + return response + + @distributed_tracing_decorator_async + async def get_foo(self): + return 5 + +@pytest.mark.asyncio +async def test_with_nothing_imported(): + with ContextHelper(): + opencensus = sys.modules["opencensus"] + del sys.modules["opencensus"] + client = MockClient(assert_current_span=True) + with pytest.raises(AssertionError): + await client.make_request(3) + sys.modules["opencensus"] = opencensus + +@pytest.mark.asyncio +async def test_with_opencensus_imported_but_not_used(): + with ContextHelper(): + client = MockClient(assert_current_span=True) + await client.make_request(3) + +@pytest.mark.asyncio +async def test_with_opencencus_used(): + with ContextHelper(): + trace = tracer_module.Tracer(sampler=AlwaysOnSampler()) + parent = trace.start_span(name="OverAll") + client = MockClient(policies=[]) + await client.get_foo(parent_span=parent) + await client.get_foo() + assert len(parent.children) == 3 + assert parent.children[0].name == "MockClient.__init__" + assert not parent.children[0].children + assert parent.children[1].name == "MockClient.get_foo" + assert not parent.children[1].children + parent.finish() + trace.finish() + +@pytest.mark.asyncio +async def for_test_different_settings(): + with ContextHelper(): + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=mock.Mock(Exporter)) + with trace.start_span(name="OverAll") as parent: + client = MockClient() + await client.make_request(2) + with trace.span("child") as child: + await client.make_request(2, parent_span=parent) + assert OpenCensusSpan.get_current_span() == child + await client.make_request(2) + assert len(parent.children) == 3 + assert parent.children[0].name == "MockClient.__init__" + assert parent.children[1].name == "MockClient.make_request" + assert parent.children[1].children[0].name == "MockClient.get_foo" + assert parent.children[1].children[1].name == "MockClient.make_request" + assert parent.children[2].name == "MockClient.make_request" + assert parent.children[2].children[0].name == "MockClient.get_foo" + assert parent.children[2].children[1].name == "MockClient.make_request" + children = parent.children[1].children + assert len(children) == 2 + trace.finish() + +@pytest.mark.asyncio +async def test_span_with_opencensus_complicated(): + for_test_different_settings() + +@pytest.mark.asyncio +async def test_span_with_opencensus_passed_in_complicated(): + with ContextHelper(tracer_to_use="opencensus"): + for_test_different_settings() From ac9ff0999229f738ce581b697e546e07468390bf Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 15:08:19 -0700 Subject: [PATCH 058/105] delete the unit test thing --- sdk/core/azure-core/tests/test_tracing_context.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_context.py b/sdk/core/azure-core/tests/test_tracing_context.py index cd54fecde2c2..2b8a32762e21 100644 --- a/sdk/core/azure-core/tests/test_tracing_context.py +++ b/sdk/core/azure-core/tests/test_tracing_context.py @@ -49,7 +49,3 @@ def work(): span = tracing_context.current_span.get() assert span == current_span assert getattr(span, "in_worker", False) - - -if __name__ == "__main__": - unittest.main() From ff07c55bc65da2d81c01c3ea24b7feb1c31e76ea Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 17:55:06 -0700 Subject: [PATCH 059/105] added add_attribute --- .../azure/core/tracing/ext/opencensus_span.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 1602c4d05923..49e3409500d1 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -12,7 +12,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Any, Dict, Optional + from typing import Dict, Optional, Union class OpenCensusSpan(object): @@ -43,7 +43,7 @@ def __init__(self, span=None, name="span"): def span_instance(self): # type: () -> Span """ - :return: The openencensus span that is being wrapped. + :return: The OpenCensus span that is being wrapped. """ return self._span_instance @@ -80,6 +80,18 @@ def to_header(self): temp_headers = tracer_from_context.propagator.to_headers(ctx) return temp_headers + def add_attribute(self, key, value): + # type: (str, Union[str, int]) -> None + """ + Add attribute (key value pair) to the current span. + + :param key: The key of the key value pair + :type key: str + :param value: The value of the key value pair + :type value: str + """ + self.span_instance.add_attribute(key, value) + @classmethod def link(cls, headers): # type: (Dict[str, str]) -> None From 30fd1652a99f0e18c31b621bab32d70aea9aa518 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 17:55:18 -0700 Subject: [PATCH 060/105] added add_attribute --- .../azure-core/azure/core/tracing/abstract_span.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index 3ced8a060ad6..5da1b2123610 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -8,7 +8,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Any, Dict, Optional + from typing import Any, Dict, Optional, Union try: from typing_extensions import Protocol @@ -52,6 +52,18 @@ def to_header(self): """ pass + def add_attribute(self, key, value): + # type: (str, Union[str, int]) -> None + """ + Add attribute (key value pair) to the current span. + + :param key: The key of the key value pair + :type key: str + :param value: The value of the key value pair + :type value: str + """ + pass + @property def span_instance(self): # type: () -> Any From 644fe2a035b2760e425dcc780cdcb4c967fb4c8c Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 17:57:49 -0700 Subject: [PATCH 061/105] added tests for add attributes --- .../azure-core/tests/test_tracing_implementations.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index ac395ebd04b8..02b0d826b8e1 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -111,3 +111,12 @@ def test_links(self): link = wrapped_class.span_instance.links[0] assert link.trace_id == "2578531519ed94423ceae67588eff2c9" assert link.span_id == "231ebdc614cb9ddd" + + def test_add_attribute(self): + with ContextHelper() as ctx: + trace = tracer_module.Tracer(sampler=AlwaysOnSampler()) + parent = trace.start_span() + wrapped_class = OpenCensusSpan(span=parent) + wrapped_class.add_attribute("test", "test2") + assert wrapped_class.span_instance.attributes["test"] == "test2" + assert parent.attributes["test"] == "test2" From e556a69d4d0199f139cb5cd8ec9ff4dc652fe2e9 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 17:58:09 -0700 Subject: [PATCH 062/105] add initial policy --- .../pipeline/policies/distributed_tracing.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py new file mode 100644 index 000000000000..f76e743b1400 --- /dev/null +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -0,0 +1,74 @@ +from azure.core.pipeline import PipelineRequest, PipelineResponse +from azure.core.tracing.context import tracing_context +from azure.core.tracing.abstract_span import AbstractSpan +from azure.core.tracing.common import set_span_contexts +from azure.core.pipeline.policies import SansIOHTTPPolicy +from azure.core.settings import settings + +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False + +if TYPE_CHECKING: + from typing import Any, TypeVar + + HTTPResponseType = TypeVar("HTTPResponseType") + HTTPRequestType = TypeVar("HTTPRequestType") + + +class DistributedTracingPolicy(SansIOHTTPPolicy): + """The policy to create spans for Azure Calls""" + + def __init__(self, name_of_spans="Azure Call"): + # type: (str, str, str) -> None + self.name_of_child_span = name_of_spans + self.parent_span_dict = {} + + def set_header(self, request, span): + # type: (PipelineRequest[HTTPRequestType], Any) -> None + """ + Sets the header information on the span. + """ + headers = span.to_header() + request.http_request.headers.update(headers) + + def on_request(self, request, **kwargs): + # type: (PipelineRequest[HTTPRequestType], Any) -> None + parent_span = tracing_context.current_span.get() # type: AbstractSpan + + if parent_span is None: + return + + only_propagate = settings.tracing_should_only_propagate() + if only_propagate: + self.set_header(request, parent_span) + return + + child = parent_span.span(name=self.name_of_child_span) + child.start() + + set_span_contexts(child) + self.parent_span_dict[child] = parent_span + self.set_header(request, child) + + def end_span(self, request, response=None): + # type: (PipelineRequest[HTTPRequestType], PipelineResponse[HTTPRequestType, HTTPResponseType]) -> None + span = tracing_context.current_span.get() # type: AbstractSpan + only_propagate = settings.tracing_should_only_propagate() + if span and not only_propagate: + # span.add_attribute("http.url", request.http_request) + if response: + span.add_attribute("status_code", response.http_response.status_code) + else: + span.add_attribute("status_code", 522) + span.finish() + set_span_contexts(self.parent_span_dict[span]) + + def on_response(self, request, response, **kwargs): + # type: (PipelineRequest[HTTPRequestType], PipelineResponse[HTTPRequestType, HTTPResponseType], Any) -> None + self.end_span(request, response=response) + + def on_exception(self, request, **kwargs): + # type: (PipelineRequest[HTTPRequestType], Any) -> bool + self.end_span(request) From 8f137c1f2a6771beb5ff7c68c088d7309624c64a Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 12 Jul 2019 17:58:58 -0700 Subject: [PATCH 063/105] remove unused import --- sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 49e3409500d1..9bd31b0d7467 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -2,7 +2,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -from opencensus.trace import tracer as tracer_module, Span, execution_context +from opencensus.trace import tracer as Span, execution_context from opencensus.trace.link import Link from opencensus.trace.propagation import trace_context_http_header_format From 0c37b386e2259a10844650916b8d67a9d7e1eeb8 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 11:18:02 -0700 Subject: [PATCH 064/105] added docstrings --- .../azure-core/azure/core/tracing/common.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 754467a0aced..cd5425623ead 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -42,6 +42,14 @@ def set_span_contexts(wrapped_span, span_instance=None): # type: (AbstractSpan, Optional[AbstractSpan]) -> None + """ + Set the sdk context and the implementation context. `span_instance` will be used to set the implementation context + if passed in else will use `wrapped_span.span_instance`. + + :param wrapped_span: The `AbstractSpan` to set as the sdk context + :type wrapped_span: `azure.core.tracing.abstract_span.AbstractSpan` + :param span_instance: The span to set as the current span for the implementation context + """ tracing_context.current_span.set(wrapped_span) impl_wrapper = settings.tracing_implementation() if wrapped_span is not None: @@ -52,7 +60,18 @@ def set_span_contexts(wrapped_span, span_instance=None): def get_parent_and_original_contexts(kwargs): # type: (Any) -> Tuple(AbstractSpan, AbstractSpan, Any) - """Returns the parent span that of the span that represents the function and the spans before that parent span""" + """ + Returns the current span so that the function's span will be its child. It will create a new span if there is + no current span in any of the context. Because it creates a new span in those cases, also returns the original + span as well. If it does not create a new span then the orig_wrapped_span should be the same as the parent span. + + `AbstractSpan = azure.core.tracing.abstract_span.AbstractSpan` + + :param kwargs: The dictionary of named variables that the user passed into the top level azure function + :param kwargs: dict + :returns: The parent_span, the original wrapped span, the original implementation span + :rtype: `AbstractSpan`, `AbstractSpan`, implementation span + """ parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan orig_wrapped_span = tracing_context.current_span.get() From b925782c4f18f145108e8f341f5615911ae16920 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 11:21:02 -0700 Subject: [PATCH 065/105] minor docstring formatting --- sdk/core/azure-core/azure/core/tracing/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index cd5425623ead..8e0452efc175 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -45,7 +45,7 @@ def set_span_contexts(wrapped_span, span_instance=None): """ Set the sdk context and the implementation context. `span_instance` will be used to set the implementation context if passed in else will use `wrapped_span.span_instance`. - + :param wrapped_span: The `AbstractSpan` to set as the sdk context :type wrapped_span: `azure.core.tracing.abstract_span.AbstractSpan` :param span_instance: The span to set as the current span for the implementation context @@ -64,7 +64,7 @@ def get_parent_and_original_contexts(kwargs): Returns the current span so that the function's span will be its child. It will create a new span if there is no current span in any of the context. Because it creates a new span in those cases, also returns the original span as well. If it does not create a new span then the orig_wrapped_span should be the same as the parent span. - + `AbstractSpan = azure.core.tracing.abstract_span.AbstractSpan` :param kwargs: The dictionary of named variables that the user passed into the top level azure function From 4dd6ba98bdaeb92b5eb8c270b6a74da76cbbe811 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 12:20:01 -0700 Subject: [PATCH 066/105] fix pylint errors --- sdk/core/azure-core/azure/core/tracing/abstract_span.py | 4 +++- sdk/core/azure-core/azure/core/tracing/common.py | 2 ++ sdk/core/azure-core/azure/core/tracing/context.py | 2 ++ sdk/core/azure-core/azure/core/tracing/decorator.py | 2 ++ sdk/core/azure-core/azure/core/tracing/decorator_async.py | 2 ++ .../azure-core/azure/core/tracing/ext/opencensus_span.py | 8 +++++--- .../azure_core_asynctests/test_tracing_decorator_async.py | 2 ++ sdk/core/azure-core/tests/test_tracing_decorator.py | 2 ++ 8 files changed, 20 insertions(+), 4 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index 5da1b2123610..1870c68a98c4 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +"""Protocol that defines what functions wrappers of tracing libraries should implement.""" + try: from typing import TYPE_CHECKING except ImportError: @@ -19,7 +21,7 @@ class AbstractSpan(Protocol): """Wraps a span from a distributed tracing implementation.""" - def __init__(self, span=None, name=None): + def __init__(self, span=None, name=None): # pylint: disable=super-init-not-called # type: (Optional[Any], Optional[str]) -> None """ If a span is given wraps the span. Else a new span is created. diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 8e0452efc175..02a1dd2825bf 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -23,6 +23,8 @@ # THE SOFTWARE. # # -------------------------------------------------------------------------- +"""Common functions shared by both the sync and the async decorators.""" + from os import environ import re diff --git a/sdk/core/azure-core/azure/core/tracing/context.py b/sdk/core/azure-core/azure/core/tracing/context.py index 3884ef067247..a00f88f2d854 100644 --- a/sdk/core/azure-core/azure/core/tracing/context.py +++ b/sdk/core/azure-core/azure/core/tracing/context.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +"""The context for the azure.core.tracing. Holds global variables in a thread and async safe way.""" + import threading from azure.core.settings import settings diff --git a/sdk/core/azure-core/azure/core/tracing/decorator.py b/sdk/core/azure-core/azure/core/tracing/decorator.py index 1d2a483fb8ef..c20b766a29b2 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator.py @@ -23,6 +23,8 @@ # THE SOFTWARE. # # -------------------------------------------------------------------------- +"""The decorator to apply if you want the given function traced.""" + import functools import azure.core.tracing.common as common diff --git a/sdk/core/azure-core/azure/core/tracing/decorator_async.py b/sdk/core/azure-core/azure/core/tracing/decorator_async.py index c7fbb714084d..1792ab4f3404 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator_async.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator_async.py @@ -23,6 +23,8 @@ # THE SOFTWARE. # # -------------------------------------------------------------------------- +"""The decorator to apply if you want the given function traced.""" + import functools import azure.core.tracing.common as common diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 9bd31b0d7467..67e5b686df85 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -2,7 +2,9 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -from opencensus.trace import tracer as Span, execution_context +"""Implements azure.core.tracing.AbstractSpan to wrap opencensus spans.""" + +from opencensus.trace import Span, execution_context from opencensus.trace.link import Link from opencensus.trace.propagation import trace_context_http_header_format @@ -36,7 +38,7 @@ def __init__(self, span=None, name="span"): # The logic is needed until opencensus fixes their bug # https://github.com/census-instrumentation/opencensus-python/issues/466 if current_span and span not in current_span.children: - current_span._child_spans.append(span) + current_span._child_spans.append(span) # pylint: disable=protected-access self._span_instance = span @property @@ -97,7 +99,7 @@ def link(cls, headers): # type: (Dict[str, str]) -> None """ Given a dictionary, extracts the context and links the context to the current tracer. - + :param headers: A key value pair dictionary :type headers: dict """ diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index d533516bdbcd..d0e2f6370a2b 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +"""The tests for decorators_async.py""" + import unittest try: diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 801c8ce8c67b..6c8ae31e35dd 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +"""The tests for decorators.py and common.py""" + import unittest try: From fae494af4502ebc282f1fc13ca3c5b017def987d Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 16:41:19 -0700 Subject: [PATCH 067/105] don't rely on opencensus children to check --- .../azure/core/tracing/ext/opencensus_span.py | 7 +- .../tests/test_tracing_decorator.py | 89 +++++++++++++++---- 2 files changed, 74 insertions(+), 22 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 67e5b686df85..61bfbf2c0ec7 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -31,14 +31,9 @@ def __init__(self, span=None, name="span"): :param name: The name of the OpenCensus span to create if a new span is needed :type name: str """ - tracer = self.get_current_tracer() if not span: - current_span = self.get_current_span() + tracer = self.get_current_tracer() span = tracer.span(name=name) - # The logic is needed until opencensus fixes their bug - # https://github.com/census-instrumentation/opencensus-python/issues/466 - if current_span and span not in current_span.children: - current_span._child_spans.append(span) # pylint: disable=protected-access self._span_instance = span @property diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 6c8ae31e35dd..ab84a88abbd7 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -23,10 +23,21 @@ from azure.core.settings import settings from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module +from opencensus.trace.span_data import SpanData from opencensus.trace.samplers import AlwaysOnSampler from opencensus.trace.base_exporter import Exporter +from opencensus.common.utils import timestamp_to_microseconds +import time import pytest +try: + from typing import TYPE_CHECKING +except ImportError: + TYPE_CHECKING = False + +if TYPE_CHECKING: + from typing import List + class ContextHelper(object): def __init__(self, environ={}, tracer_to_use=None): @@ -75,6 +86,7 @@ def verify_request(self, request): @distributed_tracing_decorator def make_request(self, numb_times, **kwargs): + time.sleep(0.01) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -84,9 +96,46 @@ def make_request(self, numb_times, **kwargs): @distributed_tracing_decorator def get_foo(self): + time.sleep(0.01) return 5 +class Node: + def __init__(self, span_data): + self.span_data = span_data # type: SpanData + self.parent = None + self.children = [] + + +class MockExporter(Exporter): + def __init__(self): + self.root = None + self._all_nodes = [] + + def export(self, span_datas): + # type: (List[SpanData]) -> None + sp = span_datas[0] # type: SpanData + node = Node(sp) + if not node.span_data.parent_span_id: + self.root = node + self._all_nodes.append(node) + + def build_tree(self): + parent_dict = {} + for node in self._all_nodes: + parent_span_id = node.span_data.parent_span_id + if parent_span_id not in parent_dict: + parent_dict[parent_span_id] = [] + parent_dict[parent_span_id].append(node) + + for node in self._all_nodes: + if node.span_data.span_id in parent_dict: + node.children = sorted( + parent_dict[node.span_data.span_id], + key=lambda x: timestamp_to_microseconds(x.span_data.start_time), + ) + + class TestCommon(unittest.TestCase): def test_set_span_context(self): with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): @@ -162,22 +211,26 @@ def test_with_opencensus_imported_but_not_used(self): def test_with_opencencus_used(self): with ContextHelper(): - trace = tracer_module.Tracer(sampler=AlwaysOnSampler()) + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) parent = trace.start_span(name="OverAll") client = MockClient(policies=[]) client.get_foo(parent_span=parent) client.get_foo() + parent.finish() + trace.finish() + exporter.build_tree() + parent = exporter.root assert len(parent.children) == 3 - assert parent.children[0].name == "MockClient.__init__" + assert parent.children[0].span_data.name == "MockClient.__init__" assert not parent.children[0].children - assert parent.children[1].name == "MockClient.get_foo" + assert parent.children[1].span_data.name == "MockClient.get_foo" assert not parent.children[1].children - parent.finish() - trace.finish() def for_test_different_settings(self): with ContextHelper(): - trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=mock.Mock(Exporter)) + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) with trace.start_span(name="OverAll") as parent: client = MockClient() client.make_request(2) @@ -185,17 +238,21 @@ def for_test_different_settings(self): client.make_request(2, parent_span=parent) assert OpenCensusSpan.get_current_span() == child client.make_request(2) - assert len(parent.children) == 3 - assert parent.children[0].name == "MockClient.__init__" - assert parent.children[1].name == "MockClient.make_request" - assert parent.children[1].children[0].name == "MockClient.get_foo" - assert parent.children[1].children[1].name == "MockClient.make_request" - assert parent.children[2].name == "MockClient.make_request" - assert parent.children[2].children[0].name == "MockClient.get_foo" - assert parent.children[2].children[1].name == "MockClient.make_request" - children = parent.children[1].children - assert len(children) == 2 trace.finish() + exporter.build_tree() + parent = exporter.root + assert len(parent.children) == 4 + assert parent.children[0].span_data.name == "MockClient.__init__" + assert parent.children[1].span_data.name == "MockClient.make_request" + assert parent.children[1].children[0].span_data.name == "MockClient.get_foo" + assert parent.children[1].children[1].span_data.name == "MockClient.make_request" + assert parent.children[2].span_data.name == "child" + assert parent.children[2].children[0].span_data.name == "MockClient.make_request" + assert parent.children[3].span_data.name == "MockClient.make_request" + assert parent.children[3].children[0].span_data.name == "MockClient.get_foo" + assert parent.children[3].children[1].span_data.name == "MockClient.make_request" + children = parent.children[1].children + assert len(children) == 2 def test_span_with_opencensus_complicated(self): self.for_test_different_settings() From e484bcf43f3ea8bc23646852e3708faa7879f92d Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 16:53:08 -0700 Subject: [PATCH 068/105] use exporter to not rely on parent.children --- .../test_tracing_decorator_async.py | 77 +++++++++++++++---- .../tests/test_tracing_decorator.py | 4 +- .../tests/test_tracing_implementations.py | 50 +++++++++++- 3 files changed, 109 insertions(+), 22 deletions(-) diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index d0e2f6370a2b..790f53583631 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -25,9 +25,46 @@ from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler from opencensus.trace.base_exporter import Exporter +from opencensus.common.utils import timestamp_to_microseconds import pytest +class Node: + def __init__(self, span_data): + self.span_data = span_data # type: SpanData + self.parent = None + self.children = [] + + +class MockExporter(Exporter): + def __init__(self): + self.root = None + self._all_nodes = [] + + def export(self, span_datas): + # type: (List[SpanData]) -> None + sp = span_datas[0] # type: SpanData + node = Node(sp) + if not node.span_data.parent_span_id: + self.root = node + self._all_nodes.append(node) + + def build_tree(self): + parent_dict = {} + for node in self._all_nodes: + parent_span_id = node.span_data.parent_span_id + if parent_span_id not in parent_dict: + parent_dict[parent_span_id] = [] + parent_dict[parent_span_id].append(node) + + for node in self._all_nodes: + if node.span_data.span_id in parent_dict: + node.children = sorted( + parent_dict[node.span_data.span_id], + key=lambda x: timestamp_to_microseconds(x.span_data.start_time), + ) + + class ContextHelper(object): def __init__(self, environ={}, tracer_to_use=None): self.orig_tracer = OpenCensusSpan.get_current_tracer() @@ -105,23 +142,27 @@ async def test_with_opencensus_imported_but_not_used(): @pytest.mark.asyncio async def test_with_opencencus_used(): with ContextHelper(): - trace = tracer_module.Tracer(sampler=AlwaysOnSampler()) + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) parent = trace.start_span(name="OverAll") client = MockClient(policies=[]) await client.get_foo(parent_span=parent) await client.get_foo() + parent.finish() + trace.finish() + exporter.build_tree() + parent = exporter.root assert len(parent.children) == 3 - assert parent.children[0].name == "MockClient.__init__" + assert parent.children[0].span_data.name == "MockClient.__init__" assert not parent.children[0].children - assert parent.children[1].name == "MockClient.get_foo" + assert parent.children[1].span_data.name == "MockClient.get_foo" assert not parent.children[1].children - parent.finish() - trace.finish() @pytest.mark.asyncio async def for_test_different_settings(): with ContextHelper(): - trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=mock.Mock(Exporter)) + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) with trace.start_span(name="OverAll") as parent: client = MockClient() await client.make_request(2) @@ -129,17 +170,21 @@ async def for_test_different_settings(): await client.make_request(2, parent_span=parent) assert OpenCensusSpan.get_current_span() == child await client.make_request(2) - assert len(parent.children) == 3 - assert parent.children[0].name == "MockClient.__init__" - assert parent.children[1].name == "MockClient.make_request" - assert parent.children[1].children[0].name == "MockClient.get_foo" - assert parent.children[1].children[1].name == "MockClient.make_request" - assert parent.children[2].name == "MockClient.make_request" - assert parent.children[2].children[0].name == "MockClient.get_foo" - assert parent.children[2].children[1].name == "MockClient.make_request" - children = parent.children[1].children - assert len(children) == 2 trace.finish() + exporter.build_tree() + parent = exporter.root + assert len(parent.children) == 4 + assert parent.children[0].span_data.name == "MockClient.__init__" + assert parent.children[1].span_data.name == "MockClient.make_request" + assert parent.children[1].children[0].span_data.name == "MockClient.get_foo" + assert parent.children[1].children[1].span_data.name == "MockClient.make_request" + assert parent.children[2].span_data.name == "child" + assert parent.children[2].children[0].span_data.name == "MockClient.make_request" + assert parent.children[3].span_data.name == "MockClient.make_request" + assert parent.children[3].children[0].span_data.name == "MockClient.get_foo" + assert parent.children[3].children[1].span_data.name == "MockClient.make_request" + children = parent.children[1].children + assert len(children) == 2 @pytest.mark.asyncio async def test_span_with_opencensus_complicated(): diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index ab84a88abbd7..83170685a02b 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -86,7 +86,7 @@ def verify_request(self, request): @distributed_tracing_decorator def make_request(self, numb_times, **kwargs): - time.sleep(0.01) + time.sleep(0.001) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -96,7 +96,7 @@ def make_request(self, numb_times, **kwargs): @distributed_tracing_decorator def get_foo(self): - time.sleep(0.01) + time.sleep(0.001) return 5 diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 02b0d826b8e1..5793685a9a1e 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -2,6 +2,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ +"""The tests for opencensus_span.py""" + import unittest try: @@ -12,10 +14,46 @@ from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler -from opencensus.ext.azure.trace_exporter import AzureExporter +from opencensus.trace.base_exporter import Exporter +from opencensus.common.utils import timestamp_to_microseconds import os +class Node: + def __init__(self, span_data): + self.span_data = span_data # type: SpanData + self.parent = None + self.children = [] + + +class MockExporter(Exporter): + def __init__(self): + self.root = None + self._all_nodes = [] + + def export(self, span_datas): + # type: (List[SpanData]) -> None + sp = span_datas[0] # type: SpanData + node = Node(sp) + if not node.span_data.parent_span_id: + self.root = node + self._all_nodes.append(node) + + def build_tree(self): + parent_dict = {} + for node in self._all_nodes: + parent_span_id = node.span_data.parent_span_id + if parent_span_id not in parent_dict: + parent_dict[parent_span_id] = [] + parent_dict[parent_span_id].append(node) + + for node in self._all_nodes: + if node.span_data.span_id in parent_dict: + node.children = sorted( + parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) + ) + + class ContextHelper(object): def __init__(self, environ={}): self.orig_tracer = OpenCensusSpan.get_current_tracer() @@ -64,8 +102,9 @@ def test_no_span_but_in_trace(self): tracer.finish() def test_span(self): + exporter = MockExporter() with ContextHelper() as ctx: - tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) + tracer = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) assert OpenCensusSpan.get_current_tracer() is tracer wrapped_class = OpenCensusSpan() assert tracer.current_span() == wrapped_class.span_instance @@ -74,8 +113,11 @@ def test_span(self): assert child.span_instance.name == "span" assert child.span_instance.context_tracer.trace_id == tracer.span_context.trace_id assert child.span_instance.parent_span is wrapped_class.span_instance - assert len(wrapped_class.span_instance.children) == 1 - assert wrapped_class.span_instance.children[0] == child.span_instance + tracer.finish() + exporter.build_tree() + parent = exporter.root + assert len(parent.children) == 1 + assert parent.children[0].span_data.span_id == child.span_instance.span_id def test_start_finish(self): with ContextHelper() as ctx: From 1eba4cd79dfa37567cc122cd72561b1efda3b37c Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 17:27:14 -0700 Subject: [PATCH 069/105] added documentation and span attributes --- .../pipeline/policies/distributed_tracing.py | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index f76e743b1400..b091d7cf0f6b 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -1,3 +1,30 @@ +# -------------------------------------------------------------------------- +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# +# The MIT License (MIT) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the ""Software""), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# -------------------------------------------------------------------------- +"""Traces network calls using the implementation library from the settings.""" + from azure.core.pipeline import PipelineRequest, PipelineResponse from azure.core.tracing.context import tracing_context from azure.core.tracing.abstract_span import AbstractSpan @@ -11,7 +38,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Any, TypeVar + from typing import Any, TypeVar, Optional HTTPResponseType = TypeVar("HTTPResponseType") HTTPRequestType = TypeVar("HTTPRequestType") @@ -24,6 +51,11 @@ def __init__(self, name_of_spans="Azure Call"): # type: (str, str, str) -> None self.name_of_child_span = name_of_spans self.parent_span_dict = {} + self._http_user_agent = "http.user_agent" + self._http_method = "http.method" + self._http_url = "http.url" + self._http_status_code = "http.status_code" + self._request_id = "x-ms-request-id" def set_header(self, request, span): # type: (PipelineRequest[HTTPRequestType], Any) -> None @@ -32,6 +64,9 @@ def set_header(self, request, span): """ headers = span.to_header() request.http_request.headers.update(headers) + span.add_attribute(self._http_method, request.http_request.method) + span.add_attribute(self._http_url, request.http_request.url) + span.add_attribute(self._http_user_agent, request.http_request.headers.get("User-Agent", "")) def on_request(self, request, **kwargs): # type: (PipelineRequest[HTTPRequestType], Any) -> None @@ -52,23 +87,25 @@ def on_request(self, request, **kwargs): self.parent_span_dict[child] = parent_span self.set_header(request, child) - def end_span(self, request, response=None): - # type: (PipelineRequest[HTTPRequestType], PipelineResponse[HTTPRequestType, HTTPResponseType]) -> None + def end_span(self, response=None): + # type: (Optional[PipelineResponse[HTTPRequestType, HTTPResponseType]]) -> None + """Ends the span that is tracing the network and updates its status.""" span = tracing_context.current_span.get() # type: AbstractSpan only_propagate = settings.tracing_should_only_propagate() if span and not only_propagate: # span.add_attribute("http.url", request.http_request) if response: - span.add_attribute("status_code", response.http_response.status_code) + span.add_attribute(self._http_status_code, response.http_response.status_code) + span.add_attribute(self._request_id, response.http_response.headers.get(self._request_id, "")) else: - span.add_attribute("status_code", 522) + span.add_attribute(self._http_status_code, 504) span.finish() set_span_contexts(self.parent_span_dict[span]) def on_response(self, request, response, **kwargs): # type: (PipelineRequest[HTTPRequestType], PipelineResponse[HTTPRequestType, HTTPResponseType], Any) -> None - self.end_span(request, response=response) + self.end_span(response=response) - def on_exception(self, request, **kwargs): + def on_exception(self, _request, **kwargs): # pylint: disable=unused-argument # type: (PipelineRequest[HTTPRequestType], Any) -> bool - self.end_span(request) + self.end_span() From 6e5b05fb9bd84091c38656986af80529a8c3195b Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 17:29:38 -0700 Subject: [PATCH 070/105] added test tracing policy --- sdk/core/azure-core/tests/test_tracing_policy.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 sdk/core/azure-core/tests/test_tracing_policy.py diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py new file mode 100644 index 000000000000..e69de29bb2d1 From 9d0159cb6c0576bdfaed6c3f49ed8813c4de74f5 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 17:37:44 -0700 Subject: [PATCH 071/105] test should only propagate --- .../test_tracing_decorator_async.py | 35 +++++++++++++++++-- .../tests/test_tracing_decorator.py | 29 +++++++++++---- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index 790f53583631..c0dd24f455f5 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -60,18 +60,18 @@ def build_tree(self): for node in self._all_nodes: if node.span_data.span_id in parent_dict: node.children = sorted( - parent_dict[node.span_data.span_id], - key=lambda x: timestamp_to_microseconds(x.span_data.start_time), + parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) ) class ContextHelper(object): - def __init__(self, environ={}, tracer_to_use=None): + def __init__(self, environ={}, tracer_to_use=None, should_only_propagate=None): self.orig_tracer = OpenCensusSpan.get_current_tracer() self.orig_current_span = OpenCensusSpan.get_current_span() self.orig_sdk_context_span = tracing_context.current_span.get() self.os_env = mock.patch.dict(os.environ, environ) self.tracer_to_use = tracer_to_use + self.should_only_propagate = should_only_propagate def __enter__(self): self.orig_tracer = OpenCensusSpan.get_current_tracer() @@ -79,6 +79,8 @@ def __enter__(self): self.orig_sdk_context_span = tracing_context.current_span.get() if self.tracer_to_use is not None: settings.tracing_implementation.set_value(self.tracer_to_use) + if self.should_only_propagate is not None: + settings.tracing_should_only_propagate.set_value(self.should_only_propagate) self.os_env.start() return self @@ -87,6 +89,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): OpenCensusSpan.set_current_span(self.orig_current_span) tracing_context.current_span.set(self.orig_sdk_context_span) settings.tracing_implementation.unset_value() + settings.tracing_should_only_propagate.unset_value() self.os_env.stop() @@ -123,6 +126,7 @@ async def make_request(self, numb_times, **kwargs): async def get_foo(self): return 5 + @pytest.mark.asyncio async def test_with_nothing_imported(): with ContextHelper(): @@ -133,12 +137,14 @@ async def test_with_nothing_imported(): await client.make_request(3) sys.modules["opencensus"] = opencensus + @pytest.mark.asyncio async def test_with_opencensus_imported_but_not_used(): with ContextHelper(): client = MockClient(assert_current_span=True) await client.make_request(3) + @pytest.mark.asyncio async def test_with_opencencus_used(): with ContextHelper(): @@ -158,6 +164,7 @@ async def test_with_opencencus_used(): assert parent.children[1].span_data.name == "MockClient.get_foo" assert not parent.children[1].children + @pytest.mark.asyncio async def for_test_different_settings(): with ContextHelper(): @@ -186,11 +193,33 @@ async def for_test_different_settings(): children = parent.children[1].children assert len(children) == 2 + @pytest.mark.asyncio async def test_span_with_opencensus_complicated(): for_test_different_settings() + @pytest.mark.asyncio async def test_span_with_opencensus_passed_in_complicated(): with ContextHelper(tracer_to_use="opencensus"): for_test_different_settings() + + +@pytest.mark.asyncio +async def test_should_only_propagate(): + with ContextHelper(should_only_propagate=True): + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) + with trace.start_span(name="OverAll") as parent: + client = MockClient() + await client.make_request(2) + with trace.span("child") as child: + await client.make_request(2, parent_span=parent) + assert OpenCensusSpan.get_current_span() == child + await client.make_request(2) + trace.finish() + exporter.build_tree() + parent = exporter.root + assert len(parent.children) == 1 + assert parent.children[0].span_data.name == "child" + assert not parent.children[0].children diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 83170685a02b..2542a0725535 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -40,12 +40,13 @@ class ContextHelper(object): - def __init__(self, environ={}, tracer_to_use=None): + def __init__(self, environ={}, tracer_to_use=None, should_only_propagate=None): self.orig_tracer = OpenCensusSpan.get_current_tracer() self.orig_current_span = OpenCensusSpan.get_current_span() self.orig_sdk_context_span = tracing_context.current_span.get() self.os_env = mock.patch.dict(os.environ, environ) self.tracer_to_use = tracer_to_use + self.should_only_propagate = should_only_propagate def __enter__(self): self.orig_tracer = OpenCensusSpan.get_current_tracer() @@ -53,6 +54,8 @@ def __enter__(self): self.orig_sdk_context_span = tracing_context.current_span.get() if self.tracer_to_use is not None: settings.tracing_implementation.set_value(self.tracer_to_use) + if self.should_only_propagate is not None: + settings.tracing_should_only_propagate.set_value(self.should_only_propagate) self.os_env.start() return self @@ -61,6 +64,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): OpenCensusSpan.set_current_span(self.orig_current_span) tracing_context.current_span.set(self.orig_sdk_context_span) settings.tracing_implementation.unset_value() + settings.tracing_should_only_propagate.unset_value() self.os_env.stop() @@ -131,8 +135,7 @@ def build_tree(self): for node in self._all_nodes: if node.span_data.span_id in parent_dict: node.children = sorted( - parent_dict[node.span_data.span_id], - key=lambda x: timestamp_to_microseconds(x.span_data.start_time), + parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) ) @@ -261,6 +264,20 @@ def test_span_with_opencensus_passed_in_complicated(self): with ContextHelper(tracer_to_use="opencensus"): self.for_test_different_settings() - -if __name__ == "__main__": - unittest.main() + def test_should_only_propagate(self): + with ContextHelper(should_only_propagate=True): + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) + with trace.start_span(name="OverAll") as parent: + client = MockClient() + client.make_request(2) + with trace.span("child") as child: + client.make_request(2, parent_span=parent) + assert OpenCensusSpan.get_current_span() == child + client.make_request(2) + trace.finish() + exporter.build_tree() + parent = exporter.root + assert len(parent.children) == 1 + assert parent.children[0].span_data.name == "child" + assert not parent.children[0].children From aa142188c44c780ee10804e642a4e62ba3962dbe Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 17:51:57 -0700 Subject: [PATCH 072/105] made test tracing helper --- .../azure-core/azure/core/tracing/common.py | 5 +- .../test_tracing_decorator_async.py | 71 +-------------- .../tests/test_tracing_decorator.py | 69 +------------- sdk/core/azure-core/tests/tracing_common.py | 91 +++++++++++++++++++ 4 files changed, 94 insertions(+), 142 deletions(-) create mode 100644 sdk/core/azure-core/tests/tracing_common.py diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 02a1dd2825bf..5dcef263a45c 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -25,12 +25,9 @@ # -------------------------------------------------------------------------- """Common functions shared by both the sync and the async decorators.""" -from os import environ -import re - from azure.core.tracing.context import tracing_context from azure.core.tracing.abstract_span import AbstractSpan -from azure.core.settings import settings, get_opencensus_span +from azure.core.settings import settings try: diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index c0dd24f455f5..0cbe0d128c9d 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -4,15 +4,12 @@ # ------------------------------------ """The tests for decorators_async.py""" -import unittest - try: from unittest import mock except ImportError: import mock import sys -import os from azure.core import HttpRequest from azure.core.pipeline import Pipeline, PipelineResponse from azure.core.pipeline.policies import HTTPPolicy @@ -20,79 +17,13 @@ from azure.core.tracing.context import tracing_context from azure.core.tracing.decorator import distributed_tracing_decorator from azure.core.tracing.decorator_async import distributed_tracing_decorator_async -from azure.core.settings import settings from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler -from opencensus.trace.base_exporter import Exporter -from opencensus.common.utils import timestamp_to_microseconds +from tracing_common import ContextHelper, MockExporter import pytest -class Node: - def __init__(self, span_data): - self.span_data = span_data # type: SpanData - self.parent = None - self.children = [] - - -class MockExporter(Exporter): - def __init__(self): - self.root = None - self._all_nodes = [] - - def export(self, span_datas): - # type: (List[SpanData]) -> None - sp = span_datas[0] # type: SpanData - node = Node(sp) - if not node.span_data.parent_span_id: - self.root = node - self._all_nodes.append(node) - - def build_tree(self): - parent_dict = {} - for node in self._all_nodes: - parent_span_id = node.span_data.parent_span_id - if parent_span_id not in parent_dict: - parent_dict[parent_span_id] = [] - parent_dict[parent_span_id].append(node) - - for node in self._all_nodes: - if node.span_data.span_id in parent_dict: - node.children = sorted( - parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) - ) - - -class ContextHelper(object): - def __init__(self, environ={}, tracer_to_use=None, should_only_propagate=None): - self.orig_tracer = OpenCensusSpan.get_current_tracer() - self.orig_current_span = OpenCensusSpan.get_current_span() - self.orig_sdk_context_span = tracing_context.current_span.get() - self.os_env = mock.patch.dict(os.environ, environ) - self.tracer_to_use = tracer_to_use - self.should_only_propagate = should_only_propagate - - def __enter__(self): - self.orig_tracer = OpenCensusSpan.get_current_tracer() - self.orig_current_span = OpenCensusSpan.get_current_span() - self.orig_sdk_context_span = tracing_context.current_span.get() - if self.tracer_to_use is not None: - settings.tracing_implementation.set_value(self.tracer_to_use) - if self.should_only_propagate is not None: - settings.tracing_should_only_propagate.set_value(self.should_only_propagate) - self.os_env.start() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - OpenCensusSpan.set_current_tracer(self.orig_tracer) - OpenCensusSpan.set_current_span(self.orig_current_span) - tracing_context.current_span.set(self.orig_sdk_context_span) - settings.tracing_implementation.unset_value() - settings.tracing_should_only_propagate.unset_value() - self.os_env.stop() - - class MockClient: @distributed_tracing_decorator def __init__(self, policies=None, assert_current_span=False): diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 2542a0725535..3fd52b8931de 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -23,10 +23,8 @@ from azure.core.settings import settings from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module -from opencensus.trace.span_data import SpanData from opencensus.trace.samplers import AlwaysOnSampler -from opencensus.trace.base_exporter import Exporter -from opencensus.common.utils import timestamp_to_microseconds +from tracing_common import ContextHelper, MockExporter import time import pytest @@ -39,35 +37,6 @@ from typing import List -class ContextHelper(object): - def __init__(self, environ={}, tracer_to_use=None, should_only_propagate=None): - self.orig_tracer = OpenCensusSpan.get_current_tracer() - self.orig_current_span = OpenCensusSpan.get_current_span() - self.orig_sdk_context_span = tracing_context.current_span.get() - self.os_env = mock.patch.dict(os.environ, environ) - self.tracer_to_use = tracer_to_use - self.should_only_propagate = should_only_propagate - - def __enter__(self): - self.orig_tracer = OpenCensusSpan.get_current_tracer() - self.orig_current_span = OpenCensusSpan.get_current_span() - self.orig_sdk_context_span = tracing_context.current_span.get() - if self.tracer_to_use is not None: - settings.tracing_implementation.set_value(self.tracer_to_use) - if self.should_only_propagate is not None: - settings.tracing_should_only_propagate.set_value(self.should_only_propagate) - self.os_env.start() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - OpenCensusSpan.set_current_tracer(self.orig_tracer) - OpenCensusSpan.set_current_span(self.orig_current_span) - tracing_context.current_span.set(self.orig_sdk_context_span) - settings.tracing_implementation.unset_value() - settings.tracing_should_only_propagate.unset_value() - self.os_env.stop() - - class MockClient: @distributed_tracing_decorator def __init__(self, policies=None, assert_current_span=False): @@ -103,42 +72,6 @@ def get_foo(self): time.sleep(0.001) return 5 - -class Node: - def __init__(self, span_data): - self.span_data = span_data # type: SpanData - self.parent = None - self.children = [] - - -class MockExporter(Exporter): - def __init__(self): - self.root = None - self._all_nodes = [] - - def export(self, span_datas): - # type: (List[SpanData]) -> None - sp = span_datas[0] # type: SpanData - node = Node(sp) - if not node.span_data.parent_span_id: - self.root = node - self._all_nodes.append(node) - - def build_tree(self): - parent_dict = {} - for node in self._all_nodes: - parent_span_id = node.span_data.parent_span_id - if parent_span_id not in parent_dict: - parent_dict[parent_span_id] = [] - parent_dict[parent_span_id].append(node) - - for node in self._all_nodes: - if node.span_data.span_id in parent_dict: - node.children = sorted( - parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) - ) - - class TestCommon(unittest.TestCase): def test_set_span_context(self): with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): diff --git a/sdk/core/azure-core/tests/tracing_common.py b/sdk/core/azure-core/tests/tracing_common.py new file mode 100644 index 000000000000..18c4e7a96bdc --- /dev/null +++ b/sdk/core/azure-core/tests/tracing_common.py @@ -0,0 +1,91 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Code shared between the async and the sync test_decorator files.""" + +import sys +import os +from azure.core import HttpRequest +from azure.core.pipeline import Pipeline, PipelineResponse +from azure.core.pipeline.policies import HTTPPolicy +from azure.core.pipeline.transport import HttpTransport +from azure.core.tracing import common +from azure.core.tracing.context import tracing_context +from azure.core.tracing.decorator import distributed_tracing_decorator +from azure.core.settings import settings +from azure.core.tracing.ext.opencensus_span import OpenCensusSpan +from opencensus.trace import tracer as tracer_module +from opencensus.trace.span_data import SpanData +from opencensus.trace.samplers import AlwaysOnSampler +from opencensus.trace.base_exporter import Exporter +from opencensus.common.utils import timestamp_to_microseconds + +try: + from unittest import mock +except ImportError: + import mock + +class ContextHelper(object): + def __init__(self, environ={}, tracer_to_use=None, should_only_propagate=None): + self.orig_tracer = OpenCensusSpan.get_current_tracer() + self.orig_current_span = OpenCensusSpan.get_current_span() + self.orig_sdk_context_span = tracing_context.current_span.get() + self.os_env = mock.patch.dict(os.environ, environ) + self.tracer_to_use = tracer_to_use + self.should_only_propagate = should_only_propagate + + def __enter__(self): + self.orig_tracer = OpenCensusSpan.get_current_tracer() + self.orig_current_span = OpenCensusSpan.get_current_span() + self.orig_sdk_context_span = tracing_context.current_span.get() + if self.tracer_to_use is not None: + settings.tracing_implementation.set_value(self.tracer_to_use) + if self.should_only_propagate is not None: + settings.tracing_should_only_propagate.set_value(self.should_only_propagate) + self.os_env.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + OpenCensusSpan.set_current_tracer(self.orig_tracer) + OpenCensusSpan.set_current_span(self.orig_current_span) + tracing_context.current_span.set(self.orig_sdk_context_span) + settings.tracing_implementation.unset_value() + settings.tracing_should_only_propagate.unset_value() + self.os_env.stop() + + +class Node: + def __init__(self, span_data): + self.span_data = span_data # type: SpanData + self.parent = None + self.children = [] + + +class MockExporter(Exporter): + def __init__(self): + self.root = None + self._all_nodes = [] + + def export(self, span_datas): + # type: (List[SpanData]) -> None + sp = span_datas[0] # type: SpanData + node = Node(sp) + if not node.span_data.parent_span_id: + self.root = node + self._all_nodes.append(node) + + def build_tree(self): + parent_dict = {} + for node in self._all_nodes: + parent_span_id = node.span_data.parent_span_id + if parent_span_id not in parent_dict: + parent_dict[parent_span_id] = [] + parent_dict[parent_span_id].append(node) + + for node in self._all_nodes: + if node.span_data.span_id in parent_dict: + node.children = sorted( + parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) + ) + From bc35a5fa3653b78f49ee5460344a30d1631a425c Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 15 Jul 2019 17:56:06 -0700 Subject: [PATCH 073/105] decrease flakiness of test --- .../azure_core_asynctests/test_tracing_decorator_async.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index 0cbe0d128c9d..cb6ffa759488 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -22,6 +22,7 @@ from opencensus.trace.samplers import AlwaysOnSampler from tracing_common import ContextHelper, MockExporter import pytest +import time class MockClient: @@ -46,6 +47,7 @@ def verify_request(self, request): @distributed_tracing_decorator_async async def make_request(self, numb_times, **kwargs): + time.sleep(0.001) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -55,6 +57,7 @@ async def make_request(self, numb_times, **kwargs): @distributed_tracing_decorator_async async def get_foo(self): + time.sleep(0.001) return 5 From 85e5e48ed33d269f3403cdc1d9d0325efef43bae Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 00:17:11 -0700 Subject: [PATCH 074/105] simplify get parent --- .../azure-core/azure/core/tracing/common.py | 24 +++++++----------- .../azure/core/tracing/decorator.py | 16 ++++++++---- .../azure/core/tracing/decorator_async.py | 16 ++++++++---- .../tests/test_tracing_decorator.py | 25 ++++++------------- 4 files changed, 38 insertions(+), 43 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/common.py b/sdk/core/azure-core/azure/core/tracing/common.py index 5dcef263a45c..f6e4716a2eb8 100644 --- a/sdk/core/azure-core/azure/core/tracing/common.py +++ b/sdk/core/azure-core/azure/core/tracing/common.py @@ -57,27 +57,21 @@ def set_span_contexts(wrapped_span, span_instance=None): impl_wrapper.set_current_span(span_instance) -def get_parent_and_original_contexts(kwargs): +def get_parent_span(parent_span): # type: (Any) -> Tuple(AbstractSpan, AbstractSpan, Any) """ Returns the current span so that the function's span will be its child. It will create a new span if there is - no current span in any of the context. Because it creates a new span in those cases, also returns the original - span as well. If it does not create a new span then the orig_wrapped_span should be the same as the parent span. + no current span in any of the context. - `AbstractSpan = azure.core.tracing.abstract_span.AbstractSpan` - - :param kwargs: The dictionary of named variables that the user passed into the top level azure function - :param kwargs: dict - :returns: The parent_span, the original wrapped span, the original implementation span - :rtype: `AbstractSpan`, `AbstractSpan`, implementation span + :param parent_span: The parent_span arg that the user passes into the top level function + :returns: the parent_span of the function to be traced + :rtype: `azure.core.tracing.abstract_span.AbstractSpan` """ - parent_span = kwargs.pop("parent_span", None) # type: AbstractSpan - orig_wrapped_span = tracing_context.current_span.get() - wrapper_class = settings.tracing_implementation() if wrapper_class is None: - return None, orig_wrapped_span, None - original_wrapper_span_instance = wrapper_class.get_current_span() + return None + + orig_wrapped_span = tracing_context.current_span.get() # parent span is given, get from my context, get from the implementation context or make our own parent_span = orig_wrapped_span if parent_span is None else wrapper_class(parent_span) if parent_span is None: @@ -88,7 +82,7 @@ def get_parent_and_original_contexts(kwargs): else wrapper_class(name="azure-sdk-for-python-first_parent_span") ) - return parent_span, orig_wrapped_span, original_wrapper_span_instance + return parent_span def should_use_trace(parent_span): diff --git a/sdk/core/azure-core/azure/core/tracing/decorator.py b/sdk/core/azure-core/azure/core/tracing/decorator.py index c20b766a29b2..5a95aae52e8f 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator.py @@ -28,6 +28,8 @@ import functools import azure.core.tracing.common as common +from azure.core.settings import settings +from azure.core.tracing.context import tracing_context def distributed_tracing_decorator(func): @@ -35,9 +37,13 @@ def distributed_tracing_decorator(func): @functools.wraps(func) def wrapper_use_tracer(self, *args, **kwargs): # type: (Any) -> Any - parent_span, original_span_from_sdk_context, original_span_instance = common.get_parent_and_original_contexts( - kwargs - ) + passed_in_parent = kwargs.pop("parent_span", None) + orig_wrapped_span = tracing_context.current_span.get() + wrapper_class = settings.tracing_implementation() + original_span_instance = None + if wrapper_class is not None: + original_span_instance = wrapper_class.get_current_span() + parent_span = common.get_parent_span(passed_in_parent) ans = None if common.should_use_trace(parent_span): common.set_span_contexts(parent_span) @@ -48,9 +54,9 @@ def wrapper_use_tracer(self, *args, **kwargs): ans = func(self, *args, **kwargs) child.finish() common.set_span_contexts(parent_span) - if getattr(parent_span, "was_created_by_azure_sdk", False): + if orig_wrapped_span is None and passed_in_parent is None: parent_span.finish() - common.set_span_contexts(original_span_from_sdk_context, span_instance=original_span_instance) + common.set_span_contexts(orig_wrapped_span, span_instance=original_span_instance) else: ans = func(self, *args, **kwargs) return ans diff --git a/sdk/core/azure-core/azure/core/tracing/decorator_async.py b/sdk/core/azure-core/azure/core/tracing/decorator_async.py index 1792ab4f3404..e25d04628c77 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator_async.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator_async.py @@ -28,6 +28,8 @@ import functools import azure.core.tracing.common as common +from azure.core.settings import settings +from azure.core.tracing.context import tracing_context def distributed_tracing_decorator_async(func): @@ -35,9 +37,13 @@ def distributed_tracing_decorator_async(func): @functools.wraps(func) async def wrapper_use_tracer(self, *args, **kwargs): # type: (Any) -> Any - parent_span, original_span_from_sdk_context, original_span_instance = common.get_parent_and_original_contexts( - kwargs - ) + passed_in_parent = kwargs.pop("parent_span", None) + orig_wrapped_span = tracing_context.current_span.get() + wrapper_class = settings.tracing_implementation() + original_span_instance = None + if wrapper_class is not None: + original_span_instance = wrapper_class.get_current_span() + parent_span = common.get_parent_span(passed_in_parent) ans = None if common.should_use_trace(parent_span): common.set_span_contexts(parent_span) @@ -48,9 +54,9 @@ async def wrapper_use_tracer(self, *args, **kwargs): ans = await func(self, *args, **kwargs) child.finish() common.set_span_contexts(parent_span) - if getattr(parent_span, "was_created_by_azure_sdk", False): + if orig_wrapped_span is None and passed_in_parent is None: parent_span.finish() - common.set_span_contexts(original_span_from_sdk_context, span_instance=original_span_instance) + common.set_span_contexts(orig_wrapped_span, span_instance=original_span_instance) else: ans = await func(self, *args, **kwargs) return ans diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 3fd52b8931de..7fe09c9ad96b 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -84,41 +84,30 @@ def test_set_span_context(self): assert parent.span_instance == wrapper.get_current_span() assert tracing_context.current_span.get() == parent - def test_get_parent_and_original_contexts(self): + def test_get_parent_span(self): with ContextHelper(): opencensus = sys.modules["opencensus"] del sys.modules["opencensus"] - parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts({}) - assert orig_span_inst is None + parent = common.get_parent_span(None) assert parent is None - assert orig_tracing_context is None sys.modules["opencensus"] = opencensus - parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts({}) - assert orig_span_inst is None + parent = common.get_parent_span(None) assert parent.span_instance.name == "azure-sdk-for-python-first_parent_span" - assert orig_tracing_context is None tracer = tracer_module.Tracer(sampler=AlwaysOnSampler()) - parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts({}) - assert orig_span_inst is None + parent = common.get_parent_span(None) assert parent.span_instance.name == "azure-sdk-for-python-first_parent_span" - assert orig_tracing_context is None parent.finish() some_span = tracer.start_span(name="some_span") - new_parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts({}) - assert orig_span_inst == some_span + new_parent = common.get_parent_span(None) assert new_parent.span_instance.name == "some_span" - assert orig_tracing_context is None + some_span.finish() - kwarg = {"parent_span": parent.span_instance} - should_be_old_parent, orig_tracing_context, orig_span_inst = common.get_parent_and_original_contexts(kwarg) - assert kwarg.get("parent_span") is None - assert orig_span_inst == some_span + should_be_old_parent = common.get_parent_span(parent.span_instance) assert should_be_old_parent.span_instance == parent.span_instance - assert orig_tracing_context is None def test_should_use_trace(self): with ContextHelper(environ={"AZURE_TRACING_ONLY_PROPAGATE": "yes"}): From 91d1d25b16499c27b9149c99408da0e0f184a35c Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 00:26:03 -0700 Subject: [PATCH 075/105] calling a decorator decorator is redundant --- sdk/core/azure-core/azure/core/tracing/decorator.py | 2 +- .../azure-core/azure/core/tracing/decorator_async.py | 2 +- .../test_tracing_decorator_async.py | 10 +++++----- sdk/core/azure-core/tests/test_tracing_decorator.py | 8 ++++---- sdk/core/azure-core/tests/tracing_common.py | 1 - 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/decorator.py b/sdk/core/azure-core/azure/core/tracing/decorator.py index 5a95aae52e8f..d033256a799d 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator.py @@ -32,7 +32,7 @@ from azure.core.tracing.context import tracing_context -def distributed_tracing_decorator(func): +def distributed_trace(func): # type: (Callable[[Any], Any]) -> Callable[[Any], Any] @functools.wraps(func) def wrapper_use_tracer(self, *args, **kwargs): diff --git a/sdk/core/azure-core/azure/core/tracing/decorator_async.py b/sdk/core/azure-core/azure/core/tracing/decorator_async.py index e25d04628c77..6e88d235bc9b 100644 --- a/sdk/core/azure-core/azure/core/tracing/decorator_async.py +++ b/sdk/core/azure-core/azure/core/tracing/decorator_async.py @@ -32,7 +32,7 @@ from azure.core.tracing.context import tracing_context -def distributed_tracing_decorator_async(func): +def distributed_trace_async(func): # type: (Callable[[Any], Any]) -> Callable[[Any], Any] @functools.wraps(func) async def wrapper_use_tracer(self, *args, **kwargs): diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index cb6ffa759488..2c3f52151ce2 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -15,8 +15,8 @@ from azure.core.pipeline.policies import HTTPPolicy from azure.core.pipeline.transport import HttpTransport from azure.core.tracing.context import tracing_context -from azure.core.tracing.decorator import distributed_tracing_decorator -from azure.core.tracing.decorator_async import distributed_tracing_decorator_async +from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator_async import distributed_trace_async from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler @@ -26,7 +26,7 @@ class MockClient: - @distributed_tracing_decorator + @distributed_trace def __init__(self, policies=None, assert_current_span=False): self.request = HttpRequest("GET", "https://bing.com") if policies is None: @@ -45,7 +45,7 @@ def verify_request(self, request): assert current_span is not None return self.expected_response - @distributed_tracing_decorator_async + @distributed_trace_async async def make_request(self, numb_times, **kwargs): time.sleep(0.001) if numb_times < 1: @@ -55,7 +55,7 @@ async def make_request(self, numb_times, **kwargs): await self.make_request(numb_times - 1, **kwargs) return response - @distributed_tracing_decorator_async + @distributed_trace_async async def get_foo(self): time.sleep(0.001) return 5 diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 7fe09c9ad96b..3bf3dfc23b3a 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -19,7 +19,7 @@ from azure.core.pipeline.transport import HttpTransport from azure.core.tracing import common from azure.core.tracing.context import tracing_context -from azure.core.tracing.decorator import distributed_tracing_decorator +from azure.core.tracing.decorator import distributed_trace from azure.core.settings import settings from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module @@ -38,7 +38,7 @@ class MockClient: - @distributed_tracing_decorator + @distributed_trace def __init__(self, policies=None, assert_current_span=False): self.request = HttpRequest("GET", "https://bing.com") if policies is None: @@ -57,7 +57,7 @@ def verify_request(self, request): assert current_span is not None return self.expected_response - @distributed_tracing_decorator + @distributed_trace def make_request(self, numb_times, **kwargs): time.sleep(0.001) if numb_times < 1: @@ -67,7 +67,7 @@ def make_request(self, numb_times, **kwargs): self.make_request(numb_times - 1, **kwargs) return response - @distributed_tracing_decorator + @distributed_trace def get_foo(self): time.sleep(0.001) return 5 diff --git a/sdk/core/azure-core/tests/tracing_common.py b/sdk/core/azure-core/tests/tracing_common.py index 18c4e7a96bdc..a41d829bb8a8 100644 --- a/sdk/core/azure-core/tests/tracing_common.py +++ b/sdk/core/azure-core/tests/tracing_common.py @@ -12,7 +12,6 @@ from azure.core.pipeline.transport import HttpTransport from azure.core.tracing import common from azure.core.tracing.context import tracing_context -from azure.core.tracing.decorator import distributed_tracing_decorator from azure.core.settings import settings from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module From 236ef8b512116e292a308e377cafd9b032c4d37b Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 10:36:09 -0700 Subject: [PATCH 076/105] middle of writing tests --- .../pipeline/policies/distributed_tracing.py | 2 +- .../azure-core/tests/test_tracing_policy.py | 41 +++++++++++++++++++ sdk/core/azure-core/tests/tracing_common.py | 19 ++++----- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index b091d7cf0f6b..1a577cbf3400 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -47,7 +47,7 @@ class DistributedTracingPolicy(SansIOHTTPPolicy): """The policy to create spans for Azure Calls""" - def __init__(self, name_of_spans="Azure Call"): + def __init__(self, name_of_spans="span - http call"): # type: (str, str, str) -> None self.name_of_child_span = name_of_spans self.parent_span_dict = {} diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index e69de29bb2d1..ead5a84a4914 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -0,0 +1,41 @@ +# ------------------------------------ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +# ------------------------------------ +"""Tests for the distributed tracing policy.""" +import requests + +import pytest + +from azure.core.pipeline import ( + PipelineResponse, + PipelineRequest, + PipelineContext +) +from azure.core.pipeline.transport import ( + HttpRequest, + HttpResponse, +) +from azure.core.pipeline.transport import RequestsTransportResponse +from azure.core.pipeline.policies.universal import UserAgentPolicy +from azure.core.pipeline.policies.distributed_tracing import DistributedTracingPolicy +from tracing_common import ContextHelper, MockExporter +from opencensus.trace import tracer as tracer_module +from opencensus.trace.samplers import AlwaysOnSampler + + +def test_distributed_tracing_policy_solo(): + with ContextHelper(): + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) + policy = DistributedTracingPolicy() + + request = HttpRequest('GET', 'http://127.0.0.1/') + policy.on_request(PipelineRequest(request, PipelineContext(None))) + + trace.finish() + exporter.build_tree() + parent = exporter.root + network_span = parent.children[0] + assert network_span.span_data.name == "span - http call" + # assert request.headers["user-agent"].endswith("mytools") diff --git a/sdk/core/azure-core/tests/tracing_common.py b/sdk/core/azure-core/tests/tracing_common.py index a41d829bb8a8..d3ace542c3dc 100644 --- a/sdk/core/azure-core/tests/tracing_common.py +++ b/sdk/core/azure-core/tests/tracing_common.py @@ -25,6 +25,7 @@ except ImportError: import mock + class ContextHelper(object): def __init__(self, environ={}, tracer_to_use=None, should_only_propagate=None): self.orig_tracer = OpenCensusSpan.get_current_tracer() @@ -65,6 +66,7 @@ class MockExporter(Exporter): def __init__(self): self.root = None self._all_nodes = [] + self.parent_dict = {} def export(self, span_datas): # type: (List[SpanData]) -> None @@ -72,19 +74,16 @@ def export(self, span_datas): node = Node(sp) if not node.span_data.parent_span_id: self.root = node + parent_span_id = node.span_data.parent_span_id + if parent_span_id not in self.parent_dict: + self.parent_dict[parent_span_id] = [] + self.parent_dict[parent_span_id].append(node) self._all_nodes.append(node) def build_tree(self): - parent_dict = {} - for node in self._all_nodes: - parent_span_id = node.span_data.parent_span_id - if parent_span_id not in parent_dict: - parent_dict[parent_span_id] = [] - parent_dict[parent_span_id].append(node) - for node in self._all_nodes: - if node.span_data.span_id in parent_dict: + if node.span_data.span_id in self.parent_dict: node.children = sorted( - parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) + self.parent_dict[node.span_data.span_id], + key=lambda x: timestamp_to_microseconds(x.span_data.start_time), ) - From 056bc2ed7eaf6953a47e6cef1d75ecbd1cb79919 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 10:54:11 -0700 Subject: [PATCH 077/105] fix settings --- sdk/core/azure-core/azure/core/settings.py | 19 ++++++++++++++++--- sdk/core/azure-core/tests/test_settings.py | 14 ++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index 0a32748d4be1..b440c4f0169d 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -135,7 +135,7 @@ def get_opencensus_span_if_opencensus_is_imported(): return get_opencensus_span() -_tracing_implementation_dict = {"opencensus": get_opencensus_span()} +_tracing_implementation_dict = {"opencensus": get_opencensus_span} def convert_tracing_impl(value): @@ -156,8 +156,21 @@ def convert_tracing_impl(value): if value is None: return get_opencensus_span_if_opencensus_is_imported() - value = value.lower() if isinstance(value, six.string_types) else value - return _tracing_implementation_dict.get(value, value) + wrapper_class = None + if isinstance(value, six.string_types): + value = value.lower() + get_wrapper_class = _tracing_implementation_dict.get(value, lambda: _Unset) + wrapper_class = get_wrapper_class() + if wrapper_class is _Unset: + raise ValueError( + "Cannot convert {} to AbstractSpan, valid values are: {}".format( + value, ", ".join(_tracing_implementation_dict) + ) + ) + else: + wrapper_class = value + + return wrapper_class class PrioritizedSetting(object): diff --git a/sdk/core/azure-core/tests/test_settings.py b/sdk/core/azure-core/tests/test_settings.py index 99bbb6144d41..11e9dc48d523 100644 --- a/sdk/core/azure-core/tests/test_settings.py +++ b/sdk/core/azure-core/tests/test_settings.py @@ -167,11 +167,17 @@ def test_convert_logging_bad(self): with pytest.raises(ValueError): m.convert_logging("junk") - def test_get_opencensus_span_if_opencensus_is_imported(self): - del sys.modules['opencensus'] - assert m.get_opencensus_span_if_opencensus_is_imported() == None + def test_convert_implementation(self): + if "opencensus" in sys.modules: + del sys.modules["opencensus"] + assert m.convert_tracing_impl(None) is None + assert m.convert_tracing_impl("opencensus") is not None import opencensus - assert m.get_opencensus_span_if_opencensus_is_imported() is not None + + assert m.convert_tracing_impl(None) is not None + assert m.convert_tracing_impl("opencensus") is not None + with pytest.raises(ValueError): + m.convert_tracing_impl("does not exist!!") _standard_settings = ["log_level", "tracing_enabled"] From 4adb5c37bc5fdd903c08080e934166bbfa0e16e9 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 12:01:08 -0700 Subject: [PATCH 078/105] add tests --- .../pipeline/policies/distributed_tracing.py | 3 +- .../azure-core/tests/test_tracing_policy.py | 101 ++++++++++++++---- 2 files changed, 84 insertions(+), 20 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index 1a577cbf3400..3dccb5eecad8 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -30,6 +30,7 @@ from azure.core.tracing.abstract_span import AbstractSpan from azure.core.tracing.common import set_span_contexts from azure.core.pipeline.policies import SansIOHTTPPolicy +from azure.core.tracing.common import get_parent_span from azure.core.settings import settings try: @@ -70,7 +71,7 @@ def set_header(self, request, span): def on_request(self, request, **kwargs): # type: (PipelineRequest[HTTPRequestType], Any) -> None - parent_span = tracing_context.current_span.get() # type: AbstractSpan + parent_span = get_parent_span(None) # type: AbstractSpan if parent_span is None: return diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index ead5a84a4914..20bb5994c283 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -3,39 +3,102 @@ # Licensed under the MIT License. # ------------------------------------ """Tests for the distributed tracing policy.""" -import requests - -import pytest - -from azure.core.pipeline import ( - PipelineResponse, - PipelineRequest, - PipelineContext -) -from azure.core.pipeline.transport import ( - HttpRequest, - HttpResponse, -) -from azure.core.pipeline.transport import RequestsTransportResponse -from azure.core.pipeline.policies.universal import UserAgentPolicy + +from azure.core.pipeline import PipelineResponse, PipelineRequest, PipelineContext from azure.core.pipeline.policies.distributed_tracing import DistributedTracingPolicy -from tracing_common import ContextHelper, MockExporter +from azure.core.pipeline.policies.universal import UserAgentPolicy +from azure.core.pipeline.transport import HttpRequest, HttpResponse from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler +from tracing_common import ContextHelper, MockExporter def test_distributed_tracing_policy_solo(): + """Test policy with no other policy and happy path""" with ContextHelper(): exporter = MockExporter() trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) policy = DistributedTracingPolicy() - request = HttpRequest('GET', 'http://127.0.0.1/') - policy.on_request(PipelineRequest(request, PipelineContext(None))) + request = HttpRequest("GET", "http://127.0.0.1/") + + pipeline_request = PipelineRequest(request, PipelineContext(None)) + policy.on_request(pipeline_request) + + response = HttpResponse(request, None) + response.headers = request.headers + response.status_code = 202 + response.headers["x-ms-request-id"] = "some request id" + policy.on_response(pipeline_request, PipelineResponse(request, response, PipelineContext(None))) + + trace.finish() + exporter.build_tree() + parent = exporter.root + network_span = parent.children[0] + assert network_span.span_data.name == "span - http call" + assert network_span.span_data.attributes.get("http.method") == "GET" + assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" + assert network_span.span_data.attributes.get("http.user_agent") == "" + assert network_span.span_data.attributes.get("x-ms-request-id") == "some request id" + assert network_span.span_data.attributes.get("http.status_code") == 202 + + +def test_distributed_tracing_policy_exception(): + """Test Policy on when an exception happens.""" + with ContextHelper(): + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) + policy = DistributedTracingPolicy() + + request = HttpRequest("GET", "http://127.0.0.1/") + + pipeline_request = PipelineRequest(request, PipelineContext(None)) + policy.on_request(pipeline_request) + + policy.on_exception(pipeline_request) + + trace.finish() + exporter.build_tree() + parent = exporter.root + network_span = parent.children[0] + assert network_span.span_data.name == "span - http call" + assert network_span.span_data.attributes.get("http.method") == "GET" + assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" + assert network_span.span_data.attributes.get("http.user_agent") == "" + assert network_span.span_data.attributes.get("x-ms-request-id") is None + assert network_span.span_data.attributes.get("http.status_code") == 504 + + +def test_distributed_tracing_policy_with_usergent(): + """Test policy working with user agent.""" + with ContextHelper(environ={"AZURE_HTTP_USER_AGENT": "mytools"}): + exporter = MockExporter() + trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) + policy = DistributedTracingPolicy() + + request = HttpRequest("GET", "http://127.0.0.1/") + + pipeline_request = PipelineRequest(request, PipelineContext(None)) + + user_agent = UserAgentPolicy() + user_agent.on_request(pipeline_request) + policy.on_request(pipeline_request) + + response = HttpResponse(request, None) + response.headers = request.headers + response.status_code = 202 + response.headers["x-ms-request-id"] = "some request id" + pipeline_response = PipelineResponse(request, response, PipelineContext(None)) + policy.on_response(pipeline_request, pipeline_response) + user_agent.on_response(pipeline_request, pipeline_response) trace.finish() exporter.build_tree() parent = exporter.root network_span = parent.children[0] assert network_span.span_data.name == "span - http call" - # assert request.headers["user-agent"].endswith("mytools") + assert network_span.span_data.attributes.get("http.method") == "GET" + assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" + assert network_span.span_data.attributes.get("http.user_agent").endswith("mytools") + assert network_span.span_data.attributes.get("x-ms-request-id") == "some request id" + assert network_span.span_data.attributes.get("http.status_code") == 202 From a7ede6b7bd2331fa4af72a4d2bff5248d40bfc3e Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 13:26:00 -0700 Subject: [PATCH 079/105] test propogation also happens --- sdk/core/azure-core/tests/test_tracing_policy.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index 20bb5994c283..090e73787533 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -29,6 +29,11 @@ def test_distributed_tracing_policy_solo(): response.headers = request.headers response.status_code = 202 response.headers["x-ms-request-id"] = "some request id" + + ctx = trace.span_context + header = trace.propagator.to_headers(ctx) + assert request.headers.get("traceparent") == header.get("traceparent") + policy.on_response(pipeline_request, PipelineResponse(request, response, PipelineContext(None))) trace.finish() @@ -55,6 +60,10 @@ def test_distributed_tracing_policy_exception(): pipeline_request = PipelineRequest(request, PipelineContext(None)) policy.on_request(pipeline_request) + ctx = trace.span_context + header = trace.propagator.to_headers(ctx) + assert request.headers.get("traceparent") == header.get("traceparent") + policy.on_exception(pipeline_request) trace.finish() @@ -89,6 +98,11 @@ def test_distributed_tracing_policy_with_usergent(): response.status_code = 202 response.headers["x-ms-request-id"] = "some request id" pipeline_response = PipelineResponse(request, response, PipelineContext(None)) + + ctx = trace.span_context + header = trace.propagator.to_headers(ctx) + assert request.headers.get("traceparent") == header.get("traceparent") + policy.on_response(pipeline_request, pipeline_response) user_agent.on_response(pipeline_request, pipeline_response) From 720c44e332e2f2a204b466546169cefd8c746eb2 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 13:28:49 -0700 Subject: [PATCH 080/105] more elegant code --- sdk/core/azure-core/azure/core/settings.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sdk/core/azure-core/azure/core/settings.py b/sdk/core/azure-core/azure/core/settings.py index b440c4f0169d..e1b4c738cdb8 100644 --- a/sdk/core/azure-core/azure/core/settings.py +++ b/sdk/core/azure-core/azure/core/settings.py @@ -156,7 +156,7 @@ def convert_tracing_impl(value): if value is None: return get_opencensus_span_if_opencensus_is_imported() - wrapper_class = None + wrapper_class = value if isinstance(value, six.string_types): value = value.lower() get_wrapper_class = _tracing_implementation_dict.get(value, lambda: _Unset) @@ -167,8 +167,6 @@ def convert_tracing_impl(value): value, ", ".join(_tracing_implementation_dict) ) ) - else: - wrapper_class = value return wrapper_class From c3d7eb8bbd9288fad1afcd2f8e0e4a9967d79c68 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 14:52:06 -0700 Subject: [PATCH 081/105] await async stuff --- .../azure_core_asynctests/test_tracing_decorator_async.py | 8 ++++---- sdk/core/azure-core/tests/test_tracing_decorator.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index 2c3f52151ce2..3341a0764762 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -47,7 +47,7 @@ def verify_request(self, request): @distributed_trace_async async def make_request(self, numb_times, **kwargs): - time.sleep(0.001) + time.sleep(0.01) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -57,7 +57,7 @@ async def make_request(self, numb_times, **kwargs): @distributed_trace_async async def get_foo(self): - time.sleep(0.001) + time.sleep(0.01) return 5 @@ -130,13 +130,13 @@ async def for_test_different_settings(): @pytest.mark.asyncio async def test_span_with_opencensus_complicated(): - for_test_different_settings() + await for_test_different_settings() @pytest.mark.asyncio async def test_span_with_opencensus_passed_in_complicated(): with ContextHelper(tracer_to_use="opencensus"): - for_test_different_settings() + await for_test_different_settings() @pytest.mark.asyncio diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 3bf3dfc23b3a..57381dab9ace 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -59,7 +59,7 @@ def verify_request(self, request): @distributed_trace def make_request(self, numb_times, **kwargs): - time.sleep(0.001) + time.sleep(0.01) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -69,7 +69,7 @@ def make_request(self, numb_times, **kwargs): @distributed_trace def get_foo(self): - time.sleep(0.001) + time.sleep(0.01) return 5 class TestCommon(unittest.TestCase): From f503b6e2302c229e63fab07b1bd5698217d30fa5 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 14:53:45 -0700 Subject: [PATCH 082/105] add await for async --- .../azure_core_asynctests/test_tracing_decorator_async.py | 8 ++++---- sdk/core/azure-core/tests/test_tracing_decorator.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index 2c3f52151ce2..3341a0764762 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -47,7 +47,7 @@ def verify_request(self, request): @distributed_trace_async async def make_request(self, numb_times, **kwargs): - time.sleep(0.001) + time.sleep(0.01) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -57,7 +57,7 @@ async def make_request(self, numb_times, **kwargs): @distributed_trace_async async def get_foo(self): - time.sleep(0.001) + time.sleep(0.01) return 5 @@ -130,13 +130,13 @@ async def for_test_different_settings(): @pytest.mark.asyncio async def test_span_with_opencensus_complicated(): - for_test_different_settings() + await for_test_different_settings() @pytest.mark.asyncio async def test_span_with_opencensus_passed_in_complicated(): with ContextHelper(tracer_to_use="opencensus"): - for_test_different_settings() + await for_test_different_settings() @pytest.mark.asyncio diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 3bf3dfc23b3a..57381dab9ace 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -59,7 +59,7 @@ def verify_request(self, request): @distributed_trace def make_request(self, numb_times, **kwargs): - time.sleep(0.001) + time.sleep(0.01) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -69,7 +69,7 @@ def make_request(self, numb_times, **kwargs): @distributed_trace def get_foo(self): - time.sleep(0.001) + time.sleep(0.01) return 5 class TestCommon(unittest.TestCase): From 6743bd1ccc85ed9c2245611a612b4b3ef9fd3a4f Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 15:31:41 -0700 Subject: [PATCH 083/105] should only have to wait a 1/1000 of a second --- .../azure_core_asynctests/test_tracing_decorator_async.py | 4 ++-- sdk/core/azure-core/tests/test_tracing_decorator.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index 3341a0764762..9732dccdde92 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -47,7 +47,7 @@ def verify_request(self, request): @distributed_trace_async async def make_request(self, numb_times, **kwargs): - time.sleep(0.01) + time.sleep(0.001) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -57,7 +57,7 @@ async def make_request(self, numb_times, **kwargs): @distributed_trace_async async def get_foo(self): - time.sleep(0.01) + time.sleep(0.001) return 5 diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 57381dab9ace..3bf3dfc23b3a 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -59,7 +59,7 @@ def verify_request(self, request): @distributed_trace def make_request(self, numb_times, **kwargs): - time.sleep(0.01) + time.sleep(0.001) if numb_times < 1: return None response = self.pipeline.run(self.request, **kwargs) @@ -69,7 +69,7 @@ def make_request(self, numb_times, **kwargs): @distributed_trace def get_foo(self): - time.sleep(0.01) + time.sleep(0.001) return 5 class TestCommon(unittest.TestCase): From c070b774f16fea0132635a8ad136c03bf70e0412 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 18:23:14 -0700 Subject: [PATCH 084/105] fix tests spans too short --- .../azure/core/tracing/ext/opencensus_span.py | 2 +- .../test_tracing_decorator_async.py | 17 ++++------------- .../azure-core/tests/test_tracing_decorator.py | 17 ++++++----------- sdk/core/azure-core/tests/tracing_common.py | 4 +--- setup.cfg | 3 --- 5 files changed, 12 insertions(+), 31 deletions(-) delete mode 100644 setup.cfg diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 61bfbf2c0ec7..bab7dce34f32 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -33,7 +33,7 @@ def __init__(self, span=None, name="span"): """ if not span: tracer = self.get_current_tracer() - span = tracer.span(name=name) + span = tracer.span(name=name) # type: Span self._span_instance = span @property diff --git a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py index 9732dccdde92..a1d6e19e1b45 100644 --- a/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py +++ b/sdk/core/azure-core/tests/azure_core_asynctests/test_tracing_decorator_async.py @@ -28,6 +28,7 @@ class MockClient: @distributed_trace def __init__(self, policies=None, assert_current_span=False): + time.sleep(0.001) self.request = HttpRequest("GET", "https://bing.com") if policies is None: policies = [] @@ -99,9 +100,10 @@ async def test_with_opencencus_used(): assert not parent.children[1].children +@pytest.mark.parametrize("value", [None, "opencensus"]) @pytest.mark.asyncio -async def for_test_different_settings(): - with ContextHelper(): +async def test_span_with_opencensus_complicated(value): + with ContextHelper(tracer_to_use=value): exporter = MockExporter() trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) with trace.start_span(name="OverAll") as parent: @@ -128,17 +130,6 @@ async def for_test_different_settings(): assert len(children) == 2 -@pytest.mark.asyncio -async def test_span_with_opencensus_complicated(): - await for_test_different_settings() - - -@pytest.mark.asyncio -async def test_span_with_opencensus_passed_in_complicated(): - with ContextHelper(tracer_to_use="opencensus"): - await for_test_different_settings() - - @pytest.mark.asyncio async def test_should_only_propagate(): with ContextHelper(should_only_propagate=True): diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 3bf3dfc23b3a..7b27ee392a0f 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -40,6 +40,7 @@ class MockClient: @distributed_trace def __init__(self, policies=None, assert_current_span=False): + time.sleep(0.001) self.request = HttpRequest("GET", "https://bing.com") if policies is None: policies = [] @@ -72,7 +73,7 @@ def get_foo(self): time.sleep(0.001) return 5 -class TestCommon(unittest.TestCase): +class TestCommon(object): def test_set_span_context(self): with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): wrapper = settings.tracing_implementation() @@ -119,7 +120,7 @@ def test_should_use_trace(self): assert common.should_use_trace(None) == False -class TestDecorator(unittest.TestCase): +class TestDecorator(object): def test_with_nothing_imported(self): with ContextHelper(): opencensus = sys.modules["opencensus"] @@ -152,8 +153,9 @@ def test_with_opencencus_used(self): assert parent.children[1].span_data.name == "MockClient.get_foo" assert not parent.children[1].children - def for_test_different_settings(self): - with ContextHelper(): + @pytest.mark.parametrize("value", ["opencensus", None]) + def test_span_with_opencensus_complicated(self, value): + with ContextHelper(tracer_to_use=value) as ctx: exporter = MockExporter() trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) with trace.start_span(name="OverAll") as parent: @@ -179,13 +181,6 @@ def for_test_different_settings(self): children = parent.children[1].children assert len(children) == 2 - def test_span_with_opencensus_complicated(self): - self.for_test_different_settings() - - def test_span_with_opencensus_passed_in_complicated(self): - with ContextHelper(tracer_to_use="opencensus"): - self.for_test_different_settings() - def test_should_only_propagate(self): with ContextHelper(should_only_propagate=True): exporter = MockExporter() diff --git a/sdk/core/azure-core/tests/tracing_common.py b/sdk/core/azure-core/tests/tracing_common.py index a41d829bb8a8..7967640180cf 100644 --- a/sdk/core/azure-core/tests/tracing_common.py +++ b/sdk/core/azure-core/tests/tracing_common.py @@ -18,7 +18,6 @@ from opencensus.trace.span_data import SpanData from opencensus.trace.samplers import AlwaysOnSampler from opencensus.trace.base_exporter import Exporter -from opencensus.common.utils import timestamp_to_microseconds try: from unittest import mock @@ -85,6 +84,5 @@ def build_tree(self): for node in self._all_nodes: if node.span_data.span_id in parent_dict: node.children = sorted( - parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) + parent_dict[node.span_data.span_id], key=lambda x: x.span_data.start_time ) - diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index d6964917ce89..000000000000 --- a/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[tool:pytest] -addopts = --durations=10 --cov --cov-report= -norecursedirs = models \ No newline at end of file From d106a162862b9f74b1e0ac7fb3089e0a573328a1 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 18:25:53 -0700 Subject: [PATCH 085/105] accidentally deleted setup.cfg --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 000000000000..d6964917ce89 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[tool:pytest] +addopts = --durations=10 --cov --cov-report= +norecursedirs = models \ No newline at end of file From dc869860da29b79bfc53c18eb7d8690787c850a2 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 18:36:07 -0700 Subject: [PATCH 086/105] add component --- .../azure/core/pipeline/policies/distributed_tracing.py | 2 ++ sdk/core/azure-core/tests/test_tracing_policy.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index 3dccb5eecad8..aee444f7725e 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -52,6 +52,7 @@ def __init__(self, name_of_spans="span - http call"): # type: (str, str, str) -> None self.name_of_child_span = name_of_spans self.parent_span_dict = {} + self._span_component = ("component", "http") self._http_user_agent = "http.user_agent" self._http_method = "http.method" self._http_url = "http.url" @@ -68,6 +69,7 @@ def set_header(self, request, span): span.add_attribute(self._http_method, request.http_request.method) span.add_attribute(self._http_url, request.http_request.url) span.add_attribute(self._http_user_agent, request.http_request.headers.get("User-Agent", "")) + span.add_attribute(*self._span_component) def on_request(self, request, **kwargs): # type: (PipelineRequest[HTTPRequestType], Any) -> None diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index 090e73787533..7d56d6293321 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -42,6 +42,7 @@ def test_distributed_tracing_policy_solo(): network_span = parent.children[0] assert network_span.span_data.name == "span - http call" assert network_span.span_data.attributes.get("http.method") == "GET" + assert network_span.span_data.attributes.get("component") == "http" assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" assert network_span.span_data.attributes.get("http.user_agent") == "" assert network_span.span_data.attributes.get("x-ms-request-id") == "some request id" @@ -72,6 +73,7 @@ def test_distributed_tracing_policy_exception(): network_span = parent.children[0] assert network_span.span_data.name == "span - http call" assert network_span.span_data.attributes.get("http.method") == "GET" + assert network_span.span_data.attributes.get("component") == "http" assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" assert network_span.span_data.attributes.get("http.user_agent") == "" assert network_span.span_data.attributes.get("x-ms-request-id") is None @@ -112,6 +114,7 @@ def test_distributed_tracing_policy_with_usergent(): network_span = parent.children[0] assert network_span.span_data.name == "span - http call" assert network_span.span_data.attributes.get("http.method") == "GET" + assert network_span.span_data.attributes.get("component") == "http" assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" assert network_span.span_data.attributes.get("http.user_agent").endswith("mytools") assert network_span.span_data.attributes.get("x-ms-request-id") == "some request id" From 7d65a46a136a8224c22e721e3b3991ae54c65f27 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Tue, 16 Jul 2019 22:46:36 -0700 Subject: [PATCH 087/105] added decorators to convenience layer --- .../azure/keyvault/keys/_client.py | 20 +++++++++++++++++- .../azure/keyvault/keys/aio/_client.py | 21 ++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py index 996683569bb9..b237ceef3520 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py @@ -2,10 +2,11 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. # ------------------------------------ -from typing import Any, Dict, Generator, Mapping, Optional, List from datetime import datetime +from typing import Any, Dict, Generator, Mapping, Optional, List from azure.core.exceptions import ResourceExistsError, ResourceNotFoundError +from azure.core.tracing.decorator import distributed_trace from ._internal import _KeyVaultClientBase from ._models import Key, KeyBase, DeletedKey, KeyOperationResult @@ -25,6 +26,7 @@ class KeyClient(_KeyVaultClientBase): # pylint:disable=protected-access + @distributed_trace def create_key( self, name, @@ -100,6 +102,7 @@ def create_key( ) return Key._from_key_bundle(bundle) + @distributed_trace def create_rsa_key( self, name, @@ -164,6 +167,7 @@ def create_rsa_key( **kwargs ) + @distributed_trace def create_ec_key( self, name, @@ -231,6 +235,7 @@ def create_ec_key( **kwargs ) + @distributed_trace def delete_key(self, name, **kwargs): # type: (str, Mapping[str, Any]) -> DeletedKey """Deletes a key from the Key Vault. @@ -258,6 +263,7 @@ def delete_key(self, name, **kwargs): bundle = self._client.delete_key(self.vault_url, name, error_map={404: ResourceNotFoundError}, **kwargs) return DeletedKey._from_deleted_key_bundle(bundle) + @distributed_trace def get_key(self, name, version=None, **kwargs): # type: (str, Optional[str], Mapping[str, Any]) -> Key """Gets the public part of a stored key. @@ -288,6 +294,7 @@ def get_key(self, name, version=None, **kwargs): ) return Key._from_key_bundle(bundle) + @distributed_trace def get_deleted_key(self, name, **kwargs): # type: (str, Mapping[str, Any]) -> DeletedKey """Gets a deleted key from the Key Vault @@ -313,6 +320,7 @@ def get_deleted_key(self, name, **kwargs): bundle = self._client.get_deleted_key(self.vault_url, name, error_map={404: ResourceNotFoundError}, **kwargs) return DeletedKey._from_deleted_key_bundle(bundle) + @distributed_trace def list_deleted_keys(self, **kwargs): # type: (Mapping[str, Any]) -> Generator[DeletedKey] """Lists the deleted keys in the Key Vault @@ -341,6 +349,7 @@ def list_deleted_keys(self, **kwargs): pages = self._client.get_deleted_keys(self._vault_url, maxresults=max_page_size, **kwargs) return (DeletedKey._from_deleted_key_item(item) for item in pages) + @distributed_trace def list_keys(self, **kwargs): # type: (Mapping[str, Any]) -> Generator[KeyBase] """List the keys in the Key Vault @@ -368,6 +377,7 @@ def list_keys(self, **kwargs): pages = self._client.get_keys(self._vault_url, maxresults=max_page_size, **kwargs) return (KeyBase._from_key_item(item) for item in pages) + @distributed_trace def list_key_versions(self, name, **kwargs): # type: (str, Mapping[str, Any]) -> Generator[KeyBase] """Retrieves a list of individual key versions with the same key name. @@ -393,6 +403,7 @@ def list_key_versions(self, name, **kwargs): pages = self._client.get_key_versions(self._vault_url, name, maxresults=max_page_size, **kwargs) return (KeyBase._from_key_item(item) for item in pages) + @distributed_trace def purge_deleted_key(self, name, **kwargs): # type: (str, Mapping[str, Any]) -> None """Permanently deletes the specified key. @@ -417,6 +428,7 @@ def purge_deleted_key(self, name, **kwargs): """ self._client.purge_deleted_key(self.vault_url, name, kwargs) + @distributed_trace def recover_deleted_key(self, name, **kwargs): # type: (str, Mapping[str, Any]) -> Key """Recovers the deleted key to its latest version. @@ -444,6 +456,7 @@ def recover_deleted_key(self, name, **kwargs): bundle = self._client.recover_deleted_key(self.vault_url, name, kwargs) return Key._from_key_bundle(bundle) + @distributed_trace def update_key( self, name, version=None, key_operations=None, enabled=None, expires=None, not_before=None, tags=None, **kwargs ): @@ -501,6 +514,7 @@ def update_key( ) return Key._from_key_bundle(bundle) + @distributed_trace def backup_key(self, name, **kwargs): # type: (str, Mapping[str, Any]) -> bytes """Backs up the specified key. @@ -538,6 +552,7 @@ def backup_key(self, name, **kwargs): backup_result = self._client.backup_key(self.vault_url, name, error_map={404: ResourceNotFoundError}, **kwargs) return backup_result.value + @distributed_trace def restore_key(self, backup, **kwargs): # type: (bytes, Mapping[str, Any]) -> Key """Restores a backed up key to the Key Vault @@ -574,6 +589,7 @@ def restore_key(self, backup, **kwargs): bundle = self._client.restore_key(self.vault_url, backup, error_map={409: ResourceExistsError}, **kwargs) return Key._from_key_bundle(bundle) + @distributed_trace def import_key(self, name, key, hsm=None, enabled=None, not_before=None, expires=None, tags=None, **kwargs): # type: (str, List[str], Optional[bool], Optional[bool], Optional[datetime], Optional[datetime], Optional[Dict[str, str]], Mapping[str, Any]) -> Key """Imports an externally created key, stores it, and returns the key to the client. @@ -611,6 +627,7 @@ def import_key(self, name, key, hsm=None, enabled=None, not_before=None, expires ) return Key._from_key_bundle(bundle) + @distributed_trace def wrap_key(self, name, algorithm, value, version=None, **kwargs): # type: (str, str, Optional[str], bytes, Mapping[str, Any]) -> KeyOperationResult """Wraps a symmetric key using a specified key. @@ -646,6 +663,7 @@ def wrap_key(self, name, algorithm, value, version=None, **kwargs): ) return KeyOperationResult(id=bundle.kid, value=bundle.result) + @distributed_trace def unwrap_key(self, name, algorithm, value, version=None, **kwargs): # type: (str, str, Optional[str], bytes, Mapping[str, Any]) -> KeyOperationResult """Unwraps a symmetric key using the specified key that was initially used diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/aio/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/aio/_client.py index 3e686b185d62..12ed9fe65e55 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/aio/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/aio/_client.py @@ -6,8 +6,10 @@ from typing import Any, AsyncIterable, Mapping, Optional, Dict, List from azure.core.exceptions import ResourceExistsError, ResourceNotFoundError - +from azure.core.tracing.decorator import distributed_trace +from azure.core.tracing.decorator_async import distributed_trace_async from azure.keyvault.keys._models import Key, DeletedKey, KeyBase, KeyOperationResult + from ._internal import _AsyncKeyVaultClientBase, AsyncPagingAdapter @@ -25,6 +27,7 @@ class KeyClient(_AsyncKeyVaultClientBase): # pylint:disable=protected-access + @distributed_trace_async async def get_key(self, name: str, version: Optional[str] = None, **kwargs: Mapping[str, Any]) -> Key: """Gets the public part of a stored key. @@ -57,6 +60,7 @@ async def get_key(self, name: str, version: Optional[str] = None, **kwargs: Mapp ) return Key._from_key_bundle(bundle) + @distributed_trace_async async def create_key( self, name: str, @@ -131,6 +135,7 @@ async def create_key( ) return Key._from_key_bundle(bundle) + @distributed_trace_async async def create_rsa_key( self, name: str, @@ -194,6 +199,7 @@ async def create_rsa_key( **kwargs, ) + @distributed_trace_async async def create_ec_key( self, name: str, @@ -259,6 +265,7 @@ async def create_ec_key( **kwargs, ) + @distributed_trace_async async def update_key( self, name: str, @@ -324,6 +331,7 @@ async def update_key( ) return Key._from_key_bundle(bundle) + @distributed_trace def list_keys(self, **kwargs: Mapping[str, Any]) -> AsyncIterable[KeyBase]: """List keys in the specified vault. @@ -351,6 +359,7 @@ def list_keys(self, **kwargs: Mapping[str, Any]) -> AsyncIterable[KeyBase]: iterable = AsyncPagingAdapter(pages, KeyBase._from_key_item) return iterable + @distributed_trace def list_key_versions(self, name: str, **kwargs: Mapping[str, Any]) -> AsyncIterable[KeyBase]: """Retrieves a list of individual key versions with the same key name. @@ -376,6 +385,7 @@ def list_key_versions(self, name: str, **kwargs: Mapping[str, Any]) -> AsyncIter iterable = AsyncPagingAdapter(pages, KeyBase._from_key_item) return iterable + @distributed_trace_async async def backup_key(self, name: str, **kwargs: Mapping[str, Any]) -> bytes: """Requests a backup of the specified key to the client. @@ -413,6 +423,7 @@ async def backup_key(self, name: str, **kwargs: Mapping[str, Any]) -> bytes: ) return backup_result.value + @distributed_trace_async async def restore_key(self, backup: bytes, **kwargs: Mapping[str, Any]) -> Key: """Restores a backed up key to a vault. @@ -448,6 +459,7 @@ async def restore_key(self, backup: bytes, **kwargs: Mapping[str, Any]) -> Key: bundle = await self._client.restore_key(self.vault_url, backup, error_map={409: ResourceExistsError}, **kwargs) return Key._from_key_bundle(bundle) + @distributed_trace_async async def delete_key(self, name: str, **kwargs: Mapping[str, Any]) -> DeletedKey: """Deletes a key from the Key Vault. @@ -474,6 +486,7 @@ async def delete_key(self, name: str, **kwargs: Mapping[str, Any]) -> DeletedKey bundle = await self._client.delete_key(self.vault_url, name, error_map={404: ResourceNotFoundError}, **kwargs) return DeletedKey._from_deleted_key_bundle(bundle) + @distributed_trace_async async def get_deleted_key(self, name: str, **kwargs: Mapping[str, Any]) -> DeletedKey: """Gets a deleted key from the Key Vault @@ -501,6 +514,7 @@ async def get_deleted_key(self, name: str, **kwargs: Mapping[str, Any]) -> Delet ) return DeletedKey._from_deleted_key_bundle(bundle) + @distributed_trace def list_deleted_keys(self, **kwargs: Mapping[str, Any]) -> AsyncIterable[DeletedKey]: """Lists the deleted keys in the specified vault. @@ -529,6 +543,7 @@ def list_deleted_keys(self, **kwargs: Mapping[str, Any]) -> AsyncIterable[Delete iterable = AsyncPagingAdapter(pages, DeletedKey._from_deleted_key_item) return iterable + @distributed_trace_async async def purge_deleted_key(self, name: str, **kwargs: Mapping[str, Any]) -> None: """Permanently deletes the specified key. @@ -552,6 +567,7 @@ async def purge_deleted_key(self, name: str, **kwargs: Mapping[str, Any]) -> Non """ await self._client.purge_deleted_key(self.vault_url, name, **kwargs) + @distributed_trace_async async def recover_deleted_key(self, name: str, **kwargs: Mapping[str, Any]) -> Key: """Recovers the deleted key to its latest version. @@ -578,6 +594,7 @@ async def recover_deleted_key(self, name: str, **kwargs: Mapping[str, Any]) -> K bundle = await self._client.recover_deleted_key(self.vault_url, name, **kwargs) return Key._from_key_bundle(bundle) + @distributed_trace_async async def import_key( self, name: str, @@ -624,6 +641,7 @@ async def import_key( ) return Key._from_key_bundle(bundle) + @distributed_trace_async async def wrap_key( self, name: str, algorithm: str, value: bytes, version: Optional[str] = None, **kwargs: Mapping[str, Any] ) -> KeyOperationResult: @@ -660,6 +678,7 @@ async def wrap_key( ) return KeyOperationResult(id=bundle.kid, value=bundle.result) + @distributed_trace_async async def unwrap_key( self, name: str, algorithm: str, value: bytes, version: Optional[str] = None, **kwargs: Mapping[str, Any] ) -> KeyOperationResult: From 3c6530e5e1c7f05a92100ca3f9ec65b7543ca2ff Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 01:02:01 -0700 Subject: [PATCH 088/105] clear all context for the tests in context helper! --- .../azure-core/tests/test_tracing_context.py | 1 + .../tests/test_tracing_decorator.py | 25 +++----- .../tests/test_tracing_implementations.py | 58 +------------------ sdk/core/azure-core/tests/tracing_common.py | 19 +++--- 4 files changed, 19 insertions(+), 84 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_context.py b/sdk/core/azure-core/tests/test_tracing_context.py index a537d4200179..59aa2c8a8b60 100644 --- a/sdk/core/azure-core/tests/test_tracing_context.py +++ b/sdk/core/azure-core/tests/test_tracing_context.py @@ -22,6 +22,7 @@ def __init__(self, environ={}, tracer_to_use=None): def __enter__(self): self.orig_sdk_context_span = tracing_context.current_span.get() + tracing_context.current_span.clear() settings.tracing_implementation.set_value(self.tracer_to_use) self.os_env.start() return self diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index 7b27ee392a0f..daa4ef84db9b 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -4,37 +4,27 @@ # ------------------------------------ """The tests for decorators.py and common.py""" -import unittest - try: from unittest import mock except ImportError: import mock import sys -import os +import time + +import pytest from azure.core import HttpRequest from azure.core.pipeline import Pipeline, PipelineResponse from azure.core.pipeline.policies import HTTPPolicy from azure.core.pipeline.transport import HttpTransport +from azure.core.settings import settings from azure.core.tracing import common from azure.core.tracing.context import tracing_context from azure.core.tracing.decorator import distributed_trace -from azure.core.settings import settings from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler from tracing_common import ContextHelper, MockExporter -import time -import pytest - -try: - from typing import TYPE_CHECKING -except ImportError: - TYPE_CHECKING = False - -if TYPE_CHECKING: - from typing import List class MockClient: @@ -73,6 +63,7 @@ def get_foo(self): time.sleep(0.001) return 5 + class TestCommon(object): def test_set_span_context(self): with ContextHelper(environ={"AZURE_SDK_TRACING_IMPLEMENTATION": "opencensus"}): @@ -113,11 +104,11 @@ def test_get_parent_span(self): def test_should_use_trace(self): with ContextHelper(environ={"AZURE_TRACING_ONLY_PROPAGATE": "yes"}): parent_span = OpenCensusSpan() - assert common.should_use_trace(parent_span) == False - assert common.should_use_trace(None) == False + assert common.should_use_trace(parent_span) is False + assert common.should_use_trace(None) is False parent_span = OpenCensusSpan() assert common.should_use_trace(parent_span) - assert common.should_use_trace(None) == False + assert common.should_use_trace(None) is False class TestDecorator(object): diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 5793685a9a1e..ee54a8458d80 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -14,62 +14,7 @@ from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler -from opencensus.trace.base_exporter import Exporter -from opencensus.common.utils import timestamp_to_microseconds -import os - - -class Node: - def __init__(self, span_data): - self.span_data = span_data # type: SpanData - self.parent = None - self.children = [] - - -class MockExporter(Exporter): - def __init__(self): - self.root = None - self._all_nodes = [] - - def export(self, span_datas): - # type: (List[SpanData]) -> None - sp = span_datas[0] # type: SpanData - node = Node(sp) - if not node.span_data.parent_span_id: - self.root = node - self._all_nodes.append(node) - - def build_tree(self): - parent_dict = {} - for node in self._all_nodes: - parent_span_id = node.span_data.parent_span_id - if parent_span_id not in parent_dict: - parent_dict[parent_span_id] = [] - parent_dict[parent_span_id].append(node) - - for node in self._all_nodes: - if node.span_data.span_id in parent_dict: - node.children = sorted( - parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) - ) - - -class ContextHelper(object): - def __init__(self, environ={}): - self.orig_tracer = OpenCensusSpan.get_current_tracer() - self.orig_current_span = OpenCensusSpan.get_current_span() - self.os_env = mock.patch.dict(os.environ, environ) - - def __enter__(self): - self.orig_tracer = OpenCensusSpan.get_current_tracer() - self.orig_current_span = OpenCensusSpan.get_current_span() - self.os_env.start() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - OpenCensusSpan.set_current_tracer(self.orig_tracer) - OpenCensusSpan.set_current_span(self.orig_current_span) - self.os_env.stop() +from tracing_common import MockExporter, ContextHelper class TestOpencensusWrapper(unittest.TestCase): @@ -130,6 +75,7 @@ def test_start_finish(self): assert wrapped_class.span_instance.start_time is not None assert wrapped_class.span_instance.end_time is not None parent.finish() + tracer.finish() def test_to_header(self): with ContextHelper() as ctx: diff --git a/sdk/core/azure-core/tests/tracing_common.py b/sdk/core/azure-core/tests/tracing_common.py index 7967640180cf..76a09a7d7768 100644 --- a/sdk/core/azure-core/tests/tracing_common.py +++ b/sdk/core/azure-core/tests/tracing_common.py @@ -4,26 +4,21 @@ # ------------------------------------ """Code shared between the async and the sync test_decorator files.""" -import sys import os -from azure.core import HttpRequest -from azure.core.pipeline import Pipeline, PipelineResponse -from azure.core.pipeline.policies import HTTPPolicy -from azure.core.pipeline.transport import HttpTransport -from azure.core.tracing import common -from azure.core.tracing.context import tracing_context + from azure.core.settings import settings +from azure.core.tracing.context import tracing_context from azure.core.tracing.ext.opencensus_span import OpenCensusSpan -from opencensus.trace import tracer as tracer_module -from opencensus.trace.span_data import SpanData -from opencensus.trace.samplers import AlwaysOnSampler +from opencensus.trace import execution_context from opencensus.trace.base_exporter import Exporter +from opencensus.trace.span_data import SpanData try: from unittest import mock except ImportError: import mock + class ContextHelper(object): def __init__(self, environ={}, tracer_to_use=None, should_only_propagate=None): self.orig_tracer = OpenCensusSpan.get_current_tracer() @@ -42,6 +37,8 @@ def __enter__(self): if self.should_only_propagate is not None: settings.tracing_should_only_propagate.set_value(self.should_only_propagate) self.os_env.start() + execution_context.clear() + tracing_context.current_span.clear() return self def __exit__(self, exc_type, exc_val, exc_tb): @@ -62,7 +59,7 @@ def __init__(self, span_data): class MockExporter(Exporter): def __init__(self): - self.root = None + self.root = None # type: SpanData self._all_nodes = [] def export(self, span_datas): From ab01300ca76a9c2ad11fc438140098229a957cb7 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 10:13:02 -0700 Subject: [PATCH 089/105] add set_http_attributes --- .../pipeline/policies/distributed_tracing.py | 26 ++++---------- .../azure/core/tracing/abstract_span.py | 19 ++++++++-- .../azure/core/tracing/ext/opencensus_span.py | 35 +++++++++++++++++-- .../tests/test_tracing_implementations.py | 24 +++++++++++++ .../azure-core/tests/test_tracing_policy.py | 4 +-- 5 files changed, 83 insertions(+), 25 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index aee444f7725e..96d549a170ca 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -52,11 +52,6 @@ def __init__(self, name_of_spans="span - http call"): # type: (str, str, str) -> None self.name_of_child_span = name_of_spans self.parent_span_dict = {} - self._span_component = ("component", "http") - self._http_user_agent = "http.user_agent" - self._http_method = "http.method" - self._http_url = "http.url" - self._http_status_code = "http.status_code" self._request_id = "x-ms-request-id" def set_header(self, request, span): @@ -66,10 +61,6 @@ def set_header(self, request, span): """ headers = span.to_header() request.http_request.headers.update(headers) - span.add_attribute(self._http_method, request.http_request.method) - span.add_attribute(self._http_url, request.http_request.url) - span.add_attribute(self._http_user_agent, request.http_request.headers.get("User-Agent", "")) - span.add_attribute(*self._span_component) def on_request(self, request, **kwargs): # type: (PipelineRequest[HTTPRequestType], Any) -> None @@ -90,25 +81,22 @@ def on_request(self, request, **kwargs): self.parent_span_dict[child] = parent_span self.set_header(request, child) - def end_span(self, response=None): - # type: (Optional[PipelineResponse[HTTPRequestType, HTTPResponseType]]) -> None + def end_span(self, request, response=None): + # type: (HTTPRequestType, Optional[HTTPResponseType]) -> None """Ends the span that is tracing the network and updates its status.""" span = tracing_context.current_span.get() # type: AbstractSpan only_propagate = settings.tracing_should_only_propagate() if span and not only_propagate: - # span.add_attribute("http.url", request.http_request) - if response: - span.add_attribute(self._http_status_code, response.http_response.status_code) - span.add_attribute(self._request_id, response.http_response.headers.get(self._request_id, "")) - else: - span.add_attribute(self._http_status_code, 504) + span.set_http_attributes(request, response=response) + if response and self._request_id in response.headers: + span.add_attribute(self._request_id, response.headers[self._request_id]) span.finish() set_span_contexts(self.parent_span_dict[span]) def on_response(self, request, response, **kwargs): # type: (PipelineRequest[HTTPRequestType], PipelineResponse[HTTPRequestType, HTTPResponseType], Any) -> None - self.end_span(response=response) + self.end_span(request.http_request, response=response.http_response) def on_exception(self, _request, **kwargs): # pylint: disable=unused-argument # type: (PipelineRequest[HTTPRequestType], Any) -> bool - self.end_span() + self.end_span(_request.http_request) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index 1870c68a98c4..dd1f9af513a7 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -10,7 +10,10 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Any, Dict, Optional, Union + from typing import Any, Dict, Optional, Union, TypeVar + + HTTPResponseType = TypeVar("HTTPResponseType") + HTTPRequestType = TypeVar("HTTPRequestType") try: from typing_extensions import Protocol @@ -21,7 +24,7 @@ class AbstractSpan(Protocol): """Wraps a span from a distributed tracing implementation.""" - def __init__(self, span=None, name=None): # pylint: disable=super-init-not-called + def __init__(self, span=None, name=None): # pylint: disable=super-init-not-called # type: (Optional[Any], Optional[str]) -> None """ If a span is given wraps the span. Else a new span is created. @@ -66,6 +69,18 @@ def add_attribute(self, key, value): """ pass + def set_http_attributes(self, request, response=None): + # type: (HTTPRequestType, HTTPResponseType) -> None + """ + Add correct attributes for a http client span. + + :param request: The request make + :type request: HTTPRequestType + :param response: The response received by the server. Is None if no response received. + :type response: HTTPResponseType + """ + pass + @property def span_instance(self): # type: () -> Any diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index bab7dce34f32..6abbd0caff38 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -5,6 +5,7 @@ """Implements azure.core.tracing.AbstractSpan to wrap opencensus spans.""" from opencensus.trace import Span, execution_context +from opencensus.trace.span import SpanKind from opencensus.trace.link import Link from opencensus.trace.propagation import trace_context_http_header_format @@ -14,7 +15,10 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Dict, Optional, Union + from typing import Dict, Optional, Union, TypeVar + + HTTPResponseType = TypeVar("HTTPResponseType") + HTTPRequestType = TypeVar("HTTPRequestType") class OpenCensusSpan(object): @@ -33,8 +37,13 @@ def __init__(self, span=None, name="span"): """ if not span: tracer = self.get_current_tracer() - span = tracer.span(name=name) # type: Span + span = tracer.span(name=name) # type: Span self._span_instance = span + self._span_component = "component" + self._http_user_agent = "http.user_agent" + self._http_method = "http.method" + self._http_url = "http.url" + self._http_status_code = "http.status_code" @property def span_instance(self): @@ -89,6 +98,28 @@ def add_attribute(self, key, value): """ self.span_instance.add_attribute(key, value) + def set_http_attributes(self, request, response=None): + # type: (HTTPRequestType, Optional[HTTPResponseType]) -> None + """ + Add correct attributes for a http client span. + + :param request: The request make + :type request: HTTPRequestType + :param response: The response received by the server. Is None if no response received. + :type response: HTTPResponseType + """ + self._span_instance.span_id = SpanKind.CLIENT + self.span_instance.add_attribute(self._span_component, "http") + self.span_instance.add_attribute(self._http_method, request.method) + self.span_instance.add_attribute(self._http_url, request.url) + user_agent = request.headers.get("User-Agent") + if user_agent: + self.span_instance.add_attribute(self._http_user_agent, user_agent) + if response: + self._span_instance.add_attribute(self._http_status_code, response.status_code) + else: + self._span_instance.add_attribute(self._http_status_code, 504) + @classmethod def link(cls, headers): # type: (Dict[str, str]) -> None diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 5793685a9a1e..6dd958d578c0 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -13,6 +13,7 @@ from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from opencensus.trace import tracer as tracer_module +from opencensus.trace.span import SpanKind from opencensus.trace.samplers import AlwaysOnSampler from opencensus.trace.base_exporter import Exporter from opencensus.common.utils import timestamp_to_microseconds @@ -162,3 +163,26 @@ def test_add_attribute(self): wrapped_class.add_attribute("test", "test2") assert wrapped_class.span_instance.attributes["test"] == "test2" assert parent.attributes["test"] == "test2" + + def set_http_attributes(self): + with ContextHelper(): + trace = tracer_module.Tracer(sampler=AlwaysOnSampler()) + parent = trace.start_span() + wrapped_class = OpenCensusSpan(span=parent) + request = mock.Mock() + setattr(request, "method", "GET") + setattr(request, "url", "some url") + response = mock.Mock() + setattr(request, "headers", {}) + setattr(response, "status_code", 200) + wrapped_class.set_http_attributes(request) + assert wrapped_class.span_instance.span_kind == SpanKind.CLIENT + assert wrapped_class.span_instance.attributes.get("http.method") == request.method + assert wrapped_class.span_instance.attributes.get("component") == "http" + assert wrapped_class.span_instance.attributes.get("http.url") == request.url + assert wrapped_class.span_instance.attributes.get("http.status_code") == 504 + assert wrapped_class.span_instance.attributes.get("http.user_agent") is None + wrapped_class.set_http_attributes(request, response) + assert wrapped_class.span_instance.attributes.get("http.status_code") == response.status_code + request.headers["User-Agent"] = "some user agent" + assert wrapped_class.span_instance.attributes.get("http.user_agent") == request.headers.get("User-Agent") diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index 7d56d6293321..8349a6f01ab9 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -44,7 +44,7 @@ def test_distributed_tracing_policy_solo(): assert network_span.span_data.attributes.get("http.method") == "GET" assert network_span.span_data.attributes.get("component") == "http" assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" - assert network_span.span_data.attributes.get("http.user_agent") == "" + assert network_span.span_data.attributes.get("http.user_agent") is None assert network_span.span_data.attributes.get("x-ms-request-id") == "some request id" assert network_span.span_data.attributes.get("http.status_code") == 202 @@ -75,7 +75,7 @@ def test_distributed_tracing_policy_exception(): assert network_span.span_data.attributes.get("http.method") == "GET" assert network_span.span_data.attributes.get("component") == "http" assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" - assert network_span.span_data.attributes.get("http.user_agent") == "" + assert network_span.span_data.attributes.get("http.user_agent") is None assert network_span.span_data.attributes.get("x-ms-request-id") is None assert network_span.span_data.attributes.get("http.status_code") == 504 From 1b9e5d4298270574c40107036ef262658fe0722b Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 10:28:01 -0700 Subject: [PATCH 090/105] fix span network name --- .../core/pipeline/policies/distributed_tracing.py | 12 +++++++++--- sdk/core/azure-core/tests/test_tracing_policy.py | 14 +++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index 96d549a170ca..967d7091ce2c 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -32,6 +32,10 @@ from azure.core.pipeline.policies import SansIOHTTPPolicy from azure.core.tracing.common import get_parent_span from azure.core.settings import settings +try: + from urllib.parse import urlparse +except ImportError: + from urlparse import urlparse try: from typing import TYPE_CHECKING @@ -48,9 +52,8 @@ class DistributedTracingPolicy(SansIOHTTPPolicy): """The policy to create spans for Azure Calls""" - def __init__(self, name_of_spans="span - http call"): + def __init__(self): # type: (str, str, str) -> None - self.name_of_child_span = name_of_spans self.parent_span_dict = {} self._request_id = "x-ms-request-id" @@ -74,7 +77,10 @@ def on_request(self, request, **kwargs): self.set_header(request, parent_span) return - child = parent_span.span(name=self.name_of_child_span) + path = urlparse(request.http_request.url).path + if not path: + path = '/' + child = parent_span.span(name=path) child.start() set_span_contexts(child) diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index 8349a6f01ab9..f06361337fde 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -40,7 +40,7 @@ def test_distributed_tracing_policy_solo(): exporter.build_tree() parent = exporter.root network_span = parent.children[0] - assert network_span.span_data.name == "span - http call" + assert network_span.span_data.name == "/" assert network_span.span_data.attributes.get("http.method") == "GET" assert network_span.span_data.attributes.get("component") == "http" assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" @@ -56,7 +56,7 @@ def test_distributed_tracing_policy_exception(): trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) policy = DistributedTracingPolicy() - request = HttpRequest("GET", "http://127.0.0.1/") + request = HttpRequest("GET", "http://127.0.0.1/temp") pipeline_request = PipelineRequest(request, PipelineContext(None)) policy.on_request(pipeline_request) @@ -71,10 +71,10 @@ def test_distributed_tracing_policy_exception(): exporter.build_tree() parent = exporter.root network_span = parent.children[0] - assert network_span.span_data.name == "span - http call" + assert network_span.span_data.name == "/temp" assert network_span.span_data.attributes.get("http.method") == "GET" assert network_span.span_data.attributes.get("component") == "http" - assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" + assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/temp" assert network_span.span_data.attributes.get("http.user_agent") is None assert network_span.span_data.attributes.get("x-ms-request-id") is None assert network_span.span_data.attributes.get("http.status_code") == 504 @@ -87,7 +87,7 @@ def test_distributed_tracing_policy_with_usergent(): trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) policy = DistributedTracingPolicy() - request = HttpRequest("GET", "http://127.0.0.1/") + request = HttpRequest("GET", "http://127.0.0.1") pipeline_request = PipelineRequest(request, PipelineContext(None)) @@ -112,10 +112,10 @@ def test_distributed_tracing_policy_with_usergent(): exporter.build_tree() parent = exporter.root network_span = parent.children[0] - assert network_span.span_data.name == "span - http call" + assert network_span.span_data.name == "/" assert network_span.span_data.attributes.get("http.method") == "GET" assert network_span.span_data.attributes.get("component") == "http" - assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" + assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1" assert network_span.span_data.attributes.get("http.user_agent").endswith("mytools") assert network_span.span_data.attributes.get("x-ms-request-id") == "some request id" assert network_span.span_data.attributes.get("http.status_code") == 202 From 03f75e284c273afd579b28911be6f77d537128a1 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 10:43:58 -0700 Subject: [PATCH 091/105] fix http request types --- sdk/core/azure-core/azure/core/tracing/abstract_span.py | 9 ++++----- .../azure-core/azure/core/tracing/ext/opencensus_span.py | 9 ++++----- sdk/core/azure-core/tests/test_tracing_policy.py | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index dd1f9af513a7..30400708134c 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -12,8 +12,7 @@ if TYPE_CHECKING: from typing import Any, Dict, Optional, Union, TypeVar - HTTPResponseType = TypeVar("HTTPResponseType") - HTTPRequestType = TypeVar("HTTPRequestType") + from azure.core.pipeline.transport import HttpRequest, HttpResponse try: from typing_extensions import Protocol @@ -70,14 +69,14 @@ def add_attribute(self, key, value): pass def set_http_attributes(self, request, response=None): - # type: (HTTPRequestType, HTTPResponseType) -> None + # type: (HttpRequest, HttpResponse) -> None """ Add correct attributes for a http client span. :param request: The request make - :type request: HTTPRequestType + :type request: HttpRequest :param response: The response received by the server. Is None if no response received. - :type response: HTTPResponseType + :type response: HttpResponse """ pass diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 6abbd0caff38..539c334d111a 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -17,8 +17,7 @@ if TYPE_CHECKING: from typing import Dict, Optional, Union, TypeVar - HTTPResponseType = TypeVar("HTTPResponseType") - HTTPRequestType = TypeVar("HTTPRequestType") + from azure.core.pipeline.transport import HttpRequest, HttpResponse class OpenCensusSpan(object): @@ -99,14 +98,14 @@ def add_attribute(self, key, value): self.span_instance.add_attribute(key, value) def set_http_attributes(self, request, response=None): - # type: (HTTPRequestType, Optional[HTTPResponseType]) -> None + # type: (HttpRequest, Optional[HttpResponse]) -> None """ Add correct attributes for a http client span. :param request: The request make - :type request: HTTPRequestType + :type request: HttpRequest :param response: The response received by the server. Is None if no response received. - :type response: HTTPResponseType + :type response: HttpResponse """ self._span_instance.span_id = SpanKind.CLIENT self.span_instance.add_attribute(self._span_component, "http") diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index f06361337fde..48c813d07acb 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -56,7 +56,7 @@ def test_distributed_tracing_policy_exception(): trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) policy = DistributedTracingPolicy() - request = HttpRequest("GET", "http://127.0.0.1/temp") + request = HttpRequest("GET", "http://127.0.0.1/temp?query=query") pipeline_request = PipelineRequest(request, PipelineContext(None)) policy.on_request(pipeline_request) @@ -74,7 +74,7 @@ def test_distributed_tracing_policy_exception(): assert network_span.span_data.name == "/temp" assert network_span.span_data.attributes.get("http.method") == "GET" assert network_span.span_data.attributes.get("component") == "http" - assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/temp" + assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/temp?query=query" assert network_span.span_data.attributes.get("http.user_agent") is None assert network_span.span_data.attributes.get("x-ms-request-id") is None assert network_span.span_data.attributes.get("http.status_code") == 504 From c097ed99abd00c712939a7e81edd9d598461fcd4 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 10:47:06 -0700 Subject: [PATCH 092/105] fix more types --- .../pipeline/policies/distributed_tracing.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index 967d7091ce2c..9ae52613ba62 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -32,6 +32,7 @@ from azure.core.pipeline.policies import SansIOHTTPPolicy from azure.core.tracing.common import get_parent_span from azure.core.settings import settings + try: from urllib.parse import urlparse except ImportError: @@ -44,9 +45,7 @@ if TYPE_CHECKING: from typing import Any, TypeVar, Optional - - HTTPResponseType = TypeVar("HTTPResponseType") - HTTPRequestType = TypeVar("HTTPRequestType") + from azure.core.pipeline.transport import HttpRequest, HttpResponse class DistributedTracingPolicy(SansIOHTTPPolicy): @@ -58,7 +57,7 @@ def __init__(self): self._request_id = "x-ms-request-id" def set_header(self, request, span): - # type: (PipelineRequest[HTTPRequestType], Any) -> None + # type: (PipelineRequest[HttpRequest], Any) -> None """ Sets the header information on the span. """ @@ -66,7 +65,7 @@ def set_header(self, request, span): request.http_request.headers.update(headers) def on_request(self, request, **kwargs): - # type: (PipelineRequest[HTTPRequestType], Any) -> None + # type: (PipelineRequest[HttpRequest], Any) -> None parent_span = get_parent_span(None) # type: AbstractSpan if parent_span is None: @@ -79,7 +78,7 @@ def on_request(self, request, **kwargs): path = urlparse(request.http_request.url).path if not path: - path = '/' + path = "/" child = parent_span.span(name=path) child.start() @@ -88,7 +87,7 @@ def on_request(self, request, **kwargs): self.set_header(request, child) def end_span(self, request, response=None): - # type: (HTTPRequestType, Optional[HTTPResponseType]) -> None + # type: (HttpRequest, Optional[HttpResponse]) -> None """Ends the span that is tracing the network and updates its status.""" span = tracing_context.current_span.get() # type: AbstractSpan only_propagate = settings.tracing_should_only_propagate() @@ -100,9 +99,9 @@ def end_span(self, request, response=None): set_span_contexts(self.parent_span_dict[span]) def on_response(self, request, response, **kwargs): - # type: (PipelineRequest[HTTPRequestType], PipelineResponse[HTTPRequestType, HTTPResponseType], Any) -> None + # type: (PipelineRequest[HttpRequest], PipelineResponse[HttpRequest, HttpResponse], Any) -> None self.end_span(request.http_request, response=response.http_response) def on_exception(self, _request, **kwargs): # pylint: disable=unused-argument - # type: (PipelineRequest[HTTPRequestType], Any) -> bool + # type: (PipelineRequest[HttpRequest], Any) -> bool self.end_span(_request.http_request) From b4f7094f15ec2be77ac9f64afef535c81281fd7e Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:14:40 -0700 Subject: [PATCH 093/105] bryan fixes --- .../pipeline/policies/distributed_tracing.py | 2 +- .../azure/core/tracing/ext/opencensus_span.py | 2 +- .../tests/test_tracing_implementations.py | 58 +------------------ .../azure-core/tests/test_tracing_policy.py | 2 +- sdk/core/azure-core/tests/tracing_common.py | 5 +- 5 files changed, 8 insertions(+), 61 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index 9ae52613ba62..ebad0acc1b97 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -89,7 +89,7 @@ def on_request(self, request, **kwargs): def end_span(self, request, response=None): # type: (HttpRequest, Optional[HttpResponse]) -> None """Ends the span that is tracing the network and updates its status.""" - span = tracing_context.current_span.get() # type: AbstractSpan + span = get_parent_span(None) # type: AbstractSpan only_propagate = settings.tracing_should_only_propagate() if span and not only_propagate: span.set_http_attributes(request, response=response) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 539c334d111a..21b48207448b 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -107,7 +107,7 @@ def set_http_attributes(self, request, response=None): :param response: The response received by the server. Is None if no response received. :type response: HttpResponse """ - self._span_instance.span_id = SpanKind.CLIENT + self._span_instance.span_kind = SpanKind.CLIENT self.span_instance.add_attribute(self._span_component, "http") self.span_instance.add_attribute(self._http_method, request.method) self.span_instance.add_attribute(self._http_url, request.url) diff --git a/sdk/core/azure-core/tests/test_tracing_implementations.py b/sdk/core/azure-core/tests/test_tracing_implementations.py index 6dd958d578c0..23df8cff33a6 100644 --- a/sdk/core/azure-core/tests/test_tracing_implementations.py +++ b/sdk/core/azure-core/tests/test_tracing_implementations.py @@ -17,62 +17,10 @@ from opencensus.trace.samplers import AlwaysOnSampler from opencensus.trace.base_exporter import Exporter from opencensus.common.utils import timestamp_to_microseconds +from tracing_common import MockExporter, ContextHelper import os -class Node: - def __init__(self, span_data): - self.span_data = span_data # type: SpanData - self.parent = None - self.children = [] - - -class MockExporter(Exporter): - def __init__(self): - self.root = None - self._all_nodes = [] - - def export(self, span_datas): - # type: (List[SpanData]) -> None - sp = span_datas[0] # type: SpanData - node = Node(sp) - if not node.span_data.parent_span_id: - self.root = node - self._all_nodes.append(node) - - def build_tree(self): - parent_dict = {} - for node in self._all_nodes: - parent_span_id = node.span_data.parent_span_id - if parent_span_id not in parent_dict: - parent_dict[parent_span_id] = [] - parent_dict[parent_span_id].append(node) - - for node in self._all_nodes: - if node.span_data.span_id in parent_dict: - node.children = sorted( - parent_dict[node.span_data.span_id], key=lambda x: timestamp_to_microseconds(x.span_data.start_time) - ) - - -class ContextHelper(object): - def __init__(self, environ={}): - self.orig_tracer = OpenCensusSpan.get_current_tracer() - self.orig_current_span = OpenCensusSpan.get_current_span() - self.os_env = mock.patch.dict(os.environ, environ) - - def __enter__(self): - self.orig_tracer = OpenCensusSpan.get_current_tracer() - self.orig_current_span = OpenCensusSpan.get_current_span() - self.os_env.start() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - OpenCensusSpan.set_current_tracer(self.orig_tracer) - OpenCensusSpan.set_current_span(self.orig_current_span) - self.os_env.stop() - - class TestOpencensusWrapper(unittest.TestCase): def test_span_passed_in(self): with ContextHelper(): @@ -164,7 +112,7 @@ def test_add_attribute(self): assert wrapped_class.span_instance.attributes["test"] == "test2" assert parent.attributes["test"] == "test2" - def set_http_attributes(self): + def test_set_http_attributes(self): with ContextHelper(): trace = tracer_module.Tracer(sampler=AlwaysOnSampler()) parent = trace.start_span() @@ -182,7 +130,7 @@ def set_http_attributes(self): assert wrapped_class.span_instance.attributes.get("http.url") == request.url assert wrapped_class.span_instance.attributes.get("http.status_code") == 504 assert wrapped_class.span_instance.attributes.get("http.user_agent") is None + request.headers["User-Agent"] = "some user agent" wrapped_class.set_http_attributes(request, response) assert wrapped_class.span_instance.attributes.get("http.status_code") == response.status_code - request.headers["User-Agent"] = "some user agent" assert wrapped_class.span_instance.attributes.get("http.user_agent") == request.headers.get("User-Agent") diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index 48c813d07acb..c6f901009348 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -80,7 +80,7 @@ def test_distributed_tracing_policy_exception(): assert network_span.span_data.attributes.get("http.status_code") == 504 -def test_distributed_tracing_policy_with_usergent(): +def test_distributed_tracing_policy_with_user_agent(): """Test policy working with user agent.""" with ContextHelper(environ={"AZURE_HTTP_USER_AGENT": "mytools"}): exporter = MockExporter() diff --git a/sdk/core/azure-core/tests/tracing_common.py b/sdk/core/azure-core/tests/tracing_common.py index 50fa984e675a..caa8605762df 100644 --- a/sdk/core/azure-core/tests/tracing_common.py +++ b/sdk/core/azure-core/tests/tracing_common.py @@ -18,6 +18,7 @@ from opencensus.trace.span_data import SpanData from opencensus.trace.samplers import AlwaysOnSampler from opencensus.trace.base_exporter import Exporter +from collections import defaultdict try: from unittest import mock @@ -65,7 +66,7 @@ class MockExporter(Exporter): def __init__(self): self.root = None self._all_nodes = [] - self.parent_dict = {} + self.parent_dict = defaultdict(list) def export(self, span_datas): # type: (List[SpanData]) -> None @@ -74,8 +75,6 @@ def export(self, span_datas): if not node.span_data.parent_span_id: self.root = node parent_span_id = node.span_data.parent_span_id - if parent_span_id not in self.parent_dict: - self.parent_dict[parent_span_id] = [] self.parent_dict[parent_span_id].append(node) self._all_nodes.append(node) From d0a5137d69c00c6664551403b59bdbc85547eb39 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:22:19 -0700 Subject: [PATCH 094/105] more efficient tests --- .../azure-core/tests/test_tracing_policy.py | 35 +++++-------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index c6f901009348..c4900fb82ed8 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -20,7 +20,7 @@ def test_distributed_tracing_policy_solo(): trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) policy = DistributedTracingPolicy() - request = HttpRequest("GET", "http://127.0.0.1/") + request = HttpRequest("GET", "http://127.0.0.1/temp?query=query") pipeline_request = PipelineRequest(request, PipelineContext(None)) policy.on_request(pipeline_request) @@ -36,47 +36,28 @@ def test_distributed_tracing_policy_solo(): policy.on_response(pipeline_request, PipelineResponse(request, response, PipelineContext(None))) + policy.on_request(pipeline_request) + policy.on_exception(pipeline_request) + trace.finish() exporter.build_tree() parent = exporter.root network_span = parent.children[0] - assert network_span.span_data.name == "/" + assert network_span.span_data.name == "/temp" assert network_span.span_data.attributes.get("http.method") == "GET" assert network_span.span_data.attributes.get("component") == "http" - assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/" + assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/temp?query=query" assert network_span.span_data.attributes.get("http.user_agent") is None assert network_span.span_data.attributes.get("x-ms-request-id") == "some request id" assert network_span.span_data.attributes.get("http.status_code") == 202 - -def test_distributed_tracing_policy_exception(): - """Test Policy on when an exception happens.""" - with ContextHelper(): - exporter = MockExporter() - trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) - policy = DistributedTracingPolicy() - - request = HttpRequest("GET", "http://127.0.0.1/temp?query=query") - - pipeline_request = PipelineRequest(request, PipelineContext(None)) - policy.on_request(pipeline_request) - - ctx = trace.span_context - header = trace.propagator.to_headers(ctx) - assert request.headers.get("traceparent") == header.get("traceparent") - - policy.on_exception(pipeline_request) - - trace.finish() - exporter.build_tree() - parent = exporter.root - network_span = parent.children[0] + network_span = parent.children[1] assert network_span.span_data.name == "/temp" assert network_span.span_data.attributes.get("http.method") == "GET" assert network_span.span_data.attributes.get("component") == "http" assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1/temp?query=query" assert network_span.span_data.attributes.get("http.user_agent") is None - assert network_span.span_data.attributes.get("x-ms-request-id") is None + assert network_span.span_data.attributes.get("x-ms-request-id") == None assert network_span.span_data.attributes.get("http.status_code") == 504 From 583cf38aabcff9553c1acab2a9b9710519a04d07 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:23:22 -0700 Subject: [PATCH 095/105] non flakey tests --- sdk/core/azure-core/tests/test_tracing_policy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index c4900fb82ed8..febce57e9531 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -11,7 +11,7 @@ from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler from tracing_common import ContextHelper, MockExporter - +import time def test_distributed_tracing_policy_solo(): """Test policy with no other policy and happy path""" @@ -35,7 +35,7 @@ def test_distributed_tracing_policy_solo(): assert request.headers.get("traceparent") == header.get("traceparent") policy.on_response(pipeline_request, PipelineResponse(request, response, PipelineContext(None))) - + time.sleep(0.001) policy.on_request(pipeline_request) policy.on_exception(pipeline_request) From c28fe3479761d3ae05ab1435710249d559a70689 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:31:58 -0700 Subject: [PATCH 096/105] make tracing only use my context --- .../pipeline/policies/distributed_tracing.py | 5 +- .../azure-core/tests/test_tracing_policy.py | 68 ++++++++++--------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index ebad0acc1b97..582701199ef2 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -30,7 +30,6 @@ from azure.core.tracing.abstract_span import AbstractSpan from azure.core.tracing.common import set_span_contexts from azure.core.pipeline.policies import SansIOHTTPPolicy -from azure.core.tracing.common import get_parent_span from azure.core.settings import settings try: @@ -66,7 +65,7 @@ def set_header(self, request, span): def on_request(self, request, **kwargs): # type: (PipelineRequest[HttpRequest], Any) -> None - parent_span = get_parent_span(None) # type: AbstractSpan + parent_span = tracing_context.current_span.get() # type: AbstractSpan if parent_span is None: return @@ -89,7 +88,7 @@ def on_request(self, request, **kwargs): def end_span(self, request, response=None): # type: (HttpRequest, Optional[HttpResponse]) -> None """Ends the span that is tracing the network and updates its status.""" - span = get_parent_span(None) # type: AbstractSpan + span = tracing_context.current_span.get() # type: AbstractSpan only_propagate = settings.tracing_should_only_propagate() if span and not only_propagate: span.set_http_attributes(request, response=response) diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index febce57e9531..97a57186bafd 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -4,12 +4,14 @@ # ------------------------------------ """Tests for the distributed tracing policy.""" +from azure.core.tracing.context import tracing_context from azure.core.pipeline import PipelineResponse, PipelineRequest, PipelineContext from azure.core.pipeline.policies.distributed_tracing import DistributedTracingPolicy from azure.core.pipeline.policies.universal import UserAgentPolicy from azure.core.pipeline.transport import HttpRequest, HttpResponse from opencensus.trace import tracer as tracer_module from opencensus.trace.samplers import AlwaysOnSampler +from azure.core.tracing.ext.opencensus_span import OpenCensusSpan from tracing_common import ContextHelper, MockExporter import time @@ -18,26 +20,28 @@ def test_distributed_tracing_policy_solo(): with ContextHelper(): exporter = MockExporter() trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) - policy = DistributedTracingPolicy() + with trace.span("parent"): + tracing_context.current_span.set(OpenCensusSpan(trace.current_span())) + policy = DistributedTracingPolicy() - request = HttpRequest("GET", "http://127.0.0.1/temp?query=query") + request = HttpRequest("GET", "http://127.0.0.1/temp?query=query") - pipeline_request = PipelineRequest(request, PipelineContext(None)) - policy.on_request(pipeline_request) + pipeline_request = PipelineRequest(request, PipelineContext(None)) + policy.on_request(pipeline_request) - response = HttpResponse(request, None) - response.headers = request.headers - response.status_code = 202 - response.headers["x-ms-request-id"] = "some request id" + response = HttpResponse(request, None) + response.headers = request.headers + response.status_code = 202 + response.headers["x-ms-request-id"] = "some request id" - ctx = trace.span_context - header = trace.propagator.to_headers(ctx) - assert request.headers.get("traceparent") == header.get("traceparent") + ctx = trace.span_context + header = trace.propagator.to_headers(ctx) + assert request.headers.get("traceparent") == header.get("traceparent") - policy.on_response(pipeline_request, PipelineResponse(request, response, PipelineContext(None))) - time.sleep(0.001) - policy.on_request(pipeline_request) - policy.on_exception(pipeline_request) + policy.on_response(pipeline_request, PipelineResponse(request, response, PipelineContext(None))) + time.sleep(0.001) + policy.on_request(pipeline_request) + policy.on_exception(pipeline_request) trace.finish() exporter.build_tree() @@ -66,28 +70,30 @@ def test_distributed_tracing_policy_with_user_agent(): with ContextHelper(environ={"AZURE_HTTP_USER_AGENT": "mytools"}): exporter = MockExporter() trace = tracer_module.Tracer(sampler=AlwaysOnSampler(), exporter=exporter) - policy = DistributedTracingPolicy() + with trace.span("parent"): + tracing_context.current_span.set(OpenCensusSpan(trace.current_span())) + policy = DistributedTracingPolicy() - request = HttpRequest("GET", "http://127.0.0.1") + request = HttpRequest("GET", "http://127.0.0.1") - pipeline_request = PipelineRequest(request, PipelineContext(None)) + pipeline_request = PipelineRequest(request, PipelineContext(None)) - user_agent = UserAgentPolicy() - user_agent.on_request(pipeline_request) - policy.on_request(pipeline_request) + user_agent = UserAgentPolicy() + user_agent.on_request(pipeline_request) + policy.on_request(pipeline_request) - response = HttpResponse(request, None) - response.headers = request.headers - response.status_code = 202 - response.headers["x-ms-request-id"] = "some request id" - pipeline_response = PipelineResponse(request, response, PipelineContext(None)) + response = HttpResponse(request, None) + response.headers = request.headers + response.status_code = 202 + response.headers["x-ms-request-id"] = "some request id" + pipeline_response = PipelineResponse(request, response, PipelineContext(None)) - ctx = trace.span_context - header = trace.propagator.to_headers(ctx) - assert request.headers.get("traceparent") == header.get("traceparent") + ctx = trace.span_context + header = trace.propagator.to_headers(ctx) + assert request.headers.get("traceparent") == header.get("traceparent") - policy.on_response(pipeline_request, pipeline_response) - user_agent.on_response(pipeline_request, pipeline_response) + policy.on_response(pipeline_request, pipeline_response) + user_agent.on_response(pipeline_request, pipeline_response) trace.finish() exporter.build_tree() From 290f27249ce87a00c34a034e64fad75cd8bce1c0 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:34:26 -0700 Subject: [PATCH 097/105] test user agent on exception --- sdk/core/azure-core/tests/test_tracing_policy.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index 97a57186bafd..a6c832ec5ef8 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -93,6 +93,11 @@ def test_distributed_tracing_policy_with_user_agent(): assert request.headers.get("traceparent") == header.get("traceparent") policy.on_response(pipeline_request, pipeline_response) + + time.sleep(0.001) + policy.on_request(pipeline_request) + policy.on_exception(pipeline_request) + user_agent.on_response(pipeline_request, pipeline_response) trace.finish() @@ -106,3 +111,12 @@ def test_distributed_tracing_policy_with_user_agent(): assert network_span.span_data.attributes.get("http.user_agent").endswith("mytools") assert network_span.span_data.attributes.get("x-ms-request-id") == "some request id" assert network_span.span_data.attributes.get("http.status_code") == 202 + + network_span = parent.children[1] + assert network_span.span_data.name == "/" + assert network_span.span_data.attributes.get("http.method") == "GET" + assert network_span.span_data.attributes.get("component") == "http" + assert network_span.span_data.attributes.get("http.url") == "http://127.0.0.1" + assert network_span.span_data.attributes.get("http.user_agent").endswith("mytools") + assert network_span.span_data.attributes.get("x-ms-request-id") is None + assert network_span.span_data.attributes.get("http.status_code") == 504 From 30904167e23c67632ed5ed0f02ff8b9d8a80d66f Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:34:42 -0700 Subject: [PATCH 098/105] pylint formatting --- sdk/core/azure-core/tests/test_tracing_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/tests/test_tracing_policy.py b/sdk/core/azure-core/tests/test_tracing_policy.py index a6c832ec5ef8..420b6c998b60 100644 --- a/sdk/core/azure-core/tests/test_tracing_policy.py +++ b/sdk/core/azure-core/tests/test_tracing_policy.py @@ -93,7 +93,7 @@ def test_distributed_tracing_policy_with_user_agent(): assert request.headers.get("traceparent") == header.get("traceparent") policy.on_response(pipeline_request, pipeline_response) - + time.sleep(0.001) policy.on_request(pipeline_request) policy.on_exception(pipeline_request) From ca59363bf4bd4cdb32c6cf81f8b2d89a06816cb0 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:36:04 -0700 Subject: [PATCH 099/105] delete unused import --- .../azure/core/pipeline/policies/distributed_tracing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py index 582701199ef2..eb86a3207ebb 100644 --- a/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py +++ b/sdk/core/azure-core/azure/core/pipeline/policies/distributed_tracing.py @@ -43,7 +43,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Any, TypeVar, Optional + from typing import Any, Optional from azure.core.pipeline.transport import HttpRequest, HttpResponse From ff8fdebb2d2449da2145477106171f758088c7ef Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:40:08 -0700 Subject: [PATCH 100/105] fix spelling --- sdk/core/azure-core/azure/core/tracing/abstract_span.py | 2 +- sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index 30400708134c..d74def0dea49 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -73,7 +73,7 @@ def set_http_attributes(self, request, response=None): """ Add correct attributes for a http client span. - :param request: The request make + :param request: The request made :type request: HttpRequest :param response: The response received by the server. Is None if no response received. :type response: HttpResponse diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index 21b48207448b..d63dc310d390 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -102,7 +102,7 @@ def set_http_attributes(self, request, response=None): """ Add correct attributes for a http client span. - :param request: The request make + :param request: The request made :type request: HttpRequest :param response: The response received by the server. Is None if no response received. :type response: HttpResponse From 11ecf0ea30931e6cbc0661c73cbc69ee393827ba Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Wed, 17 Jul 2019 14:40:32 -0700 Subject: [PATCH 101/105] pylint --- sdk/core/azure-core/azure/core/tracing/abstract_span.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/azure-core/azure/core/tracing/abstract_span.py b/sdk/core/azure-core/azure/core/tracing/abstract_span.py index d74def0dea49..9b7ca6753c0a 100644 --- a/sdk/core/azure-core/azure/core/tracing/abstract_span.py +++ b/sdk/core/azure-core/azure/core/tracing/abstract_span.py @@ -10,7 +10,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Any, Dict, Optional, Union, TypeVar + from typing import Any, Dict, Optional, Union from azure.core.pipeline.transport import HttpRequest, HttpResponse From ed8189ed5c1552953585cca6d0175925078e56f6 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 19 Jul 2019 11:49:57 -0700 Subject: [PATCH 102/105] add policy to the keyvault --- sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py | 1 - .../azure/keyvault/keys/_shared/client_base.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py index 976cfe965dd6..ee0e81d07f73 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py @@ -3,7 +3,6 @@ # Licensed under the MIT License. # ------------------------------------ from datetime import datetime -from typing import Any, Dict, Generator, Mapping, Optional, List try: from typing import TYPE_CHECKING diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_shared/client_base.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_shared/client_base.py index 147dc0506b76..27937e299b24 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_shared/client_base.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_shared/client_base.py @@ -6,6 +6,7 @@ from azure.core import Configuration from azure.core.pipeline import Pipeline from azure.core.pipeline.transport import RequestsTransport +from azure.core.pipeline.policies.distributed_tracing import DistributedTracingPolicy from ._generated import KeyVaultClient if TYPE_CHECKING: @@ -73,6 +74,7 @@ def _build_pipeline(self, config, transport): config.retry_policy, config.authentication_policy, config.logging_policy, + DistributedTracingPolicy(), ] if transport is None: From a1ed57a2d701b1c79fca117643edcf02c39c84c7 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 19 Jul 2019 14:13:35 -0700 Subject: [PATCH 103/105] add forgotten to add the tracing policy to the async client --- .../azure/keyvault/keys/_shared/async_client_base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_shared/async_client_base.py b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_shared/async_client_base.py index da245f70bd07..b60a51f5244c 100644 --- a/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_shared/async_client_base.py +++ b/sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_shared/async_client_base.py @@ -6,6 +6,7 @@ from azure.core.async_paging import AsyncPagedMixin from azure.core.configuration import Configuration from azure.core.pipeline import AsyncPipeline +from azure.core.pipeline.policies.distributed_tracing import DistributedTracingPolicy from azure.core.pipeline.transport import AsyncioRequestsTransport, HttpTransport from msrest.serialization import Model @@ -101,6 +102,7 @@ def _build_pipeline(config: Configuration, transport: HttpTransport, **kwargs: A config.retry_policy, config.authentication_policy, config.logging_policy, + DistributedTracingPolicy() ] if transport is None: From 3b360883c2af512393d6f2c3528e7d175f915e59 Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Fri, 19 Jul 2019 15:26:48 -0700 Subject: [PATCH 104/105] cant pass header when trace not sampled --- .../azure-core/azure/core/tracing/ext/opencensus_span.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py index ceb8e5a017e5..7aeb79c77e36 100644 --- a/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py +++ b/sdk/core/azure-core/azure/core/tracing/ext/opencensus_span.py @@ -15,7 +15,7 @@ TYPE_CHECKING = False if TYPE_CHECKING: - from typing import Dict, Optional, Union, TypeVar + from typing import Dict, Optional, Union from azure.core.pipeline.transport import HttpRequest, HttpResponse @@ -83,7 +83,10 @@ def to_header(self): temp_headers = {} if tracer_from_context is not None: ctx = tracer_from_context.span_context - temp_headers = tracer_from_context.propagator.to_headers(ctx) + try: + temp_headers = tracer_from_context.propagator.to_headers(ctx) + except AttributeError: + pass return temp_headers def add_attribute(self, key, value): From 639a7b392e402347c84591d4c7ca85a1c4c82b2c Mon Sep 17 00:00:00 2001 From: Suyog Soti Date: Mon, 22 Jul 2019 19:40:59 -0700 Subject: [PATCH 105/105] use assert not --- sdk/core/azure-core/tests/test_tracing_decorator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/core/azure-core/tests/test_tracing_decorator.py b/sdk/core/azure-core/tests/test_tracing_decorator.py index abd9e05142e1..385bda8e4f92 100644 --- a/sdk/core/azure-core/tests/test_tracing_decorator.py +++ b/sdk/core/azure-core/tests/test_tracing_decorator.py @@ -104,11 +104,11 @@ def test_get_parent_span(self): def test_should_use_trace(self): with ContextHelper(environ={"AZURE_TRACING_ONLY_PROPAGATE": "yes"}): parent_span = OpenCensusSpan() - assert common.should_use_trace(parent_span) is False - assert common.should_use_trace(None) is False + assert not common.should_use_trace(parent_span) + assert not common.should_use_trace(None) parent_span = OpenCensusSpan() assert common.should_use_trace(parent_span) - assert common.should_use_trace(None) is False + assert not common.should_use_trace(None) class TestDecorator(object):