diff --git a/synapse/http/server.py b/synapse/http/server.py
index b60e905a62..05ecff9b24 100644
--- a/synapse/http/server.py
+++ b/synapse/http/server.py
@@ -77,7 +77,7 @@ def request_handler(request_handler):
global _next_request_id
request_id = "%s-%s" % (request.method, _next_request_id)
_next_request_id += 1
- with LoggingContext(request_id) as request_context:
+ with LoggingContext(request_id, True) as request_context:
request_context.request = request_id
with request.processing():
try:
diff --git a/synapse/util/logcontext.py b/synapse/util/logcontext.py
index 7e6062c1b8..8a36bbf32d 100644
--- a/synapse/util/logcontext.py
+++ b/synapse/util/logcontext.py
@@ -16,10 +16,16 @@ from twisted.internet import defer
import threading
import logging
+from resource import getrusage, RUSAGE_SELF
+
logger = logging.getLogger(__name__)
+def _get_cpu_time():
+ return getrusage(RUSAGE_SELF).ru_utime * 1000
+
+
class LoggingContext(object):
"""Additional context for log formatting. Contexts are scoped within a
"with" block. Contexts inherit the state of their parent contexts.
@@ -27,7 +33,10 @@ class LoggingContext(object):
name (str): Name for the context for debugging.
"""
- __slots__ = ["parent_context", "name", "__dict__"]
+ __slots__ = [
+ "parent_context", "name", "__dict__", "cpu_time", "_current_cpu_time",
+ "_log_time"
+ ]
thread_local = threading.local()
@@ -42,11 +51,20 @@ class LoggingContext(object):
def copy_to(self, record):
pass
+ def resume(self):
+ pass
+
+ def pause(self):
+ pass
+
sentinel = Sentinel()
- def __init__(self, name=None):
+ def __init__(self, name=None, log_time=False):
self.parent_context = None
self.name = name
+ self.cpu_time = 0
+ self._current_cpu_time = 0
+ self._log_time = log_time
def __str__(self):
return "%s@%x" % (self.name, id(self))
@@ -62,6 +80,7 @@ class LoggingContext(object):
raise Exception("Attempt to enter logging context multiple times")
self.parent_context = self.current_context()
self.thread_local.current_context = self
+ self.resume()
return self
def __exit__(self, type, value, traceback):
@@ -79,9 +98,24 @@ class LoggingContext(object):
self.thread_local.current_context,
self
)
+ self.pause()
+
+ if self._log_time:
+ logger.info("Elapsed CPU time: %d %r", self.cpu_time, self.name)
+
self.thread_local.current_context = self.parent_context
self.parent_context = None
+ def resume(self):
+ if self._log_time:
+ self._current_cpu_time = _get_cpu_time()
+
+ def pause(self):
+ if self._log_time:
+ now = _get_cpu_time()
+ self.cpu_time += now - self._current_cpu_time
+ self._current_cpu_time = now
+
def __getattr__(self, name):
"""Delegate member lookup to parent context"""
return getattr(self.parent_context, name)
@@ -121,15 +155,24 @@ class PreserveLoggingContext(object):
exited. Used to restore the context after a function using
@defer.inlineCallbacks is resumed by a callback from the reactor."""
- __slots__ = ["current_context"]
+ __slots__ = ["current_context", "inner_context"]
+
+ def __init__(self, inner_context=LoggingContext.sentinel):
+ object.__setattr__(self, "inner_context", inner_context)
def __enter__(self):
"""Captures the current logging context"""
self.current_context = LoggingContext.current_context()
- LoggingContext.thread_local.current_context = LoggingContext.sentinel
+ LoggingContext.thread_local.current_context = self.inner_context
+
+ self.current_context.pause()
+ self.inner_context.resume()
def __exit__(self, type, value, traceback):
"""Restores the current logging context"""
+ self.inner_context.pause()
+ self.current_context.resume()
+
LoggingContext.thread_local.current_context = self.current_context
if self.current_context is not LoggingContext.sentinel:
@@ -164,8 +207,7 @@ class _PreservingContextDeferred(defer.Deferred):
def _wrap_callback(self, f):
def g(res, *args, **kwargs):
- with PreserveLoggingContext():
- LoggingContext.thread_local.current_context = self._log_context
+ with PreserveLoggingContext(self._log_context):
res = f(res, *args, **kwargs)
return res
return g
|