Added ProxySQL collector for diamond
This commit is contained in:
Родитель
f733d1fb5c
Коммит
9af3842324
|
@ -0,0 +1,7 @@
|
|||
enabled=True
|
||||
db = None
|
||||
host = 127.0.0.1
|
||||
passwd = stats
|
||||
port = 6032
|
||||
user = stats
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
# coding=utf-8
|
||||
|
||||
import diamond.collector
|
||||
from diamond.collector import str_to_bool
|
||||
import re
|
||||
import time
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
from MySQLdb import MySQLError
|
||||
except ImportError:
|
||||
MySQLdb = None
|
||||
MySQLError = ValueError
|
||||
|
||||
|
||||
class ProxySQLCollector(diamond.collector.Collector):
|
||||
|
||||
_GAUGE_KEYS = [
|
||||
'Active_Transactions',
|
||||
'Client_Connections_connected',
|
||||
'ConnPool_memory_bytes',
|
||||
'MySQL_Monitor_Workers',
|
||||
'MySQL_Thread_Workers',
|
||||
'Query_Cache_Entries',
|
||||
'Query_Cache_Memory_bytes',
|
||||
'SQLite3_memory_bytes',
|
||||
'Server_Connections_connected',
|
||||
'mysql_backend_buffers_bytes',
|
||||
'mysql_frontend_buffers_bytes',
|
||||
'mysql_session_internal_bytes',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ProxySQLCollector, self).__init__(*args, **kwargs)
|
||||
|
||||
def process_config(self):
|
||||
super(ProxySQLCollector, self).process_config()
|
||||
if self.config['hosts'].__class__.__name__ != 'list':
|
||||
self.config['hosts'] = [self.config['hosts']]
|
||||
|
||||
# Move legacy config format to new format
|
||||
if 'host' in self.config:
|
||||
hoststr = "%s:%s@%s:%s/%s" % (
|
||||
self.config['user'],
|
||||
self.config['passwd'],
|
||||
self.config['host'],
|
||||
self.config['port'],
|
||||
self.config['db'],
|
||||
)
|
||||
self.config['hosts'].append(hoststr)
|
||||
|
||||
self.db = None
|
||||
|
||||
def get_default_config_help(self):
|
||||
config_help = super(ProxySQLCollector, self).get_default_config_help()
|
||||
config_help.update({
|
||||
'publish':
|
||||
"Which rows of 'SHOW MYSQL STATUS' you would " +
|
||||
"like to publish. Leave unset to publish all",
|
||||
'hosts': 'List of hosts to collect from. Format is ' +
|
||||
'yourusername:yourpassword@host:port/db[/nickname]' +
|
||||
'use db "None" to avoid connecting to a particular db'
|
||||
})
|
||||
return config_help
|
||||
|
||||
def get_default_config(self):
|
||||
"""
|
||||
Returns the default collector settings
|
||||
"""
|
||||
config = super(ProxySQLCollector, self).get_default_config()
|
||||
config.update({
|
||||
'path': 'proxysql',
|
||||
# Connection settings
|
||||
'hosts': [],
|
||||
|
||||
})
|
||||
return config
|
||||
|
||||
def get_db_stats(self, query):
|
||||
cursor = self.db.cursor(cursorclass=MySQLdb.cursors.DictCursor)
|
||||
|
||||
try:
|
||||
cursor.execute(query)
|
||||
return cursor.fetchall()
|
||||
except MySQLError, e:
|
||||
self.log.error('ProxySQLCollector could not get db stats', e)
|
||||
return ()
|
||||
|
||||
def connect(self, params):
|
||||
try:
|
||||
self.db = MySQLdb.connect(**params)
|
||||
self.log.debug('ProxySQLCollector: Connected to database.')
|
||||
except MySQLError, e:
|
||||
self.log.error('ProxySQLCollector couldnt connect to database %s', e)
|
||||
return False
|
||||
return True
|
||||
|
||||
def disconnect(self):
|
||||
self.db.close()
|
||||
|
||||
def get_db_global_status(self):
|
||||
return self.get_db_stats('SHOW MYSQL STATUS')
|
||||
|
||||
def get_stats(self, params):
|
||||
metrics = {'status': {}}
|
||||
|
||||
if not self.connect(params):
|
||||
return metrics
|
||||
|
||||
rows = self.get_db_global_status()
|
||||
for row in rows:
|
||||
try:
|
||||
metrics['status'][row['Variable_name']] = float(row['Value'])
|
||||
except:
|
||||
pass
|
||||
|
||||
self.disconnect()
|
||||
|
||||
return metrics
|
||||
|
||||
def _publish_stats(self, nickname, metrics):
|
||||
|
||||
for key in metrics:
|
||||
for metric_name in metrics[key]:
|
||||
metric_value = metrics[key][metric_name]
|
||||
|
||||
if type(metric_value) is not float:
|
||||
continue
|
||||
|
||||
if metric_name not in self._GAUGE_KEYS:
|
||||
metric_value = self.derivative(nickname + metric_name,
|
||||
metric_value)
|
||||
if key == 'status':
|
||||
if (('publish' not in self.config or
|
||||
metric_name in self.config['publish'])):
|
||||
self.publish(nickname + metric_name, metric_value)
|
||||
else:
|
||||
self.publish(nickname + metric_name, metric_value)
|
||||
|
||||
def collect(self):
|
||||
|
||||
if MySQLdb is None:
|
||||
self.log.error('Unable to import MySQLdb')
|
||||
return False
|
||||
|
||||
for host in self.config['hosts']:
|
||||
matches = re.search(
|
||||
'^([^:]*):([^@]*)@([^:]*):?([^/]*)/([^/]*)/?(.*)', host)
|
||||
|
||||
if not matches:
|
||||
self.log.error(
|
||||
'Connection string not in required format, skipping: %s',
|
||||
host)
|
||||
continue
|
||||
|
||||
params = {}
|
||||
|
||||
params['host'] = matches.group(3)
|
||||
try:
|
||||
params['port'] = int(matches.group(4))
|
||||
except ValueError:
|
||||
params['port'] = 3306
|
||||
params['db'] = matches.group(5)
|
||||
params['user'] = matches.group(1)
|
||||
params['passwd'] = matches.group(2)
|
||||
|
||||
nickname = matches.group(6)
|
||||
if len(nickname):
|
||||
nickname += '.'
|
||||
|
||||
if params['db'] == 'None':
|
||||
del params['db']
|
||||
|
||||
try:
|
||||
metrics = self.get_stats(params=params)
|
||||
except Exception, e:
|
||||
try:
|
||||
self.disconnect()
|
||||
except MySQLdb.ProgrammingError:
|
||||
pass
|
||||
self.log.error('Collection failed for %s %s', nickname, e)
|
||||
continue
|
||||
|
||||
# Warn if publish contains an unknown variable
|
||||
if 'publish' in self.config and metrics['status']:
|
||||
for k in self.config['publish'].split():
|
||||
if k not in metrics['status']:
|
||||
self.log.error("No such key '%s' available, issue " +
|
||||
"'show global status' for a full " +
|
||||
"list", k)
|
||||
self._publish_stats(nickname, metrics)
|
Загрузка…
Ссылка в новой задаче