litespeed.chart.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. # -*- coding: utf-8 -*-
  2. # Description: litespeed netdata python.d module
  3. # Author: Ilya Mashchenko (ilyam8)
  4. # SPDX-License-Identifier: GPL-3.0-or-later
  5. import glob
  6. import os
  7. import re
  8. from collections import namedtuple
  9. from bases.FrameworkServices.SimpleService import SimpleService
  10. update_every = 10
  11. # charts order (can be overridden if you want less charts, or different order)
  12. ORDER = [
  13. 'net_throughput_http', # net throughput
  14. 'net_throughput_https', # net throughput
  15. 'connections_http', # connections
  16. 'connections_https', # connections
  17. 'requests', # requests
  18. 'requests_processing', # requests
  19. 'pub_cache_hits', # cache
  20. 'private_cache_hits', # cache
  21. 'static_hits', # static
  22. ]
  23. CHARTS = {
  24. 'net_throughput_http': {
  25. 'options': [None, 'Network Throughput HTTP', 'kilobits/s', 'net throughput',
  26. 'litespeed.net_throughput', 'area'],
  27. 'lines': [
  28. ['bps_in', 'in', 'absolute'],
  29. ['bps_out', 'out', 'absolute', -1]
  30. ]
  31. },
  32. 'net_throughput_https': {
  33. 'options': [None, 'Network Throughput HTTPS', 'kilobits/s', 'net throughput',
  34. 'litespeed.net_throughput', 'area'],
  35. 'lines': [
  36. ['ssl_bps_in', 'in', 'absolute'],
  37. ['ssl_bps_out', 'out', 'absolute', -1]
  38. ]
  39. },
  40. 'connections_http': {
  41. 'options': [None, 'Connections HTTP', 'conns', 'connections', 'litespeed.connections', 'stacked'],
  42. 'lines': [
  43. ['conn_free', 'free', 'absolute'],
  44. ['conn_used', 'used', 'absolute']
  45. ]
  46. },
  47. 'connections_https': {
  48. 'options': [None, 'Connections HTTPS', 'conns', 'connections', 'litespeed.connections', 'stacked'],
  49. 'lines': [
  50. ['ssl_conn_free', 'free', 'absolute'],
  51. ['ssl_conn_used', 'used', 'absolute']
  52. ]
  53. },
  54. 'requests': {
  55. 'options': [None, 'Requests', 'requests/s', 'requests', 'litespeed.requests', 'line'],
  56. 'lines': [
  57. ['requests', None, 'absolute', 1, 100]
  58. ]
  59. },
  60. 'requests_processing': {
  61. 'options': [None, 'Requests In Processing', 'requests', 'requests', 'litespeed.requests_processing', 'line'],
  62. 'lines': [
  63. ['requests_processing', 'processing', 'absolute']
  64. ]
  65. },
  66. 'pub_cache_hits': {
  67. 'options': [None, 'Public Cache Hits', 'hits/s', 'cache', 'litespeed.cache', 'line'],
  68. 'lines': [
  69. ['pub_cache_hits', 'hits', 'absolute', 1, 100]
  70. ]
  71. },
  72. 'private_cache_hits': {
  73. 'options': [None, 'Private Cache Hits', 'hits/s', 'cache', 'litespeed.cache', 'line'],
  74. 'lines': [
  75. ['private_cache_hits', 'hits', 'absolute', 1, 100]
  76. ]
  77. },
  78. 'static_hits': {
  79. 'options': [None, 'Static Hits', 'hits/s', 'static', 'litespeed.static', 'line'],
  80. 'lines': [
  81. ['static_hits', 'hits', 'absolute', 1, 100]
  82. ]
  83. }
  84. }
  85. t = namedtuple('T', ['key', 'id', 'mul'])
  86. T = [
  87. t('BPS_IN', 'bps_in', 8),
  88. t('BPS_OUT', 'bps_out', 8),
  89. t('SSL_BPS_IN', 'ssl_bps_in', 8),
  90. t('SSL_BPS_OUT', 'ssl_bps_out', 8),
  91. t('REQ_PER_SEC', 'requests', 100),
  92. t('REQ_PROCESSING', 'requests_processing', 1),
  93. t('PUB_CACHE_HITS_PER_SEC', 'pub_cache_hits', 100),
  94. t('PRIVATE_CACHE_HITS_PER_SEC', 'private_cache_hits', 100),
  95. t('STATIC_HITS_PER_SEC', 'static_hits', 100),
  96. t('PLAINCONN', 'conn_used', 1),
  97. t('AVAILCONN', 'conn_free', 1),
  98. t('SSLCONN', 'ssl_conn_used', 1),
  99. t('AVAILSSL', 'ssl_conn_free', 1),
  100. ]
  101. RE = re.compile(r'([A-Z_]+): ([0-9.]+)')
  102. ZERO_DATA = {
  103. 'bps_in': 0,
  104. 'bps_out': 0,
  105. 'ssl_bps_in': 0,
  106. 'ssl_bps_out': 0,
  107. 'requests': 0,
  108. 'requests_processing': 0,
  109. 'pub_cache_hits': 0,
  110. 'private_cache_hits': 0,
  111. 'static_hits': 0,
  112. 'conn_used': 0,
  113. 'conn_free': 0,
  114. 'ssl_conn_used': 0,
  115. 'ssl_conn_free': 0,
  116. }
  117. class Service(SimpleService):
  118. def __init__(self, configuration=None, name=None):
  119. SimpleService.__init__(self, configuration=configuration, name=name)
  120. self.order = ORDER
  121. self.definitions = CHARTS
  122. self.path = self.configuration.get('path', '/tmp/lshttpd/')
  123. self.files = list()
  124. def check(self):
  125. if not self.path:
  126. self.error('"path" not specified')
  127. return False
  128. fs = glob.glob(os.path.join(self.path, '.rtreport*'))
  129. if not fs:
  130. self.error('"{0}" has no "rtreport" files or dir is not readable'.format(self.path))
  131. return None
  132. self.debug('stats files:', fs)
  133. for f in fs:
  134. if not is_readable_file(f):
  135. self.error('{0} is not readable'.format(f))
  136. continue
  137. self.files.append(f)
  138. return bool(self.files)
  139. def get_data(self):
  140. """
  141. Format data received from http request
  142. :return: dict
  143. """
  144. data = dict(ZERO_DATA)
  145. for f in self.files:
  146. try:
  147. with open(f) as b:
  148. lines = b.readlines()
  149. except (OSError, IOError) as err:
  150. self.error(err)
  151. return None
  152. else:
  153. parse_file(data, lines)
  154. return data
  155. def parse_file(data, lines):
  156. for line in lines:
  157. if not line.startswith(('BPS_IN:', 'MAXCONN:', 'PLAINCONN:', 'REQ_RATE []:')):
  158. continue
  159. m = dict(RE.findall(line))
  160. for v in T:
  161. if v.key in m:
  162. data[v.id] += float(m[v.key]) * v.mul
  163. def is_readable_file(v):
  164. return os.path.isfile(v) and os.access(v, os.R_OK)