-
Notifications
You must be signed in to change notification settings - Fork 84
/
Copy pathcounter.py
176 lines (141 loc) · 4.57 KB
/
counter.py
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
# -*- coding:utf-8 -*-
'''
在Python应用里发送业务指标到自定义监控
- 对每分钟请求数之类的指标用`c.increment()`
- 对平均响应时间之类的指标用`c.avg_increment()`
- 对当前在线人数之类的指标用`c.setraw(1024)`
使用方法如下
# 定义计数器,启动发送线程
cm = CounterManager(api_key='test_key', host='test_host')
c = Counter('requests_count', cm)
cm.start()
# 在收到请求时递增计数器
c.increment()
'''
try:
from gevent import monkey
monkey.patch_time()
monkey.patch_thread()
monkey.patch_socket()
except ImportError:
pass
import time
import socket
import threading
import logging
class CounterManager(threading.Thread):
def __init__(self, api_key, host=socket.gethostname(), ip='127.0.0.1',
interval=60, collecter_url=('collector.monitor.dnspod.cn', 2003)):
threading.Thread.__init__(self)
self.setName('CounterManager:%s' % api_key)
self.setDaemon(True)
self.api_key = api_key
self.host = self._replace_valid_char(host)
self.ip = self._replace_valid_char(ip)
self.interval = interval
self.collecter_url = collecter_url
self.counters = []
self.client = socket.socket()
self.client.connect(collecter_url)
def _replace_valid_char(self, input):
input = input.replace('/', '_')
input = input.replace(' ', '_')
return input
def add_counter(self, counter):
logging.info('add counter:%s', counter)
self.counters.append(counter)
counter.api_key = self.api_key
counter.host = self.host
counter.ip = self.ip
def send_data_to_center(self, counter):
to_send_data = counter._get_data()
if to_send_data:
logging.info('send_data_to_center:%s', to_send_data)
self.client.send(to_send_data)
def run(self):
logging.info('counter_manager start:%s', self.api_key)
while True:
try:
for counter in self.counters:
self.send_data_to_center(counter)
except:
logging.exception('send_data_to_center error')
try:
self.client.connect(self.collecter_url)
except:
logging.exception('auto reconnect error')
finally:
time.sleep(self.interval)
def __del__(self):
try:
self.client.close()
except:
pass
class Counter(object):
'计数器'
def __init__(self, metric_name, cm, auto_clear=1):
'''创建一个计数器
metric_name是计数器名称
auto_clear表示,是否发送给collecter后清零计数器,默认是清零
'''
self.metric_name = metric_name
self.value = 0
self.has_data = False
self.auto_clear = auto_clear
self.count = 0
cm.add_counter(self)
def _pre_set_value(self):
self.count += 1
self.has_data = True
def increment(self, value=1):
self._pre_set_value()
self.value += value
def decrement(self, value=1):
self._pre_set_value()
self.value -= value
def avg_increment(self, value=1):
self._pre_set_value()
self.value = (self.value + value) / self.count
def setraw(self, value=1):
self._pre_set_value()
self.value = value
def reset(self):
self.value = 0
self.count = 0
self.has_data = False
def _get_data(self):
if not self.has_data:
return None
data = '%(api_key)s/%(host)s/%(ip)s/%(metric_name)s %(value)s %(timestamp)s\n'
self.timestamp = int(time.time())
data = data % self.__dict__
if self.auto_clear:
self.reset()
return data
def __str__(self):
return self.metric_name
def test_default():
'''
pip install nosetests
pip install mock
nosetests counter.py --nologcapture -s
'''
from mock import Mock
globals()['socket'] = Mock()
time.time = Mock(return_value=123456)
logging.basicConfig(level=logging.NOTSET)
cm = CounterManager(api_key='test_key', host='test_host')
c = Counter('test', cm)
c.increment()
assert c.value == 1
c.decrement()
assert c.value == 0
c.reset()
c.avg_increment(2)
c.avg_increment(4)
assert c.value == 3
c.setraw(5)
assert c.value == 5
cm.send_data_to_center(c)
expect = 'test_key/test_host/127.0.0.1/test 5 123456'
cm.client.send.assert_called_with(expect)