From 261906b4c821891bddd6959683d5157903694437 Mon Sep 17 00:00:00 2001 From: Carlos Alberto Cortez Date: Thu, 19 Jul 2018 21:13:19 +0200 Subject: [PATCH 1/2] Initial implementation of a global Tracer. --- opentracing/__init__.py | 4 +- opentracing/globaltracer.py | 104 ++++++++++++++++++++++++++++++++++++ tests/test_globaltracer.py | 34 ++++++++++++ 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 opentracing/globaltracer.py create mode 100644 tests/test_globaltracer.py diff --git a/opentracing/__init__.py b/opentracing/__init__.py index e72c2dc..94660d0 100644 --- a/opentracing/__init__.py +++ b/opentracing/__init__.py @@ -28,8 +28,10 @@ from .propagation import InvalidCarrierException # noqa from .propagation import SpanContextCorruptedException # noqa from .propagation import UnsupportedFormatException # noqa +from .globaltracer import init_global_tracer # noqa +from .globaltracer import get_global_tracer # noqa # Global variable that should be initialized to an instance of real tracer. # Note: it should be accessed via 'opentracing.tracer', not via # 'from opentracing import tracer', the latter seems to take a copy. -tracer = Tracer() +tracer = get_global_tracer() diff --git a/opentracing/globaltracer.py b/opentracing/globaltracer.py new file mode 100644 index 0000000..9cc4b30 --- /dev/null +++ b/opentracing/globaltracer.py @@ -0,0 +1,104 @@ +# Copyright (c) The OpenTracing Authors. +# +# 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 __future__ import absolute_import + +from threading import Lock + +from .tracer import Tracer + + +DEFAULT_TRACER = Tracer() + + +class GlobalTracer(Tracer): + _tracer = DEFAULT_TRACER + _tracer_lock = Lock() + + _instance = None # assign it after the class has been fully declared. + + @staticmethod + def get_tracer(): + return GlobalTracer._instance + + @staticmethod + def init_tracer(tracer): + if tracer is None: + raise ValueError('tracer cannot be None') + + with GlobalTracer._tracer_lock: + if GlobalTracer._tracer is not DEFAULT_TRACER: + return False + + GlobalTracer._tracer = tracer + return True + + @staticmethod + def reset_tracer(): + with GlobalTracer._tracer_lock: + GlobalTracer._tracer = DEFAULT_TRACER + + @property + def scope_manager(self): + return GlobalTracer._tracer.scope_manager + + @property + def active_span(self): + return GlobalTracer._tracer.active_span + + def start_active_span(self, + operation_name, + child_of=None, + references=None, + tags=None, + start_time=None, + ignore_active_span=False, + finish_on_close=True): + return GlobalTracer._tracer.start_active_span(operation_name, + child_of, + references, + tags, + start_time, + ignore_active_span, + finish_on_close) + + def start_span(self, + operation_name=None, + child_of=None, + references=None, + tags=None, + start_time=None, + ignore_active_span=False): + return GlobalTracer._tracer.start_span(operation_name, + child_of, + references, + tags, + start_time, + ignore_active_span) + + def inject(self, span_context, format, carrier): + return GlobalTracer._tracer.inject(span_context, format, carrier) + + def extract(self, format, carrier): + return GlobalTracer._tracer.extract(format, carrier) + +GlobalTracer._instance = GlobalTracer() + +init_global_tracer = GlobalTracer.init_tracer +get_global_tracer = GlobalTracer.get_tracer diff --git a/tests/test_globaltracer.py b/tests/test_globaltracer.py new file mode 100644 index 0000000..4a20abf --- /dev/null +++ b/tests/test_globaltracer.py @@ -0,0 +1,34 @@ +# Copyright The OpenTracing Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import absolute_import +import pytest +import opentracing +from opentracing.mocktracer import MockTracer +from opentracing.globaltracer import GlobalTracer + + +def test_singleton_reference(): + assert isinstance(opentracing.tracer, GlobalTracer) + assert opentracing.tracer is opentracing.get_global_tracer() + + +def test_multiple_registrations(): + assert opentracing.init_global_tracer(MockTracer()) is True + assert opentracing.init_global_tracer(MockTracer()) is False + + +def test_register_none(): + with pytest.raises(ValueError): + opentracing.init_global_tracer(None) From b17fd60a979bdb5338ffebfc702a33ecc2a0aefe Mon Sep 17 00:00:00 2001 From: Carlos Alberto Cortez Date: Thu, 26 Jul 2018 21:25:30 +0200 Subject: [PATCH 2/2] Use a simple getter/setter for global tracer. --- README.rst | 6 +-- docs/api.rst | 4 ++ opentracing/__init__.py | 29 ++++++++-- opentracing/globaltracer.py | 104 ------------------------------------ tests/test_globaltracer.py | 22 ++++---- 5 files changed, 46 insertions(+), 119 deletions(-) delete mode 100644 opentracing/globaltracer.py diff --git a/README.rst b/README.rst index da170df..f44c3c1 100644 --- a/README.rst +++ b/README.rst @@ -55,7 +55,7 @@ Somewhere in your server's request handler code: .. code-block:: python def handle_request(request): - span = before_request(request, opentracing.tracer) + span = before_request(request, opentracing.global_tracer()) # store span in some request-local storage using Tracer.scope_manager, # using the returned `Scope` as Context Manager to ensure # `Span` will be cleared and (in this case) `Span.finish()` be called. @@ -111,7 +111,7 @@ Somewhere in your service that's about to make an outgoing call: def before_http_request(request, current_span_extractor): op = request.operation parent_span = current_span_extractor() - outbound_span = opentracing.tracer.start_span( + outbound_span = opentracing.global_tracer().start_span( operation_name=op, child_of=parent_span ) @@ -127,7 +127,7 @@ Somewhere in your service that's about to make an outgoing call: outbound_span.set_tag(tags.PEER_PORT, port) http_header_carrier = {} - opentracing.tracer.inject( + opentracing.global_tracer().inject( span_context=outbound_span, format=Format.HTTP_HEADERS, carrier=http_header_carrier) diff --git a/docs/api.rst b/docs/api.rst index f60b923..34cfd92 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -29,6 +29,10 @@ Classes Utility Functions ----------------- +.. autofunction:: opentracing.global_tracer + +.. autofunction:: opentracing.set_global_tracer + .. autofunction:: opentracing.start_child_span .. autofunction:: opentracing.child_of diff --git a/opentracing/__init__.py b/opentracing/__init__.py index 94660d0..4698372 100644 --- a/opentracing/__init__.py +++ b/opentracing/__init__.py @@ -28,10 +28,33 @@ from .propagation import InvalidCarrierException # noqa from .propagation import SpanContextCorruptedException # noqa from .propagation import UnsupportedFormatException # noqa -from .globaltracer import init_global_tracer # noqa -from .globaltracer import get_global_tracer # noqa # Global variable that should be initialized to an instance of real tracer. # Note: it should be accessed via 'opentracing.tracer', not via # 'from opentracing import tracer', the latter seems to take a copy. -tracer = get_global_tracer() +# DEPRECATED, use global_tracer() and set_global_tracer() instead. +tracer = Tracer() + + +def global_tracer(): + """Returns the global tracer. + The default value is an instance of :class:`opentracing.Tracer` + + :rtype: :class:`Tracer` + :return: The global tracer instance. + """ + return tracer + + +def set_global_tracer(value): + """Sets the global tracer. + It is an error to pass ``None``. + + :param value: the :class:`Tracer` used as global instance. + :type value: :class:`Tracer` + """ + if value is None: + raise ValueError('The global Tracer tracer cannot be None') + + global tracer + tracer = value diff --git a/opentracing/globaltracer.py b/opentracing/globaltracer.py deleted file mode 100644 index 9cc4b30..0000000 --- a/opentracing/globaltracer.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) The OpenTracing Authors. -# -# 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 __future__ import absolute_import - -from threading import Lock - -from .tracer import Tracer - - -DEFAULT_TRACER = Tracer() - - -class GlobalTracer(Tracer): - _tracer = DEFAULT_TRACER - _tracer_lock = Lock() - - _instance = None # assign it after the class has been fully declared. - - @staticmethod - def get_tracer(): - return GlobalTracer._instance - - @staticmethod - def init_tracer(tracer): - if tracer is None: - raise ValueError('tracer cannot be None') - - with GlobalTracer._tracer_lock: - if GlobalTracer._tracer is not DEFAULT_TRACER: - return False - - GlobalTracer._tracer = tracer - return True - - @staticmethod - def reset_tracer(): - with GlobalTracer._tracer_lock: - GlobalTracer._tracer = DEFAULT_TRACER - - @property - def scope_manager(self): - return GlobalTracer._tracer.scope_manager - - @property - def active_span(self): - return GlobalTracer._tracer.active_span - - def start_active_span(self, - operation_name, - child_of=None, - references=None, - tags=None, - start_time=None, - ignore_active_span=False, - finish_on_close=True): - return GlobalTracer._tracer.start_active_span(operation_name, - child_of, - references, - tags, - start_time, - ignore_active_span, - finish_on_close) - - def start_span(self, - operation_name=None, - child_of=None, - references=None, - tags=None, - start_time=None, - ignore_active_span=False): - return GlobalTracer._tracer.start_span(operation_name, - child_of, - references, - tags, - start_time, - ignore_active_span) - - def inject(self, span_context, format, carrier): - return GlobalTracer._tracer.inject(span_context, format, carrier) - - def extract(self, format, carrier): - return GlobalTracer._tracer.extract(format, carrier) - -GlobalTracer._instance = GlobalTracer() - -init_global_tracer = GlobalTracer.init_tracer -get_global_tracer = GlobalTracer.get_tracer diff --git a/tests/test_globaltracer.py b/tests/test_globaltracer.py index 4a20abf..39e74c8 100644 --- a/tests/test_globaltracer.py +++ b/tests/test_globaltracer.py @@ -14,21 +14,25 @@ from __future__ import absolute_import import pytest +import mock import opentracing -from opentracing.mocktracer import MockTracer -from opentracing.globaltracer import GlobalTracer -def test_singleton_reference(): - assert isinstance(opentracing.tracer, GlobalTracer) - assert opentracing.tracer is opentracing.get_global_tracer() +def test_opentracing_tracer(): + assert opentracing.tracer is opentracing.global_tracer() + assert isinstance(opentracing.global_tracer(), opentracing.Tracer) -def test_multiple_registrations(): - assert opentracing.init_global_tracer(MockTracer()) is True - assert opentracing.init_global_tracer(MockTracer()) is False +@mock.patch('opentracing.tracer') +def test_set_global_tracer(mock_obj): + tracer = mock.Mock() + opentracing.set_global_tracer(tracer) + assert opentracing.global_tracer() is tracer + + opentracing.set_global_tracer(mock_obj) + assert opentracing.global_tracer() is mock_obj def test_register_none(): with pytest.raises(ValueError): - opentracing.init_global_tracer(None) + opentracing.set_global_tracer(None)