master
py 198 lines 6.57 KB
Raw
1 # -*- coding: utf-8 -*-
2 # Description: traefik netdata python.d module
3 # Author: Alexandre Menezes (@ale_menezes)
4 # SPDX-License-Identifier: GPL-3.0-or-later
5
6 from collections import defaultdict
7 from json import loads
8
9 from bases.FrameworkServices.UrlService import UrlService
10
11 ORDER = [
12 'response_statuses',
13 'response_codes',
14 'detailed_response_codes',
15 'requests',
16 'total_response_time',
17 'average_response_time',
18 'average_response_time_per_iteration',
19 'uptime'
20 ]
21
22 CHARTS = {
23 'response_statuses': {
24 'options': [None, 'Response statuses', 'requests/s', 'responses', 'traefik.response_statuses', 'stacked'],
25 'lines': [
26 ['successful_requests', 'success', 'incremental'],
27 ['server_errors', 'error', 'incremental'],
28 ['redirects', 'redirect', 'incremental'],
29 ['bad_requests', 'bad', 'incremental'],
30 ['other_requests', 'other', 'incremental']
31 ]
32 },
33 'response_codes': {
34 'options': [None, 'Responses by codes', 'requests/s', 'responses', 'traefik.response_codes', 'stacked'],
35 'lines': [
36 ['2xx', None, 'incremental'],
37 ['5xx', None, 'incremental'],
38 ['3xx', None, 'incremental'],
39 ['4xx', None, 'incremental'],
40 ['1xx', None, 'incremental'],
41 ['other', None, 'incremental']
42 ]
43 },
44 'detailed_response_codes': {
45 'options': [None, 'Detailed response codes', 'requests/s', 'responses', 'traefik.detailed_response_codes',
46 'stacked'],
47 'lines': []
48 },
49 'requests': {
50 'options': [None, 'Requests', 'requests/s', 'requests', 'traefik.requests', 'line'],
51 'lines': [
52 ['total_count', 'requests', 'incremental']
53 ]
54 },
55 'total_response_time': {
56 'options': [None, 'Total response time', 'seconds', 'timings', 'traefik.total_response_time', 'line'],
57 'lines': [
58 ['total_response_time_sec', 'response', 'absolute', 1, 10000]
59 ]
60 },
61 'average_response_time': {
62 'options': [None, 'Average response time', 'milliseconds', 'timings', 'traefik.average_response_time', 'line'],
63 'lines': [
64 ['average_response_time_sec', 'response', 'absolute', 1, 1000]
65 ]
66 },
67 'average_response_time_per_iteration': {
68 'options': [None, 'Average response time per iteration', 'milliseconds', 'timings',
69 'traefik.average_response_time_per_iteration', 'line'],
70 'lines': [
71 ['average_response_time_per_iteration_sec', 'response', 'incremental', 1, 10000]
72 ]
73 },
74 'uptime': {
75 'options': [None, 'Uptime', 'seconds', 'uptime', 'traefik.uptime', 'line'],
76 'lines': [
77 ['uptime_sec', 'uptime', 'absolute']
78 ]
79 }
80 }
81
82 HEALTH_STATS = [
83 'uptime_sec',
84 'average_response_time_sec',
85 'total_response_time_sec',
86 'total_count',
87 'total_status_code_count'
88 ]
89
90
91 class Service(UrlService):
92 def __init__(self, configuration=None, name=None):
93 UrlService.__init__(self, configuration=configuration, name=name)
94 self.url = self.configuration.get('url', 'http://localhost:8080/health')
95 self.order = ORDER
96 self.definitions = CHARTS
97 self.last_total_response_time = 0
98 self.last_total_count = 0
99 self.data = {
100 'successful_requests': 0,
101 'redirects': 0,
102 'bad_requests': 0,
103 'server_errors': 0,
104 'other_requests': 0,
105 '1xx': 0,
106 '2xx': 0,
107 '3xx': 0,
108 '4xx': 0,
109 '5xx': 0,
110 'other': 0,
111 'average_response_time_per_iteration_sec': 0,
112 }
113
114 def _get_data(self):
115 data = self._get_raw_data()
116
117 if not data:
118 return None
119
120 data = loads(data)
121
122 self.get_data_per_code_status(raw_data=data)
123
124 self.get_data_per_code_family(raw_data=data)
125
126 self.get_data_per_code(raw_data=data)
127
128 self.data.update(fetch_data_(raw_data=data, metrics=HEALTH_STATS))
129
130 self.data['average_response_time_sec'] *= 1000000
131 self.data['total_response_time_sec'] *= 10000
132 if data['total_count'] != self.last_total_count:
133 self.data['average_response_time_per_iteration_sec'] = \
134 (data['total_response_time_sec'] - self.last_total_response_time) * \
135 1000000 / (data['total_count'] - self.last_total_count)
136 else:
137 self.data['average_response_time_per_iteration_sec'] = 0
138 self.last_total_response_time = data['total_response_time_sec']
139 self.last_total_count = data['total_count']
140
141 return self.data or None
142
143 def get_data_per_code_status(self, raw_data):
144 data = defaultdict(int)
145 for code, value in raw_data['total_status_code_count'].items():
146 code_prefix = code[0]
147 if code_prefix == '1' or code_prefix == '2' or code == '304':
148 data['successful_requests'] += value
149 elif code_prefix == '3':
150 data['redirects'] += value
151 elif code_prefix == '4':
152 data['bad_requests'] += value
153 elif code_prefix == '5':
154 data['server_errors'] += value
155 else:
156 data['other_requests'] += value
157 self.data.update(data)
158
159 def get_data_per_code_family(self, raw_data):
160 data = defaultdict(int)
161 for code, value in raw_data['total_status_code_count'].items():
162 code_prefix = code[0]
163 if code_prefix == '1':
164 data['1xx'] += value
165 elif code_prefix == '2':
166 data['2xx'] += value
167 elif code_prefix == '3':
168 data['3xx'] += value
169 elif code_prefix == '4':
170 data['4xx'] += value
171 elif code_prefix == '5':
172 data['5xx'] += value
173 else:
174 data['other'] += value
175 self.data.update(data)
176
177 def get_data_per_code(self, raw_data):
178 for code, value in raw_data['total_status_code_count'].items():
179 if self.charts:
180 if code not in self.data:
181 self.charts['detailed_response_codes'].add_dimension([code, code, 'incremental'])
182 self.data[code] = value
183
184
185 def fetch_data_(raw_data, metrics):
186 data = dict()
187
188 for metric in metrics:
189 value = raw_data
190 metrics_list = metric.split('.')
191 try:
192 for m in metrics_list:
193 value = value[m]
194 except KeyError:
195 continue
196 data['_'.join(metrics_list)] = value
197
198 return data