From d4070bd5ff84abd70225c9b893557198560b3e68 Mon Sep 17 00:00:00 2001 From: Mike Bryant Date: Tue, 19 Dec 2017 15:38:43 +0000 Subject: [PATCH 1/4] fix: If no tracer is given, supply a valid DjangoTracer Without this, the private methods called don't exist on opentracing.Tracer Fixes #8 --- django_opentracing/middleware.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/django_opentracing/middleware.py b/django_opentracing/middleware.py index bea0804..01439f4 100644 --- a/django_opentracing/middleware.py +++ b/django_opentracing/middleware.py @@ -1,4 +1,5 @@ from django.conf import settings +from django_opentracing.tracer import DjangoTracer import opentracing try: # Django >= 1.10 @@ -21,7 +22,7 @@ def __init__(self, get_response=None): if hasattr(settings, 'OPENTRACING_TRACER'): self._tracer = settings.OPENTRACING_TRACER else: - self._tracer = opentracing.Tracer() + self._tracer = DjangoTracer(opentracing.Tracer()) def process_view(self, request, view_func, view_args, view_kwargs): # determine whether this middleware should be applied From 81688efe8402acfe26b29077e0779e0c9383ad2d Mon Sep 17 00:00:00 2001 From: Mike Bryant Date: Tue, 19 Dec 2017 15:48:22 +0000 Subject: [PATCH 2/4] fix: Add missing get_response --- django_opentracing/middleware.py | 1 + 1 file changed, 1 insertion(+) diff --git a/django_opentracing/middleware.py b/django_opentracing/middleware.py index 01439f4..f9fb7fc 100644 --- a/django_opentracing/middleware.py +++ b/django_opentracing/middleware.py @@ -19,6 +19,7 @@ def __init__(self, get_response=None): - Is it better to place all tracing info in the settings file, or to require a tracing.py file with configurations? - Also, better to have try/catch with empty tracer or just fail fast if there's no tracer specified ''' + self.get_response = get_response if hasattr(settings, 'OPENTRACING_TRACER'): self._tracer = settings.OPENTRACING_TRACER else: From e625a6396937e3886de5e5eac9a39945f266f9c3 Mon Sep 17 00:00:00 2001 From: Mike Bryant Date: Wed, 20 Dec 2017 15:43:30 +0000 Subject: [PATCH 3/4] fix: Use lazy initialization for the opentracing Tracer Classes shouldn't be instantiated during settings import. Delay initialization till the middleware is needed. Fixes #10 Fixes #7 --- README.rst | 12 +++++++++ django_opentracing/middleware.py | 11 +++----- django_opentracing/tracer.py | 43 +++++++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 45f21c3..1d14f94 100644 --- a/README.rst +++ b/README.rst @@ -37,6 +37,18 @@ In order to implement tracing in your system, add the following lines of code to # only valid if OPENTRACING_TRACE_ALL == True OPENTRACING_TRACED_ATTRIBUTES = ['arg1', 'arg2'], + # Callable that returns an `opentracing.Tracer` implementation. + OPENTRACING_TRACER_CALLABLE = 'opentracing.Tracer' + + # Parameters for the callable (Depending on the tracer implementation chosen) + OPENTRACING_TRACER_PARAMETERS = { + 'example-parameter-host': 'collector', + } + +If you want to directly override the `DjangoTracer` used, you can use the following. This may cause import loops (See #10) + +.. code-block:: python + # some_opentracing_tracer can be any valid OpenTracing tracer implementation OPENTRACING_TRACER = django_opentracing.DjangoTracer(some_opentracing_tracer), diff --git a/django_opentracing/middleware.py b/django_opentracing/middleware.py index f9fb7fc..74056b1 100644 --- a/django_opentracing/middleware.py +++ b/django_opentracing/middleware.py @@ -1,6 +1,5 @@ -from django.conf import settings -from django_opentracing.tracer import DjangoTracer -import opentracing +from django.conf import settings +from django_opentracing.tracer import initialize_global_tracer, DjangoTracer try: # Django >= 1.10 from django.utils.deprecation import MiddlewareMixin @@ -20,10 +19,8 @@ def __init__(self, get_response=None): - Also, better to have try/catch with empty tracer or just fail fast if there's no tracer specified ''' self.get_response = get_response - if hasattr(settings, 'OPENTRACING_TRACER'): - self._tracer = settings.OPENTRACING_TRACER - else: - self._tracer = DjangoTracer(opentracing.Tracer()) + initialize_global_tracer() + self._tracer = DjangoTracer() def process_view(self, request, view_func, view_args, view_kwargs): # determine whether this middleware should be applied diff --git a/django_opentracing/tracer.py b/django_opentracing/tracer.py index e0e6e5f..fb662b9 100644 --- a/django_opentracing/tracer.py +++ b/django_opentracing/tracer.py @@ -1,13 +1,17 @@ from django.conf import settings +from django.utils.module_loading import import_string import opentracing +import threading class DjangoTracer(object): ''' @param tracer the OpenTracing tracer to be used to trace requests using this DjangoTracer ''' - def __init__(self, tracer): - self._tracer = tracer + def __init__(self, tracer=None): + self._tracer_implementation = None + if tracer: + self._tracer_implementation = tracer self._current_spans = {} if not hasattr(settings, 'OPENTRACING_TRACE_ALL'): self._trace_all = False @@ -16,6 +20,13 @@ def __init__(self, tracer): else: self._trace_all = True + @property + def _tracer(self): + if self._tracer_implementation: + return self._tracer_implementation + else: + return opentracing.tracer + def get_span(self, request): ''' @param request @@ -85,4 +96,30 @@ def _apply_tracing(self, request, view_func, attributes): def _finish_tracing(self, request): span = self._current_spans.pop(request, None) if span is not None: - span.finish() \ No newline at end of file + span.finish() + + +def initialize_global_tracer(): + ''' + Initialisation as per https://github.com/opentracing/opentracing-python/blob/9f9ef02d4ef7863fb26d3534a38ccdccf245494c/opentracing/__init__.py#L36 + + Here the global tracer object gets initialised once from Django settings. + ''' + # Short circuit without taking a lock + if initialize_global_tracer.complete: + return + with initialize_global_tracer.lock: + if initialize_global_tracer.complete: + return + if hasattr(settings, 'OPENTRACING_TRACER'): + # Backwards compatibility with the old way of defining the tracer + opentracing.tracer = settings.OPENTRACING_TRACER._tracer + else: + tracer_callable = getattr(settings, 'OPENTRACING_TRACER_CALLABLE', 'opentracing.Tracer') + tracer_parameters = getattr(settings, 'OPENTRACING_TRACER_PARAMETERS', {}) + opentracing.tracer = import_string(tracer_callable)(**tracer_parameters) + initialize_global_tracer.complete = True + + +initialize_global_tracer.lock = threading.Lock() +initialize_global_tracer.complete = False From 1d7aa68e54894a0dc97bb5f3ebaeba560c094d5b Mon Sep 17 00:00:00 2001 From: Mike Bryant Date: Sun, 24 Dec 2017 10:56:49 +0000 Subject: [PATCH 4/4] fix: Initialize the This lets us reference the global store of spans for requests and so on --- django_opentracing/middleware.py | 4 ++-- django_opentracing/tracer.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/django_opentracing/middleware.py b/django_opentracing/middleware.py index 74056b1..7626695 100644 --- a/django_opentracing/middleware.py +++ b/django_opentracing/middleware.py @@ -1,5 +1,5 @@ from django.conf import settings -from django_opentracing.tracer import initialize_global_tracer, DjangoTracer +from django_opentracing.tracer import initialize_global_tracer try: # Django >= 1.10 from django.utils.deprecation import MiddlewareMixin @@ -20,7 +20,7 @@ def __init__(self, get_response=None): ''' self.get_response = get_response initialize_global_tracer() - self._tracer = DjangoTracer() + self._tracer = settings.OPENTRACING_TRACER def process_view(self, request, view_func, view_args, view_kwargs): # determine whether this middleware should be applied diff --git a/django_opentracing/tracer.py b/django_opentracing/tracer.py index fb662b9..1ee2742 100644 --- a/django_opentracing/tracer.py +++ b/django_opentracing/tracer.py @@ -118,6 +118,7 @@ def initialize_global_tracer(): tracer_callable = getattr(settings, 'OPENTRACING_TRACER_CALLABLE', 'opentracing.Tracer') tracer_parameters = getattr(settings, 'OPENTRACING_TRACER_PARAMETERS', {}) opentracing.tracer = import_string(tracer_callable)(**tracer_parameters) + settings.OPENTRACING_TRACER = DjangoTracer() initialize_global_tracer.complete = True