Mark Bergsma has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/371636 )

Change subject: Instrument the Twisted reactor with Prometheus metrics
......................................................................

Instrument the Twisted reactor with Prometheus metrics

instrumented_reactor adds InstrumentedReactor which derives from either
the EPollReactor or SelectReactor (depending on availability on the
platform), and decorates the most important methods with Prometheus
timing metrics.

Change-Id: Ie7cccd14687808b1c4d4a224d82b8b4ed8d0cbce
---
M pybal/__init__.py
A pybal/instrumented_reactor.py
M pybal/metrics.py
3 files changed, 94 insertions(+), 5 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/operations/debs/pybal 
refs/changes/36/371636/1

diff --git a/pybal/__init__.py b/pybal/__init__.py
index ade8bc3..a6418e7 100644
--- a/pybal/__init__.py
+++ b/pybal/__init__.py
@@ -4,6 +4,11 @@
 
 The pybal package contains all PyBal modules
 """
+
+# Use our own instrumented epoll reactor
+from pybal import instrumented_reactor
+instrumented_reactor.install()
+
 import test
 
 from .version import *
diff --git a/pybal/instrumented_reactor.py b/pybal/instrumented_reactor.py
new file mode 100644
index 0000000..7cc0ac2
--- /dev/null
+++ b/pybal/instrumented_reactor.py
@@ -0,0 +1,64 @@
+"""
+reactor.py
+
+An instrumented reactor (epoll or select).
+To install the reactor (and you should do this before any connections,
+listeners or connectors are added)::
+
+    from pybal import instrumented_reactor
+    instrumented_reactor.install()
+"""
+
+from pybal.metrics import Summary
+
+try:
+    from twisted.internet.epollreactor import EPollReactor
+    ancestor = EPollReactor
+except ImportError:
+    from twisted.internet.selectreactor import SelectReactor
+    ancestor = SelectReactor
+
+class InstrumentedReactor(ancestor):
+    metric_keywords = {
+        'namespace': 'pybal',
+        'subsystem': 'reactor'
+    }
+    metrics = {
+        'iteration_duration':
+            Summary(
+                'iteration_duration',
+                'Reactor iteration latency',
+                **metric_keywords),
+        'do_read_or_write_duration':
+            Summary(
+                'do_read_or_write_duration',
+                'doReadOrWrite latency',
+                labelnames=('selectable', 'method'),
+                **metric_keywords),
+        'run_until_current_duration':
+            Summary(
+                'run_until_current_duration',
+                'runUntilCurrent latency',
+                **metric_keywords)
+    }
+
+    if ancestor.__name__ == 'EPollReactor':
+        doPoll = 
metrics['iteration_duration'].time()(EPollReactor.doPoll.__func__)
+        doIteration = doPoll
+    elif ancestor.__name__ == 'SelectReactor':
+        doSelect = 
metrics['iteration_duration'].time()(SelectReactor.doSelect.__func__)
+        doIteration = doSelect
+
+    runUntilCurrent = 
metrics['run_until_current_duration'].time()(ancestor.runUntilCurrent.__func__)
+
+    def _doReadOrWrite(self, selectable, method):
+        with self.metrics['do_read_or_write_duration'].labels(
+            type(selectable).__name__, method).time():
+                super(InstrumentedReactor, self)._doReadOrWrite(selectable, 
method)
+
+def install():
+    """
+    Install the instrumented epoll() reactor.
+    """
+    from twisted.internet.main import installReactor
+    installReactor(InstrumentedReactor())
diff --git a/pybal/metrics.py b/pybal/metrics.py
index 4ccb064..74de644 100644
--- a/pybal/metrics.py
+++ b/pybal/metrics.py
@@ -10,24 +10,44 @@
 except ImportError:
     metrics_implementation = 'dummy'
 
-class DummyMetric(object):
-    def __init__(self, **kwargs):
+class DummyTimer(object):
+    def __call__(self, func):
+        def wrap(*args, **kwargs):
+            func(*args, **kwargs)
+        return wrap
+
+    def __enter__(self, *args, **kwargs):
+        pass
+    def __exit__(self, *args, **kwargs):
         pass
 
-    def labels(self, **kwargs):
+class DummyMetric(object):
+    def __init__(self, *args, **kwargs):
+        pass
+
+    def labels(self, *args, **kwargs):
         return self
 
 class DummyCounter(DummyMetric):
-    def inc(self, **kwargs):
+    def inc(self, *args, **kwargs):
         pass
 
 class DummyGauge(DummyMetric):
-    def set(**kwargs):
+    def set(self, *args, **kwargs):
         pass
+
+class DummySummary(DummyMetric):
+    def observe(self, *args, **kwargs):
+        pass
+
+    def time(self):
+        return DummyTimer()
 
 if metrics_implementation == 'prometheus':
     Counter = prometheus_client.Counter
     Gauge = prometheus_client.Gauge
+    Summary = prometheus_client.Summary
 else:
     Counter = DummyCounter
     Gauge = DummyGauge
+    Summary = DummySummary

-- 
To view, visit https://gerrit.wikimedia.org/r/371636
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ie7cccd14687808b1c4d4a224d82b8b4ed8d0cbce
Gerrit-PatchSet: 1
Gerrit-Project: operations/debs/pybal
Gerrit-Branch: master
Gerrit-Owner: Mark Bergsma <m...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to