forked from xrmx/uwsgitop
-
Notifications
You must be signed in to change notification settings - Fork 3
/
uwsgimon
executable file
·196 lines (159 loc) · 6.2 KB
/
uwsgimon
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
191
192
193
194
195
196
#!/usr/bin/env python
# coding: utf-8
import argparse
import errno
import os
try:
import simplejson as json
except ImportError:
import json
import socket
import sys
import time
from collections import defaultdict
def inet_addr(arg):
sfamily = socket.AF_INET
host, port = arg.rsplit(':', 1)
addr = (host, int(port))
return sfamily, addr, host
def unix_addr(arg):
sfamily = socket.AF_UNIX
addr = arg
return sfamily, addr, socket.gethostname()
def abstract_unix_addr(arg):
sfamily = socket.AF_UNIX
addr = '\0' + arg[1:]
return sfamily, addr, socket.gethostname()
def udp_send(dest, data):
host, port = dest.replace('udp://', '').split(':')
s = socket.socket(type=socket.SOCK_DGRAM)
try:
s.setblocking(0)
s.sendto(bytearray(data, 'utf-8'), (host, int(port)))
finally:
s.close()
def run(stats, node, role, freq, f, udp=None):
if ':' in stats:
sfamily, addr, host = inet_addr(stats)
elif stats.startswith('@'):
sfamily, addr, host = abstract_unix_addr(stats)
else:
sfamily, addr, host = unix_addr(stats)
# RPS calculation
first = True
last_ts = time.time()
last_req = defaultdict(int)
while True:
js = ''
s = socket.socket(sfamily, socket.SOCK_STREAM)
try:
s.connect(addr)
while True:
data = s.recv(4096)
if len(data) < 1:
break
js += data.decode('utf8', 'ignore')
except IOError as e:
if e.errno != errno.EINTR:
raise
continue
except:
raise Exception("unable to get uWSGI statistics")
finally:
s.close()
try:
dd = json.loads(js)
except json.JSONDecodeError:
continue
if 'workers' not in dd:
raise Exception("unable to get uWSGI statistics")
workers = dd['workers']
result = dict()
result['ver'] = dd.get('version', 'unknown')
result['node'] = node
result['role'] = role
result['req'] = sum([w['requests'] for w in workers])
_sum_avg = _sum_rss = _sum_vsz = 0
dt = time.time() - last_ts
for w in workers:
# calculate rps
w['rps'] = int(round((w['requests'] - last_req[w['id']]) / dt))
last_req[w['id']] = w['requests']
# sum to-be-averaged metrics
_sum_avg += w['avg_rt']
_sum_rss += w['rss']
_sum_vsz += w['vsz']
last_ts = time.time()
# after reloading, rps maybe a negative number, set it to 0
result['rps'] = max(0 if first else sum([w['rps'] for w in workers]), 0)
first = False
result['avg'] = round(_sum_avg / float(len(workers)) / 1000.0)
result['lq'] = int(dd.get('listen_queue', 0))
result['tx'] = sum([w['tx'] for w in workers])
_status = [w['status'] for w in workers]
result['busy'] = _status.count('busy')
result['idle'] = len(_status) - result['busy']
result['rss'] = int(_sum_rss / float(len(workers)))
result['vsz'] = int(_sum_vsz / float(len(workers)))
line = '{}\n'.format(f.strip().format(**result))
if udp:
udp_send(udp, line)
else:
sys.stdout.write(line)
sys.stdout.flush()
time.sleep(freq)
def main():
DEFAULT_NODE = socket.gethostname()
DEFAULT_ROLE = 'uwsgi'
DEFAULT_FREQ = 5
DEFAULT_FORMAT = 'uwsgi,node={node},role={role} req={req}i,rps={rps}i,avg={avg},lq={lq}i,busy={busy}i,idle={idle}i,rss={rss}i'
DEFAULT_UDP = None
DEFAULT_FOREVER = False
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('--node', '-n', dest='node', default=None, help='uWSGI node name, default: "{}"'.format(DEFAULT_NODE))
parser.add_argument('--role', '-r', dest='role', default=None, help='uWSGI role name, default: "{}"'.format(DEFAULT_ROLE))
parser.add_argument('--frequency', '-q', dest='freq', default=None, type=float, help='uWSGI stats refresh frequency in seconds, default: {}'.format(DEFAULT_FREQ))
parser.add_argument('--format', '-f', dest='format', default=None,
help='''output format, available variables:
ver - uWSGI version
node - uWSGI node name
role - uWSGI role name
req - total requests
rps - requests per second
avg - average response time
lq - listen queue
busy - busy workers count
idle - idle workers count
rss - average RSS (Resident Set Size)
vsz - average VSZ (Virtual Memory Size)
e.g. "{}"'''.format(DEFAULT_FORMAT))
parser.add_argument('--udp', '-u', dest='udp', default=None, help='output to UDP destination ("[udp://]<host>:<port>"), instead of stdout, default: {}'.format(DEFAULT_UDP))
parser.add_argument('--forever', dest='forever', default=None, action='store_const', const=True, help='ignore all exceptions and run forever, default: {}'.format(DEFAULT_FOREVER))
parser.add_argument('stats', help='uWSGI stats address')
args = parser.parse_args()
node = args.node or os.getenv('UWSGIMON_NODE') or DEFAULT_NODE
role = args.role or os.getenv('UWSGIMON_ROLE') or DEFAULT_ROLE
freq = float(args.freq or os.getenv('UWSGIMON_FREQ') or DEFAULT_FREQ)
format_ = args.format or os.getenv('UWSGIMON_FORMAT') or DEFAULT_FORMAT
udp = args.udp or os.getenv('UWSGIMON_UDP') or DEFAULT_UDP
forever = args.forever is not None or os.getenv('UWSGIMON_FOREVER') is not None or DEFAULT_FOREVER
if forever:
while True:
try:
run(stats=args.stats, node=node, role=role, freq=freq, f=format_, udp=udp)
except Exception as e:
sys.stderr.write('forever: {}\n'.format(e))
sys.stderr.flush()
time.sleep(freq)
continue
else:
try:
run(stats=args.stats, node=node, role=role, freq=freq, f=format_, udp=udp)
except IOError as e:
if e.errno == errno.EPIPE:
sys.exit(0)
raise
import signal
signal.signal(signal.SIGINT, lambda signal, frame: sys.exit(130))
if __name__ == '__main__':
main()