Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
657c34e
add opencensus impl
SuyogSoti Jul 9, 2019
8573e23
dont need to check for noop tracer
SuyogSoti Jul 9, 2019
989c4dc
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-pytho…
SuyogSoti Jul 9, 2019
3f64325
get rid of span and trace id
SuyogSoti Jul 9, 2019
0b04d80
fix span instance
SuyogSoti Jul 9, 2019
9f4e507
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-pytho…
SuyogSoti Jul 9, 2019
7b728d8
added documentation
SuyogSoti Jul 9, 2019
bfd355c
added documentation and refactor names of a few variables
SuyogSoti Jul 9, 2019
cd06c97
write test for opencensus wrapper
SuyogSoti Jul 10, 2019
a9f7a6f
put opencensus in the dev requirements
SuyogSoti Jul 10, 2019
b8f63df
initial common
SuyogSoti Jul 10, 2019
408c5ad
only import the wrapper when necessary
SuyogSoti Jul 10, 2019
9baf366
add check for the exporter
SuyogSoti Jul 10, 2019
f4c52fd
rework logic and fix some settings
SuyogSoti Jul 10, 2019
d439df9
added initial decorator
SuyogSoti Jul 10, 2019
f256d62
some mroe documentation
SuyogSoti Jul 10, 2019
9067279
added decorators
SuyogSoti Jul 10, 2019
eee5e09
small change
SuyogSoti Jul 10, 2019
6050bd7
some minor fixes
SuyogSoti Jul 10, 2019
c9faa4b
test patch happening
SuyogSoti Jul 10, 2019
6d35557
fix space
SuyogSoti Jul 10, 2019
0f6c1f7
share a function
SuyogSoti Jul 10, 2019
2bfffe5
clearer logic for setting span context
SuyogSoti Jul 10, 2019
c1f6d69
better logic
SuyogSoti Jul 10, 2019
18890ea
better logic
SuyogSoti Jul 10, 2019
6a4a545
fix environ variable
SuyogSoti Jul 11, 2019
e7b5707
test the way opencensus does it
SuyogSoti Jul 11, 2019
aaf2152
middle of tests
SuyogSoti Jul 11, 2019
7ccf041
only load if opencensus has already been imported
SuyogSoti Jul 11, 2019
6a01918
fix spelling mistake
SuyogSoti Jul 11, 2019
36572c5
temp
SuyogSoti Jul 11, 2019
d9a2d95
Merge branch 'opencensus_impl' into temp
SuyogSoti Jul 11, 2019
280173f
finish writing tests for common
SuyogSoti Jul 11, 2019
b3f44fb
charles fixes
SuyogSoti Jul 11, 2019
9cb1bd9
Merge branch 'opencensus_impl' into temp
SuyogSoti Jul 11, 2019
77e0ccf
fix tests
SuyogSoti Jul 11, 2019
1e9c7dc
fix test settings
SuyogSoti Jul 11, 2019
d72248e
to header should not take a dict
SuyogSoti Jul 11, 2019
135d1c0
from header should be class method
SuyogSoti Jul 11, 2019
56e8269
Merge branch 'opencensus_impl' into temp
SuyogSoti Jul 11, 2019
73203c2
initial tests
SuyogSoti Jul 11, 2019
e304d40
dont create trace and get rid of end_tracer
SuyogSoti Jul 11, 2019
ff5627d
dont need to save the trace
SuyogSoti Jul 11, 2019
1e4b03a
more little fixes
SuyogSoti Jul 11, 2019
d55bca1
Merge branch 'opencensus_impl' into temp
SuyogSoti Jul 11, 2019
72e0dd4
some intermediatary changes
SuyogSoti Jul 11, 2019
aa5b671
fix type annotations
SuyogSoti Jul 11, 2019
0ce1a22
rst fix types
SuyogSoti Jul 11, 2019
b9c7d72
add :class:annotations
SuyogSoti Jul 11, 2019
57b2a57
fix line wrapping
SuyogSoti Jul 11, 2019
9b6260e
Merge branch 'opencensus_impl' into temp
SuyogSoti Jul 11, 2019
8b1ed8f
added tests for decorator
SuyogSoti Jul 12, 2019
c041818
rename opencensus wrapper
SuyogSoti Jul 12, 2019
8da57ec
Merge branch 'opencensus_impl' into overall
SuyogSoti Jul 12, 2019
56d2e7d
intermediate changes
SuyogSoti Jul 12, 2019
de6f552
use spans the right way
SuyogSoti Jul 12, 2019
691eb3d
some formatting
SuyogSoti Jul 12, 2019
6a1f733
Merge branch 'opencensus_impl' into overall
SuyogSoti Jul 12, 2019
20d5afe
some grammar
SuyogSoti Jul 12, 2019
c55c901
Merge branch 'opencensus_impl' into overall
SuyogSoti Jul 12, 2019
3e80b1e
restructure settings and make tests pass
SuyogSoti Jul 12, 2019
3f9f4ae
rename get_parent
SuyogSoti Jul 12, 2019
412bf7a
fix typings
SuyogSoti Jul 12, 2019
3c6ab1c
use protocol and from_headers becomes links
SuyogSoti Jul 12, 2019
de8b5f4
Merge branch 'opencensus_impl' into overall
SuyogSoti Jul 12, 2019
de41353
ramifications of opencensus wrapper being a protocol
SuyogSoti Jul 12, 2019
db2f865
add tests for link
SuyogSoti Jul 12, 2019
7c637e6
Merge branch 'opencensus_impl' into overall
SuyogSoti Jul 12, 2019
27c9876
added async tests
SuyogSoti Jul 12, 2019
ac9ff09
delete the unit test thing
SuyogSoti Jul 12, 2019
f0ba4ee
fix merge conflict
SuyogSoti Jul 12, 2019
ff07c55
added add_attribute
SuyogSoti Jul 13, 2019
30fd165
added add_attribute
SuyogSoti Jul 13, 2019
644fe2a
added tests for add attributes
SuyogSoti Jul 13, 2019
fc6a284
Merge branch 'opencensus_impl' into add_decorator
SuyogSoti Jul 13, 2019
8f137c1
remove unused import
SuyogSoti Jul 13, 2019
8e8aa21
Merge branch 'master' into add_decorator
SuyogSoti Jul 13, 2019
a373e40
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-pytho…
SuyogSoti Jul 15, 2019
0c37b38
added docstrings
SuyogSoti Jul 15, 2019
b925782
minor docstring formatting
SuyogSoti Jul 15, 2019
4dd6ba9
fix pylint errors
SuyogSoti Jul 15, 2019
fae494a
don't rely on opencensus children to check
SuyogSoti Jul 15, 2019
e484bcf
use exporter to not rely on parent.children
SuyogSoti Jul 15, 2019
76c75bc
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-pytho…
SuyogSoti Jul 15, 2019
9d0159c
test should only propagate
SuyogSoti Jul 16, 2019
aa14218
made test tracing helper
SuyogSoti Jul 16, 2019
bc35a5f
decrease flakiness of test
SuyogSoti Jul 16, 2019
85e5e48
simplify get parent
SuyogSoti Jul 16, 2019
91d1d25
calling a decorator decorator is redundant
SuyogSoti Jul 16, 2019
603d4f6
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-pytho…
SuyogSoti Jul 16, 2019
056bc2e
fix settings
SuyogSoti Jul 16, 2019
720c44e
more elegant code
SuyogSoti Jul 16, 2019
f503b6e
add await for async
SuyogSoti Jul 16, 2019
6743bd1
should only have to wait a 1/1000 of a second
SuyogSoti Jul 16, 2019
c070b77
fix tests spans too short
SuyogSoti Jul 17, 2019
df7f09e
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-pytho…
SuyogSoti Jul 17, 2019
d106a16
accidentally deleted setup.cfg
SuyogSoti Jul 17, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 82 additions & 29 deletions sdk/core/azure-core/azure/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,19 @@
from collections import namedtuple
import logging
import os
from typing import Any, Union
import six
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


