memcached.chart.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. # -*- coding: utf-8 -*-
  2. # Description: memcached netdata python.d module
  3. # Author: Pawel Krupa (paulfantom)
  4. from bases.FrameworkServices.SocketService import SocketService
  5. # default module values (can be overridden per job in `config`)
  6. #update_every = 2
  7. priority = 60000
  8. retries = 60
  9. # default job configuration (overridden by python.d.plugin)
  10. # config = {'local': {
  11. # 'update_every': update_every,
  12. # 'retries': retries,
  13. # 'priority': priority,
  14. # 'host': 'localhost',
  15. # 'port': 11211,
  16. # 'unix_socket': None
  17. # }}
  18. ORDER = ['cache', 'net', 'connections', 'items', 'evicted_reclaimed',
  19. 'get', 'get_rate', 'set_rate', 'cas', 'delete', 'increment', 'decrement', 'touch', 'touch_rate']
  20. CHARTS = {
  21. 'cache': {
  22. 'options': [None, 'Cache Size', 'megabytes', 'cache', 'memcached.cache', 'stacked'],
  23. 'lines': [
  24. ['avail', 'available', 'absolute', 1, 1048576],
  25. ['used', 'used', 'absolute', 1, 1048576]
  26. ]},
  27. 'net': {
  28. 'options': [None, 'Network', 'kilobits/s', 'network', 'memcached.net', 'area'],
  29. 'lines': [
  30. ['bytes_read', 'in', 'incremental', 8, 1024],
  31. ['bytes_written', 'out', 'incremental', -8, 1024]
  32. ]},
  33. 'connections': {
  34. 'options': [None, 'Connections', 'connections/s', 'connections', 'memcached.connections', 'line'],
  35. 'lines': [
  36. ['curr_connections', 'current', 'incremental'],
  37. ['rejected_connections', 'rejected', 'incremental'],
  38. ['total_connections', 'total', 'incremental']
  39. ]},
  40. 'items': {
  41. 'options': [None, 'Items', 'items', 'items', 'memcached.items', 'line'],
  42. 'lines': [
  43. ['curr_items', 'current', 'absolute'],
  44. ['total_items', 'total', 'absolute']
  45. ]},
  46. 'evicted_reclaimed': {
  47. 'options': [None, 'Items', 'items', 'items', 'memcached.evicted_reclaimed', 'line'],
  48. 'lines': [
  49. ['reclaimed', 'reclaimed', 'absolute'],
  50. ['evictions', 'evicted', 'absolute']
  51. ]},
  52. 'get': {
  53. 'options': [None, 'Requests', 'requests', 'get ops', 'memcached.get', 'stacked'],
  54. 'lines': [
  55. ['get_hits', 'hits', 'percent-of-absolute-row'],
  56. ['get_misses', 'misses', 'percent-of-absolute-row']
  57. ]},
  58. 'get_rate': {
  59. 'options': [None, 'Rate', 'requests/s', 'get ops', 'memcached.get_rate', 'line'],
  60. 'lines': [
  61. ['cmd_get', 'rate', 'incremental']
  62. ]},
  63. 'set_rate': {
  64. 'options': [None, 'Rate', 'requests/s', 'set ops', 'memcached.set_rate', 'line'],
  65. 'lines': [
  66. ['cmd_set', 'rate', 'incremental']
  67. ]},
  68. 'delete': {
  69. 'options': [None, 'Requests', 'requests', 'delete ops', 'memcached.delete', 'stacked'],
  70. 'lines': [
  71. ['delete_hits', 'hits', 'percent-of-absolute-row'],
  72. ['delete_misses', 'misses', 'percent-of-absolute-row'],
  73. ]},
  74. 'cas': {
  75. 'options': [None, 'Requests', 'requests', 'check and set ops', 'memcached.cas', 'stacked'],
  76. 'lines': [
  77. ['cas_hits', 'hits', 'percent-of-absolute-row'],
  78. ['cas_misses', 'misses', 'percent-of-absolute-row'],
  79. ['cas_badval', 'bad value', 'percent-of-absolute-row']
  80. ]},
  81. 'increment': {
  82. 'options': [None, 'Requests', 'requests', 'increment ops', 'memcached.increment', 'stacked'],
  83. 'lines': [
  84. ['incr_hits', 'hits', 'percent-of-absolute-row'],
  85. ['incr_misses', 'misses', 'percent-of-absolute-row']
  86. ]},
  87. 'decrement': {
  88. 'options': [None, 'Requests', 'requests', 'decrement ops', 'memcached.decrement', 'stacked'],
  89. 'lines': [
  90. ['decr_hits', 'hits', 'percent-of-absolute-row'],
  91. ['decr_misses', 'misses', 'percent-of-absolute-row']
  92. ]},
  93. 'touch': {
  94. 'options': [None, 'Requests', 'requests', 'touch ops', 'memcached.touch', 'stacked'],
  95. 'lines': [
  96. ['touch_hits', 'hits', 'percent-of-absolute-row'],
  97. ['touch_misses', 'misses', 'percent-of-absolute-row']
  98. ]},
  99. 'touch_rate': {
  100. 'options': [None, 'Rate', 'requests/s', 'touch ops', 'memcached.touch_rate', 'line'],
  101. 'lines': [
  102. ['cmd_touch', 'rate', 'incremental']
  103. ]}
  104. }
  105. class Service(SocketService):
  106. def __init__(self, configuration=None, name=None):
  107. SocketService.__init__(self, configuration=configuration, name=name)
  108. self.request = "stats\r\n"
  109. self.host = "localhost"
  110. self.port = 11211
  111. self._keep_alive = True
  112. self.unix_socket = None
  113. self.order = ORDER
  114. self.definitions = CHARTS
  115. def _get_data(self):
  116. """
  117. Get data from socket
  118. :return: dict
  119. """
  120. response = self._get_raw_data()
  121. if response is None:
  122. # error has already been logged
  123. return None
  124. if response.startswith('ERROR'):
  125. self.error("received ERROR")
  126. return None
  127. try:
  128. parsed = response.split("\n")
  129. except AttributeError:
  130. self.error("response is invalid/empty")
  131. return None
  132. # split the response
  133. data = {}
  134. for line in parsed:
  135. if line.startswith('STAT'):
  136. try:
  137. t = line[5:].split(' ')
  138. data[t[0]] = t[1]
  139. except (IndexError, ValueError):
  140. self.debug("invalid line received: " + str(line))
  141. if not data:
  142. self.error("received data doesn't have any records")
  143. return None
  144. # custom calculations
  145. try:
  146. data['avail'] = int(data['limit_maxbytes']) - int(data['bytes'])
  147. data['used'] = int(data['bytes'])
  148. except (KeyError, ValueError, TypeError):
  149. pass
  150. return data
  151. def _check_raw_data(self, data):
  152. if data.endswith('END\r\n'):
  153. self.debug("received full response from memcached")
  154. return True
  155. self.debug("waiting more data from memcached")
  156. return False
  157. def check(self):
  158. """
  159. Parse configuration, check if memcached is available
  160. :return: boolean
  161. """
  162. self._parse_config()
  163. data = self._get_data()
  164. if data is None:
  165. return False
  166. return True