From 86f0f230b5b8a7f18ee741d7f565a68b0b0f4bad Mon Sep 17 00:00:00 2001 From: Eric Weaver Date: Thu, 25 Sep 2025 16:43:21 -0400 Subject: [PATCH] Add query errors by user metric --- mysql/datadog_checks/mysql/mysql.py | 3 +++ mysql/datadog_checks/mysql/queries.py | 19 +++++++++++++++++++ mysql/metadata.csv | 1 + mysql/tests/test_mysql.py | 2 ++ mysql/tests/variables.py | 2 +- 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/mysql/datadog_checks/mysql/mysql.py b/mysql/datadog_checks/mysql/mysql.py index 347d5793bd737..e8d50a0ec0874 100644 --- a/mysql/datadog_checks/mysql/mysql.py +++ b/mysql/datadog_checks/mysql/mysql.py @@ -63,6 +63,7 @@ from .metadata import MySQLMetadata from .queries import ( QUERY_DEADLOCKS, + QUERY_ERRORS_RAISED, QUERY_USER_CONNECTIONS, SQL_95TH_PERCENTILE, SQL_AVG_QUERY_RUN_TIME, @@ -427,6 +428,8 @@ def _get_runtime_queries(self, db): if self.global_variables.performance_schema_enabled: queries.extend([QUERY_USER_CONNECTIONS]) + if self.version.version_compatible((8, 0, 0)): + queries.extend([QUERY_ERRORS_RAISED]) if self._index_metrics.include_index_metrics: queries.extend(self._index_metrics.queries) self._runtime_queries_cached = self._new_query_executor(queries) diff --git a/mysql/datadog_checks/mysql/queries.py b/mysql/datadog_checks/mysql/queries.py index d296436a4660d..6837bdfbc49a1 100644 --- a/mysql/datadog_checks/mysql/queries.py +++ b/mysql/datadog_checks/mysql/queries.py @@ -242,6 +242,25 @@ ], } +QUERY_ERRORS_RAISED = { + 'name': 'performance_schema.events_errors_summary_by_user_by_error', + 'query': """ + SELECT + SUM_ERROR_RAISED as errors_raised, + ERROR_NUMBER as error_number, + ERROR_NAME as error_name, + USER as user + FROM performance_schema.events_errors_summary_by_user_by_error + WHERE SUM_ERROR_RAISED > 0 + """.strip(), + 'columns': [ + {'name': 'mysql.performance.errors_raised_by_user', 'type': 'gauge'}, + {'name': 'error_number', 'type': 'tag'}, + {'name': 'error_name', 'type': 'tag'}, + {'name': 'user', 'type': 'tag'}, + ], +} + def show_replica_status_query(version, is_mariadb, channel=''): if version.version_compatible((10, 5, 1)) or not is_mariadb and version.version_compatible((8, 0, 22)): diff --git a/mysql/metadata.csv b/mysql/metadata.csv index a1190d85be2b6..5b8e3061c97af 100644 --- a/mysql/metadata.csv +++ b/mysql/metadata.csv @@ -156,6 +156,7 @@ mysql.performance.created_tmp_disk_tables,gauge,,table,second,The rate of intern mysql.performance.created_tmp_files,gauge,,file,second,The rate of temporary files created by second.,-1,mysql,tmp files created, mysql.performance.created_tmp_tables,gauge,,table,second,The rate of internal temporary tables created by second by the server while executing statements.,0,mysql,tmp tables created, mysql.performance.digest_95th_percentile.avg_us,gauge,,microsecond,,Query response time 95th percentile per schema.,0,mysql,mysql response time 95th, +mysql.performance.errors_raised_by_user,gauge,,error,,"The number of errors raised per user, error code and name. Tags: `user`, `error_number`, `error_name`",0,mysql,errors raised, mysql.performance.handler_commit,gauge,,operation,second,The number of internal COMMIT statements.,0,mysql,mysql performance handler_commit, mysql.performance.handler_delete,gauge,,operation,second,The number of internal DELETE statements.,0,mysql,mysql performance handler_delete, mysql.performance.handler_prepare,gauge,,operation,second,The number of internal PREPARE statements.,0,mysql,mysql performance handler_prepare, diff --git a/mysql/tests/test_mysql.py b/mysql/tests/test_mysql.py index 7af8c6268dc99..cef1307885e1a 100644 --- a/mysql/tests/test_mysql.py +++ b/mysql/tests/test_mysql.py @@ -293,6 +293,8 @@ def _assert_complex_config( ), at_least=0, # TODO this metric includes processlist_host tag which contains a random IP address ) + elif mname == 'mysql.performance.errors_raised' and MYSQL_VERSION_PARSED < parse_version('8.0'): + continue elif mname == 'mysql.replication.group.member_status': aggregator.assert_metric(mname, tags=metric_tags + group_replication_tags, count=expected_counts) elif mname in variables.GROUP_REPLICATION_VARS + variables.GROUP_REPLICATION_VARS_8_0_2: diff --git a/mysql/tests/variables.py b/mysql/tests/variables.py index 17476af1b4396..81c998c3d4cc3 100644 --- a/mysql/tests/variables.py +++ b/mysql/tests/variables.py @@ -254,7 +254,7 @@ PERFORMANCE_VARS = ['mysql.performance.query_run_time.avg', 'mysql.performance.digest_95th_percentile.avg_us'] -COMMON_PERFORMANCE_VARS = ['mysql.performance.user_connections'] +COMMON_PERFORMANCE_VARS = ['mysql.performance.user_connections', 'mysql.performance.errors_raised'] # This exists to comply with some of the testing patterns with the old API. QUERY_EXECUTOR_METRIC_SETS = {