__all__ = ("settings",)
Expand Down Expand Up @@ -61,9 +73,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))

Expand Down Expand Up @@ -102,14 +114,63 @@ 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


def get_opencensus_span():
# type: () -> OpenCensusSpan
"""Returns the OpenCensusSpan if opencensus is installed else returns None"""
try:
from azure.core.tracing.ext.opencensus_span import OpenCensusSpan

return OpenCensusSpan
except ImportError:
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

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 value is None:
return get_opencensus_span_if_opencensus_is_imported()

wrapper_class = value
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)
)
)

return wrapper_class


class PrioritizedSetting(object):
"""Return a value for a global setting according to configuration precedence.

Expand Down Expand Up @@ -138,9 +199,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
Expand Down Expand Up @@ -311,11 +370,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
Expand All @@ -335,11 +390,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)

Expand All @@ -348,17 +399,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_should_only_propagate = PrioritizedSetting(
"tracing_should_only_propagate", env_var="AZURE_TRACING_ONLY_PROPAGATE", convert=convert_bool, default=False
)


Expand Down
6 changes: 3 additions & 3 deletions sdk/core/azure-core/azure/core/tracing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Licensed under the MIT License.
# ------------------------------------
from azure.core.tracing.abstract_span import AbstractSpan
from azure.core.tracing.context import tracing_context

__all__ = ["tracing_context", "AbstractSpan"]

__all__ = [
"AbstractSpan",
]
4 changes: 3 additions & 1 deletion sdk/core/azure-core/azure/core/tracing/abstract_span.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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.
Expand Down
92 changes: 92 additions & 0 deletions sdk/core/azure-core/azure/core/tracing/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# --------------------------------------------------------------------------
#
# 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.
#
# --------------------------------------------------------------------------
"""Common functions shared by both the sync and the async decorators."""

from azure.core.tracing.context import tracing_context
from azure.core.tracing.abstract_span import AbstractSpan
from azure.core.settings import settings


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, 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:
span_instance = wrapped_span.span_instance
if impl_wrapper is not None:
impl_wrapper.set_current_span(span_instance)


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.

: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`
"""
wrapper_class = settings.tracing_implementation()
if wrapper_class is None:
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:
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")
)

return parent_span


def should_use_trace(parent_span):
# 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)
7 changes: 4 additions & 3 deletions sdk/core/azure-core/azure/core/tracing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
# 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

try:
from typing import TYPE_CHECKING
Expand Down Expand Up @@ -119,7 +122,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
Expand All @@ -129,7 +131,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 = 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()
Expand All @@ -140,7 +142,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
Expand Down
64 changes: 64 additions & 0 deletions sdk/core/azure-core/azure/core/tracing/decorator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# --------------------------------------------------------------------------
#
# 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.
#
# --------------------------------------------------------------------------
"""The decorator to apply if you want the given function traced."""

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_trace(func):
# type: (Callable[[Any], Any]) -> Callable[[Any], Any]
@functools.wraps(func)
def wrapper_use_tracer(self, *args, **kwargs):
# type: (Any) -> Any
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)
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 orig_wrapped_span is None and passed_in_parent is None:
parent_span.finish()
common.set_span_contexts(orig_wrapped_span, span_instance=original_span_instance)
else:
ans = func(self, *args, **kwargs)
return ans

return wrapper_use_tracer
Loading