summary refs log tree commit diff
path: root/synapse/util/metrics.py
diff options
context:
space:
mode:
authorAndrew Morgan <1342360+anoadragon453@users.noreply.github.com>2020-10-21 16:42:09 +0100
committerGitHub <noreply@github.com>2020-10-21 16:42:09 +0100
commit4d5ed3daf41a9f5e644f73d4dfd0a1f23c6a0a80 (patch)
treeb3457beef004549eb8d986dc8c84914ab025c258 /synapse/util/metrics.py
parentRemember mappings when we bind a 3pid using the internal sydent bind API (#66) (diff)
parentChangelog (diff)
downloadsynapse-4d5ed3daf41a9f5e644f73d4dfd0a1f23c6a0a80.tar.xz
Merge pull request #67 from matrix-org/anoa/dinsic_release_1_21_x
Merge Synapse release v1.21.2 into 'dinsic'
Diffstat (limited to 'synapse/util/metrics.py')
-rw-r--r--synapse/util/metrics.py79
1 files changed, 48 insertions, 31 deletions
diff --git a/synapse/util/metrics.py b/synapse/util/metrics.py

index ec61e14423..ffdea0de8d 100644 --- a/synapse/util/metrics.py +++ b/synapse/util/metrics.py
@@ -13,15 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -import inspect import logging from functools import wraps +from typing import Any, Callable, Optional, TypeVar, cast from prometheus_client import Counter -from twisted.internet import defer - -from synapse.logging.context import LoggingContext, current_context +from synapse.logging.context import ( + ContextResourceUsage, + LoggingContext, + current_context, +) from synapse.metrics import InFlightGauge logger = logging.getLogger(__name__) @@ -60,34 +62,42 @@ in_flight = InFlightGauge( sub_metrics=["real_time_max", "real_time_sum"], ) +T = TypeVar("T", bound=Callable[..., Any]) -def measure_func(name=None): - def wrapper(func): - block_name = func.__name__ if name is None else name - if inspect.iscoroutinefunction(func): +def measure_func(name: Optional[str] = None) -> Callable[[T], T]: + """ + Used to decorate an async function with a `Measure` context manager. + + Usage: + + @measure_func() + async def foo(...): + ... - @wraps(func) - async def measured_func(self, *args, **kwargs): - with Measure(self.clock, block_name): - r = await func(self, *args, **kwargs) - return r + Which is analogous to: - else: + async def foo(...): + with Measure(...): + ... - @wraps(func) - @defer.inlineCallbacks - def measured_func(self, *args, **kwargs): - with Measure(self.clock, block_name): - r = yield func(self, *args, **kwargs) - return r + """ - return measured_func + def wrapper(func: T) -> T: + block_name = func.__name__ if name is None else name + + @wraps(func) + async def measured_func(self, *args, **kwargs): + with Measure(self.clock, block_name): + r = await func(self, *args, **kwargs) + return r + + return cast(T, measured_func) return wrapper -class Measure(object): +class Measure: __slots__ = [ "clock", "name", @@ -98,27 +108,27 @@ class Measure(object): def __init__(self, clock, name): self.clock = clock self.name = name - self._logging_context = None + parent_context = current_context() + self._logging_context = LoggingContext( + "Measure[%s]" % (self.name,), parent_context + ) self.start = None - def __enter__(self): - if self._logging_context: + def __enter__(self) -> "Measure": + if self.start is not None: raise RuntimeError("Measure() objects cannot be re-used") self.start = self.clock.time() - parent_context = current_context() - self._logging_context = LoggingContext( - "Measure[%s]" % (self.name,), parent_context - ) self._logging_context.__enter__() in_flight.register((self.name,), self._update_in_flight) + return self def __exit__(self, exc_type, exc_val, exc_tb): - if not self._logging_context: + if self.start is None: raise RuntimeError("Measure() block exited without being entered") duration = self.clock.time() - self.start - usage = self._logging_context.get_resource_usage() + usage = self.get_resource_usage() in_flight.unregister((self.name,), self._update_in_flight) self._logging_context.__exit__(exc_type, exc_val, exc_tb) @@ -134,6 +144,13 @@ class Measure(object): except ValueError: logger.warning("Failed to save metrics! Usage: %s", usage) + def get_resource_usage(self) -> ContextResourceUsage: + """Get the resources used within this Measure block + + If the Measure block is still active, returns the resource usage so far. + """ + return self._logging_context.get_resource_usage() + def _update_in_flight(self, metrics): """Gets called when processing in flight metrics """