traefik.chart.py 6.6 KB

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