Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mysql] custom query metrics #1673

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions checks.d/mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@

class MySql(AgentCheck):
SERVICE_CHECK_NAME = 'mysql.can_connect'
MAX_CUSTOM_QUERIES = 20

def __init__(self, name, init_config, agentConfig, instances=None):
AgentCheck.__init__(self, name, init_config, agentConfig, instances)
Expand All @@ -62,7 +63,7 @@ def get_library_versions(self):
return {"pymysql": pymysql.__version__}

def check(self, instance):
host, port, user, password, mysql_sock, defaults_file, tags, options = \
host, port, user, password, mysql_sock, defaults_file, tags, options, queries = \
self._get_config(instance)

if (not host or not user) and not defaults_file:
Expand All @@ -74,7 +75,7 @@ def check(self, instance):
self._collect_metadata(db, host)

# Metric collection
self._collect_metrics(host, db, tags, options)
self._collect_metrics(host, db, tags, options, queries)
if Platform.is_linux():
self._collect_system_metrics(host, db, tags)

Expand All @@ -87,8 +88,9 @@ def _get_config(self, instance):
defaults_file = instance.get('defaults_file', '')
tags = instance.get('tags', None)
options = instance.get('options', {})
queries = instance.get('queries', [])

return host, port, user, password, mysql_sock, defaults_file, tags, options
return host, port, user, password, mysql_sock, defaults_file, tags, options, queries

def _connect(self, host, port, mysql_sock, user, password, defaults_file):
service_check_tags = [
Expand Down Expand Up @@ -132,7 +134,7 @@ def _connect(self, host, port, mysql_sock, user, password, defaults_file):

return db

def _collect_metrics(self, host, db, tags, options):
def _collect_metrics(self, host, db, tags, options, queries):
cursor = db.cursor()
cursor.execute("SHOW /*!50002 GLOBAL */ STATUS;")
status_results = dict(cursor.fetchall())
Expand Down Expand Up @@ -191,6 +193,15 @@ def _collect_metrics(self, host, db, tags, options):
"SHOW SLAVE STATUS", db, tags=tags
)

# Collect custom query metrics
# Max of 20 queries allowed
if isinstance(queries, list):
for index, check in enumerate(queries):
if index == self.MAX_CUSTOM_QUERIES:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of checking this condition everytime, maybe we can just trim the list to at most self.MAX_CUSTOM_QUERIES elems before iterating: for index, check in enumerate(queries[:self.MAX_CUSTOM_QUERIES]) The warning can be logged just based on len(queries)

self.warning("Maximum number (20) of custom queries reached. Skipping the rest.")
break
self._collect_dict(check['type'], {check['field']: check['metric']}, check['query'], db, tags=tags)

def _collect_metadata(self, db, host):
self._get_version(db, host)

Expand Down Expand Up @@ -287,7 +298,9 @@ def _collect_dict(self, metric_type, field_metric_map, query, db, tags):
# cursor.description is a tuple of (column_name, ..., ...)
try:
col_idx = [d[0].lower() for d in cursor.description].index(field.lower())
self.log.debug("Collecting metric: %s" % metric)
if result[col_idx] is not None:
self.log.debug("Collecting done, value %s" % result[col_idx])
if metric_type == GAUGE:
self.gauge(metric, float(result[col_idx]), tags=tags)
elif metric_type == RATE:
Expand Down
5 changes: 5 additions & 0 deletions ci/mysql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@

task before_script: ['ci:common:before_script'] do
sh %(mysql -e "create user 'dog'@'localhost' identified by 'dog'" -uroot)
sh %(mysql -e "CREATE DATABASE testdb;" -uroot)
sh %(mysql -e "CREATE TABLE testdb.users (name VARCHAR(20), age INT);" -uroot)
sh %(mysql -e "GRANT SELECT ON testdb.users TO 'dog'@'localhost';" -uroot)
sh %(mysql -e "INSERT INTO testdb.users (name,age) VALUES('Alice',25);" -uroot)
sh %(mysql -e "INSERT INTO testdb.users (name,age) VALUES('Bob',20);" -uroot)
end

task script: ['ci:common:script'] do
Expand Down
11 changes: 11 additions & 0 deletions conf.d/mysql.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,14 @@ instances:
# options: # Optional
# replication: 0
# galera_cluster: 1
# queries: # Optional, maximum of 20 custom query metrics
# - # Sample Custom metric
# query: SELECT TIMESTAMPDIFF(second,MAX(create_time),NOW()) as last_accessed FROM requests
# metric: app.seconds_since_last_request
# type: gauge
# field: last_accessed
# - # Sample Custom metric
# query: SELECT TIMESTAMPDIFF(second,MAX(create_time),NOW()) as last_user FROM users
# metric: app.seconds_since_new_user
# type: gauge
# field: last_user
20 changes: 19 additions & 1 deletion tests/checks/integration/test_mysql.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,21 @@ class TestMySql(AgentCheckTest):
'user': 'dog',
'pass': 'dog',
'options': {'replication': True},
'tags': METRIC_TAGS
'tags': METRIC_TAGS,
'queries': [
{
'query': "SELECT * from testdb.users where name='Alice' limit 1;",
'metric': 'alice.age',
'type': 'gauge',
'field': 'age'
},
{
'query': "SELECT * from testdb.users where name='Bob' limit 1;",
'metric': 'bob.age',
'type': 'gauge',
'field': 'age'
}
]
}]

CONNECTION_FAILURE = [{
Expand Down Expand Up @@ -117,6 +131,10 @@ def test_check(self):
# Assert service metadata
self.assertServiceMetadata(['version'], count=1)

#test custom query metrics
self.assertMetric('alice.age', value=25)
self.assertMetric('bob.age', value=20)

# Raises when COVERAGE=true and coverage < 100%
self.coverage_report()

Expand Down