springboot.chart.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. # -*- coding: utf-8 -*-
  2. # Description: tomcat netdata python.d module
  3. # Author: Wing924
  4. # SPDX-License-Identifier: GPL-3.0-or-later
  5. import json
  6. from bases.FrameworkServices.UrlService import UrlService
  7. DEFAULT_ORDER = [
  8. 'response_code',
  9. 'threads',
  10. 'gc_time',
  11. 'gc_ope',
  12. 'heap',
  13. ]
  14. DEFAULT_CHARTS = {
  15. 'response_code': {
  16. 'options': [None, "Response Codes", "requests/s", "response", "springboot.response_code", "stacked"],
  17. 'lines': [
  18. ["resp_other", 'Other', 'incremental'],
  19. ["resp_1xx", '1xx', 'incremental'],
  20. ["resp_2xx", '2xx', 'incremental'],
  21. ["resp_3xx", '3xx', 'incremental'],
  22. ["resp_4xx", '4xx', 'incremental'],
  23. ["resp_5xx", '5xx', 'incremental'],
  24. ]
  25. },
  26. 'threads': {
  27. 'options': [None, "Threads", "current threads", "threads", "springboot.threads", "area"],
  28. 'lines': [
  29. ["threads_daemon", 'daemon', 'absolute'],
  30. ["threads", 'total', 'absolute'],
  31. ]
  32. },
  33. 'gc_time': {
  34. 'options': [None, "GC Time", "milliseconds", "garbage collection", "springboot.gc_time", "stacked"],
  35. 'lines': [
  36. ["gc_copy_time", 'Copy', 'incremental'],
  37. ["gc_marksweepcompact_time", 'MarkSweepCompact', 'incremental'],
  38. ["gc_parnew_time", 'ParNew', 'incremental'],
  39. ["gc_concurrentmarksweep_time", 'ConcurrentMarkSweep', 'incremental'],
  40. ["gc_ps_scavenge_time", 'PS Scavenge', 'incremental'],
  41. ["gc_ps_marksweep_time", 'PS MarkSweep', 'incremental'],
  42. ["gc_g1_young_generation_time", 'G1 Young Generation', 'incremental'],
  43. ["gc_g1_old_generation_time", 'G1 Old Generation', 'incremental'],
  44. ]
  45. },
  46. 'gc_ope': {
  47. 'options': [None, "GC Operations", "operations/s", "garbage collection", "springboot.gc_ope", "stacked"],
  48. 'lines': [
  49. ["gc_copy_count", 'Copy', 'incremental'],
  50. ["gc_marksweepcompact_count", 'MarkSweepCompact', 'incremental'],
  51. ["gc_parnew_count", 'ParNew', 'incremental'],
  52. ["gc_concurrentmarksweep_count", 'ConcurrentMarkSweep', 'incremental'],
  53. ["gc_ps_scavenge_count", 'PS Scavenge', 'incremental'],
  54. ["gc_ps_marksweep_count", 'PS MarkSweep', 'incremental'],
  55. ["gc_g1_young_generation_count", 'G1 Young Generation', 'incremental'],
  56. ["gc_g1_old_generation_count", 'G1 Old Generation', 'incremental'],
  57. ]
  58. },
  59. 'heap': {
  60. 'options': [None, "Heap Memory Usage", "KiB", "heap memory", "springboot.heap", "area"],
  61. 'lines': [
  62. ["heap_committed", 'committed', "absolute"],
  63. ["heap_used", 'used', "absolute"],
  64. ]
  65. }
  66. }
  67. class ExtraChartError(ValueError):
  68. pass
  69. class Service(UrlService):
  70. def __init__(self, configuration=None, name=None):
  71. UrlService.__init__(self, configuration=configuration, name=name)
  72. self.url = self.configuration.get('url', "http://localhost:8080/metrics")
  73. self._setup_charts()
  74. def _get_data(self):
  75. """
  76. Format data received from http request
  77. :return: dict
  78. """
  79. raw_data = self._get_raw_data()
  80. if not raw_data:
  81. return None
  82. try:
  83. data = json.loads(raw_data)
  84. except ValueError:
  85. self.debug('%s is not a valid JSON page' % self.url)
  86. return None
  87. result = {
  88. 'resp_1xx': 0,
  89. 'resp_2xx': 0,
  90. 'resp_3xx': 0,
  91. 'resp_4xx': 0,
  92. 'resp_5xx': 0,
  93. 'resp_other': 0,
  94. }
  95. for key, value in data.iteritems():
  96. if 'counter.status.' in key:
  97. status_type = key[15:16] + 'xx'
  98. if status_type[0] not in '12345':
  99. status_type = 'other'
  100. result['resp_' + status_type] += value
  101. else:
  102. result[key.replace('.', '_')] = value
  103. return result or None
  104. def _setup_charts(self):
  105. self.order = []
  106. self.definitions = {}
  107. defaults = self.configuration.get('defaults', {})
  108. for chart in DEFAULT_ORDER:
  109. if defaults.get(chart, True):
  110. self.order.append(chart)
  111. self.definitions[chart] = DEFAULT_CHARTS[chart]
  112. for extra in self.configuration.get('extras', []):
  113. self._add_extra_chart(extra)
  114. self.order.append(extra['id'])
  115. def _add_extra_chart(self, chart):
  116. chart_id = chart.get('id', None) or self.die('id is not defined in extra chart')
  117. options = chart.get('options', None) or self.die('option is not defined in extra chart: %s' % chart_id)
  118. lines = chart.get('lines', None) or self.die('lines is not defined in extra chart: %s' % chart_id)
  119. title = options.get('title', None) or self.die('title is missing: %s' % chart_id)
  120. units = options.get('units', None) or self.die('units is missing: %s' % chart_id)
  121. family = options.get('family', title)
  122. context = options.get('context', 'springboot.' + title)
  123. charttype = options.get('charttype', 'line')
  124. result = {
  125. 'options': [None, title, units, family, context, charttype],
  126. 'lines': [],
  127. }
  128. for line in lines:
  129. dimension = line.get('dimension', None) or self.die('dimension is missing: %s' % chart_id)
  130. name = line.get('name', dimension)
  131. algorithm = line.get('algorithm', 'absolute')
  132. multiplier = line.get('multiplier', 1)
  133. divisor = line.get('divisor', 1)
  134. result['lines'].append([dimension, name, algorithm, multiplier, divisor])
  135. self.definitions[chart_id] = result
  136. @staticmethod
  137. def die(error_message):
  138. raise ExtraChartError(error_message)