##// END OF EJS Templates
statsd: fix unicode tags generation as we use str now
super-admin -
r4795:1a40cc02 default
parent child Browse files
Show More
@@ -1,128 +1,131 b''
1 1 from __future__ import absolute_import, division, unicode_literals
2 2
3 3 import re
4 4 import random
5 5 from collections import deque
6 6 from datetime import timedelta
7 7 from repoze.lru import lru_cache
8 8
9 9 from .timer import Timer
10 10
11 TAG_INVALID_CHARS_RE = re.compile(r"[^\w\d_\-:/\.]", re.UNICODE)
11 TAG_INVALID_CHARS_RE = re.compile(
12 r"[^\w\d_\-:/\.]",
13 #re.UNICODE
14 )
12 15 TAG_INVALID_CHARS_SUBS = "_"
13 16
14 17
15 18 @lru_cache(maxsize=500)
16 19 def _normalize_tags_with_cache(tag_list):
17 20 return [TAG_INVALID_CHARS_RE.sub(TAG_INVALID_CHARS_SUBS, tag) for tag in tag_list]
18 21
19 22
20 23 def normalize_tags(tag_list):
21 24 # We have to turn our input tag list into a non-mutable tuple for it to
22 25 # be hashable (and thus usable) by the @lru_cache decorator.
23 26 return _normalize_tags_with_cache(tuple(tag_list))
24 27
25 28
26 29 class StatsClientBase(object):
27 30 """A Base class for various statsd clients."""
28 31
29 32 def close(self):
30 33 """Used to close and clean up any underlying resources."""
31 34 raise NotImplementedError()
32 35
33 36 def _send(self):
34 37 raise NotImplementedError()
35 38
36 39 def pipeline(self):
37 40 raise NotImplementedError()
38 41
39 42 def timer(self, stat, rate=1, tags=None):
40 43 return Timer(self, stat, rate, tags)
41 44
42 45 def timing(self, stat, delta, rate=1, tags=None):
43 46 """
44 47 Send new timing information.
45 48
46 49 `delta` can be either a number of milliseconds or a timedelta.
47 50 """
48 51 if isinstance(delta, timedelta):
49 52 # Convert timedelta to number of milliseconds.
50 53 delta = delta.total_seconds() * 1000.
51 54 self._send_stat(stat, '%0.6f|ms' % delta, rate, tags)
52 55
53 56 def incr(self, stat, count=1, rate=1, tags=None):
54 57 """Increment a stat by `count`."""
55 58 self._send_stat(stat, '%s|c' % count, rate, tags)
56 59
57 60 def decr(self, stat, count=1, rate=1, tags=None):
58 61 """Decrement a stat by `count`."""
59 62 self.incr(stat, -count, rate, tags)
60 63
61 64 def gauge(self, stat, value, rate=1, delta=False, tags=None):
62 65 """Set a gauge value."""
63 66 if value < 0 and not delta:
64 67 if rate < 1:
65 68 if random.random() > rate:
66 69 return
67 70 with self.pipeline() as pipe:
68 71 pipe._send_stat(stat, '0|g', 1)
69 72 pipe._send_stat(stat, '%s|g' % value, 1)
70 73 else:
71 74 prefix = '+' if delta and value >= 0 else ''
72 75 self._send_stat(stat, '%s%s|g' % (prefix, value), rate, tags)
73 76
74 77 def set(self, stat, value, rate=1):
75 78 """Set a set value."""
76 79 self._send_stat(stat, '%s|s' % value, rate)
77 80
78 81 def _send_stat(self, stat, value, rate, tags=None):
79 82 self._after(self._prepare(stat, value, rate, tags))
80 83
81 84 def _prepare(self, stat, value, rate, tags=None):
82 85 if rate < 1:
83 86 if random.random() > rate:
84 87 return
85 88 value = '%s|@%s' % (value, rate)
86 89
87 90 if self._prefix:
88 91 stat = '%s.%s' % (self._prefix, stat)
89 92
90 93 res = '%s:%s%s' % (
91 94 stat,
92 95 value,
93 96 ("|#" + ",".join(normalize_tags(tags))) if tags else "",
94 97 )
95 98 return res
96 99
97 100 def _after(self, data):
98 101 if data:
99 102 self._send(data)
100 103
101 104
102 105 class PipelineBase(StatsClientBase):
103 106
104 107 def __init__(self, client):
105 108 self._client = client
106 109 self._prefix = client._prefix
107 110 self._stats = deque()
108 111
109 112 def _send(self):
110 113 raise NotImplementedError()
111 114
112 115 def _after(self, data):
113 116 if data is not None:
114 117 self._stats.append(data)
115 118
116 119 def __enter__(self):
117 120 return self
118 121
119 122 def __exit__(self, typ, value, tb):
120 123 self.send()
121 124
122 125 def send(self):
123 126 if not self._stats:
124 127 return
125 128 self._send()
126 129
127 130 def pipeline(self):
128 131 return self.__class__(self)
General Comments 0
You need to be logged in to leave comments. Login now