Skip to content

Commit df4abd8

Browse files
committed
Added context managers and decorators.
1 parent ff6c059 commit df4abd8

File tree

7 files changed

+293
-24
lines changed

7 files changed

+293
-24
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ publish: clean-pyc
3333
$(DOCKER_RUN_COMMAND) "twine upload dist/*"
3434
@echo ""
3535

36-
test: clean-conteainers
36+
test: clean-containers
3737
@echo "Test application $(version)"
3838
$(DOCKER_RUN_COMMAND) "uwsgi --pyrun setup.py --pyargv test --sharedarea=100 --enable-threads"
3939
@echo ""

README.rst

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ INSTALLATION
2424

2525
To use pyprometheus use pip or easy_install:
2626

27-
`pip install pyprometheus`
27+
:code:`pip install pyprometheus`
2828

2929
or
3030

31-
`easy_install pyprometheus`
31+
:code:`easy_install pyprometheus`
3232

3333

3434
HOW TO INSTRUMENTING CODE
@@ -44,11 +44,29 @@ A gauge is a metric that represents a single numerical value that can arbitraril
4444

4545
storage = LocalMemoryStorage()
4646
registry = CollectorRegistry(storage=storage)
47-
job_in_progress = Gauge("job_in_progress", "Description", registry=registry)
47+
gauge = Gauge("job_in_progress", "Description", registry=registry)
48+
49+
gauge.inc(10)
50+
gauge.dec(5)
51+
gauge.set(21.1)
52+
53+
54+
utilities::
55+
56+
gauge.set_to_current_time() # Set to current unixtime
57+
58+
# Increment when entered, decrement when exited.
59+
@gauge.track_in_progress()
60+
def f():
61+
pass
62+
63+
with gauge.track_in_progress():
64+
pass
65+
66+
67+
with gauge.time():
68+
time.sleep(10)
4869

49-
job_in_progress.inc(10)
50-
job_in_progress.dec(5)
51-
job_in_progress.set(21.1)
5270

5371

5472
Counter
@@ -61,9 +79,12 @@ A counter is a cumulative metric that represents a single numerical value that o
6179

6280
storage = LocalMemoryStorage()
6381
registry = CollectorRegistry(storage=storage)
64-
requests_total = Counter("requests_total", "Description", registry=registry)
82+
counter = Counter("requests_total", "Description", registry=registry)
83+
84+
counter.inc(10)
85+
86+
6587

66-
requests_total.inc(10)
6788

6889

