-
Notifications
You must be signed in to change notification settings - Fork 601
/
Copy pathhttpd.py
170 lines (138 loc) · 5.8 KB
/
httpd.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
# coding=utf-8
"""
Collect stats from Apache HTTPD server using mod_status
#### Dependencies
* mod_status
* httplib
* urlparse
"""
import re
import httplib
import urlparse
import diamond.collector
class HttpdCollector(diamond.collector.Collector):
def process_config(self):
super(HttpdCollector, self).process_config()
if 'url' in self.config:
self.config['urls'].append(self.config['url'])
self.urls = {}
if isinstance(self.config['urls'], basestring):
self.config['urls'] = self.config['urls'].split(',')
for url in self.config['urls']:
# Handle the case where there is a trailing comman on the urls list
if len(url) == 0:
continue
if ' ' in url:
parts = url.split(' ')
self.urls[parts[0]] = parts[1]
else:
self.urls[''] = url
def get_default_config_help(self):
config_help = super(HttpdCollector, self).get_default_config_help()
config_help.update({
'urls': "Urls to server-status in auto format, comma seperated," +
" Format 'nickname http://host:port/server-status?auto, " +
", nickname http://host:port/server-status?auto, etc'",
'redirects': "The maximum number of redirect requests to follow.",
})
return config_help
def get_default_config(self):
"""
Returns the default collector settings
"""
config = super(HttpdCollector, self).get_default_config()
config.update({
'path': 'httpd',
'urls': ['localhost http://localhost:8080/server-status?auto'],
'redirects': 5,
})
return config
def collect(self):
for nickname in self.urls.keys():
url = self.urls[nickname]
try:
redirects = 0
while True:
# Parse Url
parts = urlparse.urlparse(url)
# Set httplib class
if parts.scheme == 'http':
connection = httplib.HTTPConnection(parts.netloc)
elif parts.scheme == 'https':
connection = httplib.HTTPSConnection(parts.netloc)
else:
raise Exception("Invalid scheme: %s" % parts.scheme)
# Setup Connection
url = "%s?%s" % (parts.path, parts.query)
connection.request("GET", url)
response = connection.getresponse()
data = response.read()
headers = dict(response.getheaders())
if (('location' not in headers or
headers['location'] == url)):
connection.close()
break
url = headers['location']
connection.close()
redirects += 1
if redirects > self.config['redirects']:
raise Exception("Too many redirects!")
except Exception, e:
self.log.error(
"Error retrieving HTTPD stats for '%s': %s",
url, e)
continue
exp = re.compile('^([A-Za-z ]+):\s+(.+)$')
for line in data.split('\n'):
if line:
m = exp.match(line)
if m:
k = m.group(1)
v = m.group(2)
# IdleWorkers gets determined from the scoreboard
if k == 'IdleWorkers':
continue
if k == 'Scoreboard':
for sb_kv in self._parseScoreboard(v):
self._publish(nickname, sb_kv[0], sb_kv[1])
else:
self._publish(nickname, k, v)
def _publish(self, nickname, key, value):
metrics = ['ReqPerSec', 'BytesPerSec', 'BytesPerReq', 'BusyWorkers',
'Total Accesses', 'IdleWorkers', 'StartingWorkers',
'ReadingWorkers', 'WritingWorkers', 'KeepaliveWorkers',
'DnsWorkers', 'ClosingWorkers', 'LoggingWorkers',
'FinishingWorkers', 'CleanupWorkers']
metrics_precision = ['ReqPerSec', 'BytesPerSec', 'BytesPerReq']
if key in metrics:
# Get Metric Name
presicion_metric = False
metric_name = "%s" % re.sub('\s+', '', key)
if metric_name in metrics_precision:
presicion_metric = 1
# Prefix with the nickname?
if len(nickname) > 0:
metric_name = nickname + '.' + metric_name
# Use precision for ReqPerSec BytesPerSec BytesPerReq
if presicion_metric:
# Get Metric Value
metric_value = "%f" % float(value)
# Publish Metric
self.publish(metric_name, metric_value, precision=5)
else:
# Get Metric Value
metric_value = "%d" % float(value)
# Publish Metric
self.publish(metric_name, metric_value)
def _parseScoreboard(self, sb):
ret = []
ret.append(('IdleWorkers', sb.count('_')))
ret.append(('ReadingWorkers', sb.count('R')))
ret.append(('WritingWorkers', sb.count('W')))
ret.append(('KeepaliveWorkers', sb.count('K')))
ret.append(('DnsWorkers', sb.count('D')))
ret.append(('ClosingWorkers', sb.count('C')))
ret.append(('LoggingWorkers', sb.count('L')))
ret.append(('FinishingWorkers', sb.count('G')))
ret.append(('CleanupWorkers', sb.count('I')))
return ret