From 0fb5d22252d5a6407951fd5000eb57ef5da77592 Mon Sep 17 00:00:00 2001 From: Marco Pracucci Date: Wed, 5 Dec 2018 17:14:18 +0100 Subject: [PATCH] Fixed tests after introduction of pgbouncer 1.8 support and improved README --- CHANGELOG.md | 7 + README.md | 43 ++--- prometheus_pgbouncer_exporter/collector.py | 8 +- tests/test_collector.py | 173 +++++++++++++++++++-- 4 files changed, 189 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78098dc..ade9c04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +- [BREAKING CHANGE] Renamed `pgbouncer_stats_queries_total` to `pgbouncer_stats_requests_total` on pgbouncer <= 1.7 +- [FEATURE] Added pgbouncer >= 1.8 support [#8](https://github.com/spreaker/prometheus-pgbouncer-exporter/pull/8) (thanks to [bitglue](https://github.com/bitglue)), including the following new metrics: + - `pgbouncer_stats_transactions_total` + - `pgbouncer_stats_queries_total` + - `pgbouncer_stats_transactions_duration_microseconds` + - `pgbouncer_stats_waiting_duration_microseconds` + ### 1.0.1 (2018-08-30) - [BUGFIX] Fixed `PGBOUNCER_EXPORTER_PORT` environment variable data type [#7](https://github.com/spreaker/prometheus-pgbouncer-exporter/pull/7) diff --git a/README.md b/README.md index 87e417c..1d7d79a 100644 --- a/README.md +++ b/README.md @@ -44,27 +44,28 @@ PgBouncer is a single thread application and so can only saturate a single CPU c The exporter exports the following metrics for each monitored pgbouncer instance: -| Metric name | Type | Description | -| -------------------------------------------------- | -------- | ---------------- | -| `pgbouncer_stats_requests_total` | counter | Total number of requests pooled. Could be transactions or queries, depending on pool mode. (labels: `database`, pgbouncer < 1.8) | -| `pgbouncer_stats_queries_total` | counter | Total number of SQL queries pooled by pgbouncer (labels: `database`, pgbouncer >= 1.8) | -| `pgbouncer_stats_queries_duration_microseconds` | counter | Total number of microseconds spent waiting for a server to return a query response. Includes time spent waiting for an available connection. (labels: `database`, pgbouncer >= 1.8) | -| `pgbouncer_stats_waiting_duration_microseconds` | counter | Total number of microseconds spent waiting for an available connection. (labels: `database`, pgbouncer >= 1.8) | -| `pgbouncer_stats_received_bytes_total` | counter | Total volume in bytes of network traffic received by pgbouncer (labels: `database`) | -| `pgbouncer_stats_sent_bytes_total` | counter | Total volume in bytes of network traffic sent by pgbouncer (labels: `database`) | -| `pgbouncer_stats_transactions_total` | counter | Total number of SQL transactions pooled by pgbouncer (labels: `database`, pgbouncer >= 1.8) | -| `pgbouncer_stats_transactions_duration_microseconds`| counter | Total number of microseconds spent in a transaction. Includes time spent waiting for an available connection. (labels: `database`, pgbouncer >= 1.8) | -| `pgbouncer_pools_client_active_connections` | gauge | Client connections that are linked to server connection and can process queries (labels: `database`, `user`) | -| `pgbouncer_pools_client_waiting_connections` | gauge | Client connections have sent queries but have not yet got a server connection (labels: `database`, `user`) | -| `pgbouncer_pools_server_active_connections` | gauge | Server connections that linked to client (labels: `database`, `user`) | -| `pgbouncer_pools_server_idle_connections` | gauge | Server connections that unused and immediately usable for client queries (labels: `database`, `user`) | -| `pgbouncer_pools_server_used_connections` | gauge | Server connections that have been idle more than server_check_delay, so they needs server_check_query to run on it before it can be used (labels: `database`, `user`) | -| `pgbouncer_pools_server_testing_connections` | gauge | Server connections that are currently running either server_reset_query or server_check_query (labels: `database`, `user`) | -| `pgbouncer_pools_server_login_connections` | gauge | Server connections currently in logging in process (labels: `database`, `user`) | -| `pgbouncer_pools_client_maxwait_seconds` | gauge | How long the first (oldest) client in queue has waited, in seconds (labels: `database`, `user`) | -| `pgbouncer_databases_database_pool_size` | gauge | Configured pool size limit (labels: `database`, `backend_database`) | -| `pgbouncer_databases_database_reserve_pool_size` | gauge | Configured reserve limit (labels: `database`, `backend_database`) | -| `pgbouncer_databases_database_current_connections` | gauge | Total number of per-database Database connections count (labels: `database`, `backend_database`) | +| Metric name | Type | PgBouncer | Description | +| --------------------------------------------------- | -------- | --------- | ---------------- | +| `pgbouncer_stats_requests_total` | counter | _<= 1.7_ | Total number of requests pooled. Could be transactions or queries, depending on pool mode. (labels: `database`) | +| `pgbouncer_stats_queries_total` | counter | _>= 1.8_ | Total number of SQL queries pooled by pgbouncer (labels: `database`) | +| `pgbouncer_stats_queries_duration_microseconds` | counter | _all_ | Total number of microseconds spent waiting for a server to return a query response. Includes time spent waiting for an available connection. (labels: `database`) | +| `pgbouncer_stats_waiting_duration_microseconds` | counter | _>= 1.8_ | Total number of microseconds spent waiting for an available connection. (labels: `database`) | +| `pgbouncer_stats_received_bytes_total` | counter | _all_ | Total volume in bytes of network traffic received by pgbouncer (labels: `database`) | +| `pgbouncer_stats_sent_bytes_total` | counter | _all_ | Total volume in bytes of network traffic sent by pgbouncer (labels: `database`) | +| `pgbouncer_stats_transactions_total` | counter | _>= 1.8_ | Total number of SQL transactions pooled by pgbouncer (labels: `database`) | +| `pgbouncer_stats_transactions_duration_microseconds`| counter | _>= 1.8_ | Total number of microseconds spent in a transaction. Includes time spent waiting for an available connection. (labels: `database`) | +| `pgbouncer_pools_client_active_connections` | gauge | _all_ | Client connections that are linked to server connection and can process queries (labels: `database`, `user`) | +| `pgbouncer_pools_client_waiting_connections` | gauge | _all_ | Client connections have sent queries but have not yet got a server connection (labels: `database`, `user`) | +| `pgbouncer_pools_server_active_connections` | gauge | _all_ | Server connections that linked to client (labels: `database`, `user`) | +| `pgbouncer_pools_server_idle_connections` | gauge | _all_ | Server connections that unused and immediately usable for client queries (labels: `database`, `user`) | +| `pgbouncer_pools_server_used_connections` | gauge | _all_ | Server connections that have been idle more than server_check_delay, so they needs server_check_query to run on it before it can be used (labels: `database`, `user`) | +| `pgbouncer_pools_server_testing_connections` | gauge | _all_ | Server connections that are currently running either server_reset_query or server_check_query (labels: `database`, `user`) | +| `pgbouncer_pools_server_login_connections` | gauge | _all_ | Server connections currently in logging in process (labels: `database`, `user`) | +| `pgbouncer_pools_client_maxwait_seconds` | gauge | _all_ | How long the first (oldest) client in queue has waited, in seconds (labels: `database`, `user`) | +| `pgbouncer_databases_database_pool_size` | gauge | _all_ | Configured pool size limit (labels: `database`, `backend_database`) | +| `pgbouncer_databases_database_reserve_pool_size` | gauge | _all_ | Configured reserve limit (labels: `database`, `backend_database`) | +| `pgbouncer_databases_database_current_connections` | gauge | _all_ | Total number of per-database Database connections count (labels: `database`, `backend_database`) | + ## Configuration file diff --git a/prometheus_pgbouncer_exporter/collector.py b/prometheus_pgbouncer_exporter/collector.py index 591a61b..d9f1cb8 100644 --- a/prometheus_pgbouncer_exporter/collector.py +++ b/prometheus_pgbouncer_exporter/collector.py @@ -55,18 +55,18 @@ def collect(self): results = self._filterMetricsByExcludeDatabases(results, self.config.getExcludeDatabases()) metrics += self._exportMetrics(results, "pgbouncer_stats_", [ # pgbouncer < 1.8 - {"type": "counter", "column": "total_requests", "metric": "requests_total", "help": "Total number of requests pooled. Could be transactions or queries, depending on pool mode."}, + {"type": "counter", "column": "total_requests", "metric": "requests_total", "help": "Total number of requests pooled. Could be transactions or queries, depending on pool mode."}, # pgbouncer >= 1.8 {"type": "counter", "column": "total_xact_count", "metric": "transactions_total", "help": "Total number of transactions pooled"}, {"type": "counter", "column": "total_query_count", "metric": "queries_total", "help": "Total number of queries pooled"}, {"type": "counter", "column": "total_xact_time", "metric": "transactions_duration_microseconds", "help": "Total number of microseconds spent in a transaction. Includes time spent waiting for an available connection."}, - {"type": "counter", "column": "total_query_time", "metric": "queries_duration_microseconds", "help": "Total number of microseconds spent waiting for a server to return a query response. Includes time spent waiting for an available connection."}, {"type": "counter", "column": "total_wait_time", "metric": "waiting_duration_microseconds", "help": "Total number of microseconds spent waiting for an available connection."}, # all versions - {"type": "counter", "column": "total_received", "metric": "received_bytes_total", "help": "Total volume in bytes of network traffic received by pgbouncer"}, - {"type": "counter", "column": "total_sent", "metric": "sent_bytes_total", "help": "Total volume in bytes of network traffic sent by pgbouncer"}, + {"type": "counter", "column": "total_query_time", "metric": "queries_duration_microseconds", "help": "Total number of microseconds spent waiting for a server to return a query response. Includes time spent waiting for an available connection."}, + {"type": "counter", "column": "total_received", "metric": "received_bytes_total", "help": "Total volume in bytes of network traffic received by pgbouncer"}, + {"type": "counter", "column": "total_sent", "metric": "sent_bytes_total", "help": "Total volume in bytes of network traffic sent by pgbouncer"}, ], {"database": "database"}, self.config.getExtraLabels()) else: diff --git a/tests/test_collector.py b/tests/test_collector.py index fdfa2c2..4d5be62 100644 --- a/tests/test_collector.py +++ b/tests/test_collector.py @@ -10,7 +10,7 @@ def getMetricsByName(metrics, name): return list(filter(lambda item: item["name"] == name, metrics)) -def fetchMetricsSuccessMock(conn, query): +def fetchMetricsSuccessFromPgBouncer17Mock(conn, query): if query == "SHOW STATS": return [ {"database": "test", "total_requests": 1, "total_query_time": 2, "total_received": 3, "total_sent": 4}, @@ -29,10 +29,26 @@ def fetchMetricsSuccessMock(conn, query): else: return False -def fetchMetricsFailureMock(conn, query): - raise Exception("Error while fetching metrics") +def fetchMetricsSuccessFromPgBouncer18Mock(conn, query): + if query == "SHOW STATS": + return [ + {"database": "test", "total_query_count": 1, "total_xact_count": 2, "total_xact_time": 2, "total_wait_time": 1, "total_query_time": 2, "total_received": 3, "total_sent": 4}, + {"database": "prod", "total_query_count": 4, "total_xact_count": 6, "total_xact_time": 3, "total_wait_time": 2, "total_query_time": 3, "total_received": 2, "total_sent": 1} + ] + elif query == "SHOW POOLS": + return [ + {"database": "test", "user": "marco", "cl_active": 1, "cl_waiting": 2, "sv_active": 3, "sv_idle": 4, "sv_used": 5, "sv_tested": 6, "sv_login": 7, "maxwait": 8 }, + {"database": "prod", "user": "marco", "cl_active": 8, "cl_waiting": 7, "sv_active": 6, "sv_idle": 5, "sv_used": 4, "sv_tested": 3, "sv_login": 2, "maxwait": 1 } + ] + elif query == "SHOW DATABASES": + return [ + {"name": "test","database": "test", "pool_size": 50, "reserve_pool": 10, "current_connections": 30 }, + {"name": "prod","database": "prod", "pool_size": 90, "reserve_pool": 20, "current_connections": 75 } + ] + else: + return False -def fetchMetricsPartialFailureMock(conn, query): +def fetchMetricsPartialFailureFromPgBouncer17Mock(conn, query): if query == "SHOW STATS": return [ {"database": "test", "total_requests": 1, "total_query_time": 2, "total_received": 3, "total_sent": 4}, @@ -45,6 +61,9 @@ def fetchMetricsPartialFailureMock(conn, query): else: return False +def fetchMetricsFailureMock(conn, query): + raise Exception("Error while fetching metrics") + # # Tests # @@ -59,7 +78,7 @@ def testShouldExportPgbouncerUpMetricOnAllMetricsScraped(self): config = PgbouncerConfig({}) collector = PgbouncerMetricsCollector(config) collector._createConnection = MagicMock(return_value=False) - collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessMock) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessFromPgBouncer17Mock) metrics = getMetricsByName(collector.collect(), "pgbouncer_up") @@ -85,7 +104,7 @@ def testShouldExportPgbouncerDownMetricOnMetricsPartiallyScraped(self): config = PgbouncerConfig({}) collector = PgbouncerMetricsCollector(config) collector._createConnection = MagicMock(return_value=False) - collector._fetchMetrics = MagicMock(side_effect=fetchMetricsPartialFailureMock) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsPartialFailureFromPgBouncer17Mock) metrics = getMetricsByName(collector.collect(), "pgbouncer_up") @@ -98,7 +117,7 @@ def testShouldExportDatabasesMetrics(self): config = PgbouncerConfig({}) collector = PgbouncerMetricsCollector(config) collector._createConnection = MagicMock(return_value=False) - collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessMock) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessFromPgBouncer17Mock) metrics = getMetricsByName(collector.collect(), "pgbouncer_databases_database_pool_size") self.assertEqual(len(metrics), 2) @@ -127,6 +146,126 @@ def testShouldExportDatabasesMetrics(self): self.assertEqual(metrics[1]["value"], 75) self.assertEqual(metrics[1]["labels"], {"backend_database":"prod", "database":"prod"}) + def testShouldExportQueriesMetricsFromPgBouncer17(self): + config = PgbouncerConfig({}) + collector = PgbouncerMetricsCollector(config) + collector._createConnection = MagicMock(return_value=False) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessFromPgBouncer17Mock) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_requests_total") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 1) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 4) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_duration_microseconds") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 2) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 3) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_received_bytes_total") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 3) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 2) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_sent_bytes_total") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 4) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 1) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_total") + self.assertEqual(len(metrics), 0) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_transactions_total") + self.assertEqual(len(metrics), 0) + + def testShouldExportQueriesMetricsFromPgBouncer18(self): + config = PgbouncerConfig({}) + collector = PgbouncerMetricsCollector(config) + collector._createConnection = MagicMock(return_value=False) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessFromPgBouncer18Mock) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_total") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 1) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 4) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_transactions_total") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 2) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 6) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_transactions_duration_microseconds") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 2) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 3) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_waiting_duration_microseconds") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 1) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 2) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_duration_microseconds") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 2) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 3) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_received_bytes_total") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 3) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 2) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_sent_bytes_total") + self.assertEqual(len(metrics), 2) + self.assertEqual(metrics[0]["type"], "counter") + self.assertEqual(metrics[0]["value"], 4) + self.assertEqual(metrics[0]["labels"], {"database":"test"}) + self.assertEqual(metrics[1]["type"], "counter") + self.assertEqual(metrics[1]["value"], 1) + self.assertEqual(metrics[1]["labels"], {"database":"prod"}) + + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_requests_total") + self.assertEqual(len(metrics), 0) + # # Databases filtering # @@ -135,9 +274,9 @@ def testShouldNotFilterDatabasesByDefault(self): config = PgbouncerConfig({}) collector = PgbouncerMetricsCollector(config) collector._createConnection = MagicMock(return_value=False) - collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessMock) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessFromPgBouncer17Mock) - metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_total") + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_requests_total") self.assertEqual(len(metrics), 2) self.assertEqual(metrics[0]["type"], "counter") self.assertEqual(metrics[0]["value"], 1) @@ -159,9 +298,9 @@ def testShouldNotFilterDatabasesOnEmptyIncludeDatabasesConfigOption(self): config = PgbouncerConfig({"include_databases":[]}) collector = PgbouncerMetricsCollector(config) collector._createConnection = MagicMock(return_value=False) - collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessMock) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessFromPgBouncer17Mock) - metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_total") + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_requests_total") self.assertEqual(len(metrics), 2) self.assertEqual(metrics[0]["type"], "counter") self.assertEqual(metrics[0]["value"], 1) @@ -183,9 +322,9 @@ def testShouldFilterDatabasesByIncludeDatabasesConfigOption(self): config = PgbouncerConfig({"include_databases": ["prod"]}) collector = PgbouncerMetricsCollector(config) collector._createConnection = MagicMock(return_value=False) - collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessMock) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessFromPgBouncer17Mock) - metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_total") + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_requests_total") self.assertEqual(len(metrics), 1) self.assertEqual(metrics[0]["type"], "counter") self.assertEqual(metrics[0]["value"], 4) @@ -201,9 +340,9 @@ def testShouldFilterDatabasesByExcludeDatabasesConfigOption(self): config = PgbouncerConfig({"exclude_databases": ["prod"]}) collector = PgbouncerMetricsCollector(config) collector._createConnection = MagicMock(return_value=False) - collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessMock) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsSuccessFromPgBouncer17Mock) - metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_total") + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_requests_total") self.assertEqual(len(metrics), 1) self.assertEqual(metrics[0]["type"], "counter") self.assertEqual(metrics[0]["value"], 1) @@ -223,9 +362,9 @@ def testShouldReturnScrapedMetricsOnPartialFailure(self): config = PgbouncerConfig({}) collector = PgbouncerMetricsCollector(config) collector._createConnection = MagicMock(return_value=False) - collector._fetchMetrics = MagicMock(side_effect=fetchMetricsPartialFailureMock) + collector._fetchMetrics = MagicMock(side_effect=fetchMetricsPartialFailureFromPgBouncer17Mock) - metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_queries_total") + metrics = getMetricsByName(collector.collect(), "pgbouncer_stats_requests_total") self.assertEqual(len(metrics), 2) self.assertEqual(metrics[0]["type"], "counter") self.assertEqual(metrics[0]["value"], 1)