From b43e3af7449dcfdc61d0f3fd1e72f750cd187af4 Mon Sep 17 00:00:00 2001 From: Jorik Schellekens Date: Fri, 21 Jun 2019 12:10:20 +0100 Subject: [PATCH] Scope manager using LogContexts We piggy-back our tracer scopes by using log context. The current log context gives us the current scope. If new scope is created we create a stack of scopes in the context. --- synapse/util/logcontext.py | 8 ++- synapse/util/scopecontextmanager.py | 87 +++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 synapse/util/scopecontextmanager.py diff --git a/synapse/util/logcontext.py b/synapse/util/logcontext.py index fe412355d8..62ffc36f60 100644 --- a/synapse/util/logcontext.py +++ b/synapse/util/logcontext.py @@ -164,6 +164,7 @@ class LoggingContext(object): "usage_start", "main_thread", "alive", "request", "tag", + "active_scope" ] thread_local = threading.local() @@ -215,6 +216,7 @@ class LoggingContext(object): self.request = None self.tag = "" self.alive = True + self.active_scope = None self.parent_context = parent_context @@ -299,10 +301,12 @@ class LoggingContext(object): another LoggingContext """ - # 'request' is the only field we currently use in the logger, so that's - # all we need to copy + # we track the current request record.request = self.request + # we also track the current active_scope: + record.active_scope = self.active_scope + def start(self): if threading.current_thread() is not self.main_thread: logger.warning("Started logcontext %s on different thread", self) diff --git a/synapse/util/scopecontextmanager.py b/synapse/util/scopecontextmanager.py new file mode 100644 index 0000000000..370210eb44 --- /dev/null +++ b/synapse/util/scopecontextmanager.py @@ -0,0 +1,87 @@ +from .logcontext import LoggingContext +from opentracing import ScopeManager, Scope +import logging + +logger = logging.getLogger(__name__) + +class LogContextScopeManager(ScopeManager): + + _homeserver_whitelist = ["*"] + _user_whitelist = ["*"] + + def __init__(self, config): + # Set the whitelists + logger.info(config.tracer_config) + _homeserver_whitelist = config.tracer_config["homeserver_whitelist"] + _user_whitelist = config.tracer_config["user_whitelist"] + + @property + def active(self): + """ + Returns the currently active Scope which can be used to access the + currently active Scope.span. + If there is a non-null Scope, its wrapped Span + becomes an implicit parent of any newly-created Span at + Tracer.start_active_span() time. + + Return: + (Scope) : the Scope that is active, or None if not + available. + """ + ctx = LoggingContext.current_context() + if ctx is LoggingContext.sentinel or ctx.active_scope is None: + return None + else: + return ctx.active_scope + + def activate(self, span, finish_on_close): + """ + Makes a Span active. + Args + span (Span): the span that should become active. + finish_on_close (Boolean): whether Span should be automatically + finished when Scope.close() is called. + + Return: + Scope to control the end of the active period for + *span*. It is a programming error to neglect to call + Scope.close() on the returned instance. + """ + logger.info("activating scope") + ctx = LoggingContext.current_context() + if ctx is LoggingContext.sentinel: + # We don't want this scope to affect. + logger.warning("Tried to activate scope outside of loggingcontext") + return Scope(None, span) + + scope = _LogContextScope(self, span, finish_on_close) + self._set_logcontext_scope(scope, ctx) + return scope + + def _set_logcontext_scope(self, scope, ctx=None): + if ctx is None: + ctx = LoggingContext.current_context() + + ctx.active_scope = scope + + def request_from_whitelisted_homeserver(self, request): + pass + + def user_whitelisted(self, request): + pass + +class _LogContextScope(Scope): + def __init__(self, manager, span, finish_on_close): + super(_LogContextScope, self).__init__(manager, span) + self._finish_on_close = finish_on_close + self._to_restore = manager.active + + def close(self): + if self.manager.active is not self: + logger.warning("Tried to close a none active scope!") + return + + self.manager._set_logcontext_scope(self._to_restore) + + if self._finish_on_close: + self.span.finish() \ No newline at end of file