6990
Summary
@@ -81,6 +102,17 @@ Similar to a histogram, a summary samples observations (usually things like requ
81102
s.observe(0.100)
82103

83104

105+
utilities for timing code::
106+
107+
@gauge.time()
108+
def func():
109+
time.sleep(10)
110+
111+
with gauge.time():
112+
time.sleep(10)
113+
114+
115+
84116
Histogram
85117
~~~~~~~~~
86118

@@ -91,9 +123,19 @@ A histogram samples observations (usually things like request durations or respo
91123

92124
storage = LocalMemoryStorage()
93125
registry = CollectorRegistry(storage=storage)
94-
h = Histogram("requests_duration_seconds", "Description", registry=registry)
126+
histogram = Histogram("requests_duration_seconds", "Description", registry=registry)
127+
128+
histogram.observe(1.1)
129+
130+
utilities for timing code::
131+
132+
@histogram.time()
133+
def func():
134+
time.sleep(10)
135+
136+
with histogram.time():
137+
time.sleep(10)
95138

96-
h.observe(1.1)
97139

98140

99141
Labels

docs/usage.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
HOW TO INSTRUMENTING CODE
2+
=========================

pyprometheus/managers.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
pyprometheus.values
6+
~~~~~~~~~~~~~~~~~~~
7+
8+
Prometheus instrumentation library for Python applications
9+
10+
:copyright: (c) 2017 by Alexandr Lispython.
11+
:license: , see LICENSE for more details.
12+
:github: http://github.com/Lispython/pyprometheus
13+
"""
14+
import time
15+
from functools import wraps
16+
17+
default_timer = time.time
18+
19+
class BaseManager(object):
20+
def __call__(self, f):
21+
@wraps(f)
22+
def wrapper(*args, **kwargs):
23+
with self:
24+
return f(*args, **kwargs)
25+
return wrapper
26+
27+
28+
class TimerManager(BaseManager):
29+
def __init__(self, collector):
30+
self._collector = collector
31+
32+
def __enter__(self):
33+
self._start_time = default_timer()
34+
35+
def __exit__(self, exc_type, exc_value, traceback):
36+
self._collector.observe(default_timer() - self._start_time)
37+
38+
39+
class InprogressTrackerManager(BaseManager):
40+
41+
def __init__(self, gauge):
42+
self._gauge = gauge
43+
44+
def __enter__(self):
45+
self._gauge.inc()
46+
47+
def __exit__(self, exc_info, exc_value, traceback):
48+
self._gauge.dec()
49+
50+
51+
class GaugeTimerManager(TimerManager):
52+
53+
def __exit__(self, exc_type, exc_value, traceback):
54+
self._collector.set(default_timer() - self._start_time)

pyprometheus/metrics.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
CounterValue, SummaryValue,
1919
HistogramValue)
2020

21-
2221
class BaseMetric(object):
2322

2423
value_class = MetricValue
@@ -27,6 +26,8 @@ class BaseMetric(object):
2726

2827
TYPE = "untyped"
2928

29+
PARENT_METHODS = set()
30+
3031
def __init__(self, name, doc, labels=[], registry=None):
3132
self._name = name
3233
self._doc = doc
@@ -45,7 +46,7 @@ def __repr__(self):
4546
return u"<{0}[{1}]: {2} samples>".format(self.__class__.__name__, self._name, len(self._samples))
4647

4748
def get_proxy(self):
48-
if self._labelsnames:
49+
if self._labelnames:
4950
raise RuntimeError("You need to use labels")
5051
return self.value_class(self, label_values={})
5152

@@ -123,23 +124,35 @@ def get_samples(self):
123124
return self._samples.values()
124125

125126

127+
def __getattr__(self, name):
128+
if name in self.PARENT_METHODS:
129+
return getattr(self.get_proxy(), name)
130+
131+
raise AttributeError
132+
# return super(BaseMetric, self).__getattr__(name)
133+
134+
135+
136+
137+
138+
126139
class Gauge(BaseMetric):
127140

128141
TYPE = "gauge"
129142

130143
value_class = GaugeValue
131144

132-
inc = property(BaseMetric.get_proxy)
133-
dec = property(BaseMetric.get_proxy)
134-
set = property(BaseMetric.get_proxy)
145+
PARENT_METHODS = set(('inc', 'dec', 'set', 'get', 'track_inprogress',
146+
'set_to_current_time', 'time', 'value'))
147+
135148

136149

137150
class Counter(BaseMetric):
138151
TYPE = "counter"
139152

140153
value_class = CounterValue
141154

142-
inc = property(BaseMetric.get_proxy)
155+
PARENT_METHODS = set(('inc', 'get', 'value'))
143156

144157

145158
class Summary(BaseMetric):
@@ -151,12 +164,12 @@ class Summary(BaseMetric):
151164

152165
NOT_ALLOWED_LABELS = set('quantile')
153166

167+
PARENT_METHODS = set(('observe', 'value', 'time'))
168+
154169
def __init__(self, name, doc, labels=[], quantiles=False, registry=None):
155170
self._quantiles = list(sorted(quantiles)) if quantiles else []
156171
super(Summary, self).__init__(name, doc, labels, registry)
157172

158-
observe = property(BaseMetric.get_proxy)
159-
160173
@property
161174
def quantiles(self):
162175
return self._quantiles
@@ -193,12 +206,12 @@ class Histogram(BaseMetric):
193206

194207
value_class = HistogramValue
195208

209+
PARENT_METHODS = set(('observe', 'value', 'time'))
210+
196211
def __init__(self, name, doc, labels=[], buckets=DEFAULT_BUCKETS, registry=None):
197212
self._buckets = list(sorted(buckets)) if buckets else []
198213
super(Histogram, self).__init__(name, doc, labels, registry)
199214

200-
observe = property(BaseMetric.get_proxy)
201-
202215
@property
203216
def buckets(self):
204217
return self._buckets

pyprometheus/values.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import time
1616

1717
from pyprometheus.const import TYPES
18-
18+
from pyprometheus.managers import TimerManager, InprogressTrackerManager, GaugeTimerManager
1919

2020
class MetricValue(object):
2121
"""Base metric collector
@@ -109,16 +109,26 @@ class GaugeValue(MetricValue):
109109

110110
TYPE = TYPES.GAUGE
111111

112-
def dec(self, amount):
112+
def dec(self, amount=1):
113113
self.inc(-amount)
114114

115115
def set(self, value):
116116
self._metric._storage.write_value(self.key, value)
117+
return value
117118

118119
@property
119120
def value(self):
120121
return self.get()
121122

123+
def track_in_progress(self):
124+
return InprogressTrackerManager(self)
125+
126+
def set_to_current_time(self):
127+
return self.set(time.time())
128+
129+
def time(self):
130+
return GaugeTimerManager(self)
131+
122132

123133
class CounterValue(MetricValue):
124134

@@ -231,6 +241,9 @@ def export_str(self):
231241
return "\n".join([self._sum.export_str, self._count.export_str] + [quantile.export_str for quantile in self._quantiles])
232242

233243

244+
def time(self):
245+
return TimerManager(self)
246+
234247

235248
class HistogramCountValue(SummaryCountValue):
236249
TYPE = TYPES.HISTOGRAM_COUNTER
@@ -319,3 +332,7 @@ def value(self):
319332
@property
320333
def export_str(self):
321334
return "\n".join([self._sum.export_str, self._count.export_str] + [bucket.export_str for bucket in self._buckets])
335+
336+
337+
def time(self):
338+
return TimerManager(self)

0 commit comments

Comments
 (0)