-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathstatsd.py
More file actions
190 lines (150 loc) · 6.14 KB
/
Copy pathstatsd.py
File metadata and controls
190 lines (150 loc) · 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# -*- coding: utf-8 -*-
#
# This file is part of python-statsd-client released under the Apache
# License, Version 2.0. See the NOTICE for more information.
import random
from functools import wraps
from socket import socket, AF_INET, SOCK_DGRAM
import time
import logging
__version__ = '1.0.7'
STATSD_HOST = 'localhost'
STATSD_PORT = 8125
STATSD_SAMPLE_RATE = None
STATSD_BUCKET_PREFIX = None
def decrement(bucket, delta=1, sample_rate=None):
_statsd.decr(bucket, delta, sample_rate)
def increment(bucket, delta=1, sample_rate=None):
_statsd.incr(bucket, delta, sample_rate)
def gauge(bucket, value, sample_rate=None):
_statsd.gauge(bucket, value, sample_rate)
def timing(bucket, ms, sample_rate=None):
_statsd.timing(bucket, ms, sample_rate)
class StatsdClient(object):
def __init__(self, host=None, port=None, prefix=None, sample_rate=None):
self._host = host or STATSD_HOST
self._port = port or STATSD_PORT
self._sample_rate = sample_rate or STATSD_SAMPLE_RATE
self._socket = socket(AF_INET, SOCK_DGRAM)
self._prefix = prefix or STATSD_BUCKET_PREFIX
if self._prefix and not isinstance(self._prefix, bytes):
self._prefix = self._prefix.encode('utf8')
def __del__(self):
self._socket.close()
def decr(self, bucket, delta=1, sample_rate=None):
"""Decrements a counter by delta.
"""
value = str(-1 * delta).encode('utf8') + b'|c'
self._send(bucket, value, sample_rate)
def incr(self, bucket, delta=1, sample_rate=None):
"""Increment a counter by delta.
"""
value = str(delta).encode('utf8') + b'|c'
self._send(bucket, value, sample_rate)
def gauge(self, bucket, value, sample_rate=None):
"""Send a gauge value.
"""
str_value = str(value).encode('utf8') + b'|g'
self._send(bucket, str_value, sample_rate)
def _send(self, bucket, value, sample_rate=None):
"""Format and send data to statsd.
"""
try:
bucket = bucket if isinstance(bucket, bytes) else bucket.encode('utf8')
sample_rate = sample_rate or self._sample_rate
if sample_rate and sample_rate < 1.0 and sample_rate > 0:
if random.random() <= sample_rate:
value = value + b'|@' + str(sample_rate).encode('utf8')
else:
return
stat = bucket + b':' + value
if self._prefix:
stat = self._prefix + b'.' + stat
self._socket.sendto(stat, (self._host, self._port))
except Exception as e:
_logger.error("Failed to send statsd packet.", exc_info=True)
def timing(self, bucket, ms, sample_rate=None):
"""Creates a timing sample.
"""
value = str(ms).encode('utf8') + b'|ms'
self._send(bucket, value, sample_rate)
class StatsdCounter(object):
"""Counter for StatsD.
"""
def __init__(self, bucket, host=None, port=None, prefix=None,
sample_rate=None):
self._client = StatsdClient(host=host, port=port, prefix=prefix,
sample_rate=sample_rate)
self._bucket = bucket if isinstance(bucket, bytes) else bucket.encode('utf8')
def __add__(self, num):
self._client.incr(self._bucket, delta=num)
return self
def __sub__(self, num):
self._client.decr(self._bucket, delta=num)
return self
class StatsdTimer(object):
"""Timer for StatsD.
"""
def __init__(self, bucket, host=None, port=None, prefix=None,
sample_rate=None):
self._client = StatsdClient(host=host, port=port, prefix=prefix,
sample_rate=sample_rate)
self._bucket = bucket if isinstance(bucket, bytes) else bucket.encode('utf8')
def __enter__(self):
self.start()
return self
def __exit__(self, type, value, traceback):
if type is not None:
self.stop(b'total-except')
else:
self.stop()
def start(self, bucket_key=b'start'):
"""Start the timer.
"""
bucket_key = bucket_key if isinstance(bucket_key, bytes) else bucket_key.encode('utf8')
self._start = time.time() * 1000
self._splits = [(bucket_key, self._start), ]
def split(self, bucket_key):
"""Records time since start() or last call to split() and sends
result to statsd.
"""
bucket_key = bucket_key if isinstance(bucket_key, bytes) else bucket_key.encode('utf8')
self._splits.append((bucket_key, time.time() * 1000))
self._client.timing(self._bucket + b'.' + bucket_key,
self._splits[-1][1] - self._splits[-2][1])
def stop(self, bucket_key=b'total'):
"""Stops the timer and sends total time to statsd.
"""
bucket_key = bucket_key if isinstance(bucket_key, bytes) else bucket_key.encode('utf8')
self._stop = time.time() * 1000
self._client.timing(self._bucket + b'.' + bucket_key,
self._stop - self._start)
@staticmethod
def wrap(bucket):
def wrapper(func):
@wraps(func)
def f(*args, **kw):
with StatsdTimer(bucket):
return func(*args, **kw)
return f
return wrapper
def init_statsd(settings=None):
"""Initialize global statsd client.
"""
global _statsd
global STATSD_HOST
global STATSD_PORT
global STATSD_SAMPLE_RATE
global STATSD_BUCKET_PREFIX
if settings:
STATSD_HOST = settings.get('STATSD_HOST', STATSD_HOST)
STATSD_PORT = settings.get('STATSD_PORT', STATSD_PORT)
STATSD_SAMPLE_RATE = settings.get('STATSD_SAMPLE_RATE',
STATSD_SAMPLE_RATE)
STATSD_BUCKET_PREFIX = settings.get('STATSD_BUCKET_PREFIX',
STATSD_BUCKET_PREFIX)
_statsd = StatsdClient(host=STATSD_HOST, port=STATSD_PORT,
sample_rate=STATSD_SAMPLE_RATE, prefix=STATSD_BUCKET_PREFIX)
return _statsd
_logger = logging.getLogger('statsd')
_statsd = init_statsd()