postgresql_metrics/metrics_gatherer.py (138 lines of code) (raw):
# -*- coding: utf-8 -*-
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This module provides all the metrics in correct format for output.
Steps for adding statistic values as metrics:
1) Write function for extracting the statistics value:
* extract from DB, then write it in postgres_queries.py
* extract from local Postgres data directory,
then write it in localhost_postgres_stats.py
2) Write a function into default_metrics.py for transferring a statistic into a metric.
3) Write a function in this module to call both of the above defined functions,
returning the metrics in correct form (always in a list, see below).
4) Add the name of the function in this module into the configuration,
and define the interval the metric should be called.
"""
from postgresql_metrics.default_metrics import (
metric_client_connections,
metric_database_size,
metric_transaction_rate,
metric_rollbacks_rate,
metric_seconds_since_last_vacuum,
metric_blocks_read_from_disk,
metric_blocks_read_from_buffer,
metric_blocks_heap_hit_ratio,
metric_locks_granted,
metric_locks_waiting,
metric_sec_since_oldest_xact_start,
metric_table_bloat,
metric_index_hit_ratio,
metric_replication_delay_bytes,
metric_wal_file_amount,
metric_incoming_replication_running,
metric_multixact_members_per_mxid,
metric_multixact_remaining_ratio,
metric_xid_remaining_ratio,
metric_multixact_members_remaining_ratio,
)
from postgresql_metrics.localhost_postgres_stats import get_amount_of_wal_files, get_multixact_member_files
from postgresql_metrics.postgres_queries import (
get_client_connections_amount,
get_disk_usage_for_database,
get_transaction_rate_for_database,
get_seconds_since_last_vacuum_per_table,
get_heap_hit_statistics,
get_lock_statistics,
get_oldest_transaction_timestamp,
get_table_bloat,
get_index_hit_rates,
get_replication_delays,
get_tables_with_oids_for_current_db,
get_wal_receiver_status,
get_max_mxid_age,
get_max_xid_age,
)
MEMBERS_PER_MEMBER_FILE = 52352
MAX_MULTIXACT_MEMBERS = 2**32
WRAPAROUND_LIMIT = (2**32/2) - 1
# Notice that all functions here are expected to return a list of metrics.
# Notice also that the names of these functions should match the configuration.
def get_stats_client_connections(_data_dir, db_connection):
client_amount = get_client_connections_amount(db_connection)
return [metric_client_connections(client_amount)]
def get_stats_disk_usage_for_database(_data_dir, db_connection):
db_size = get_disk_usage_for_database(db_connection)
return [metric_database_size(db_size[0], db_size[1])]
def get_stats_tx_rate_for_database(_data_dir, db_connection):
db_name, tx_rate, tx_rollbacks = get_transaction_rate_for_database(db_connection)
if tx_rate is not None:
return [metric_transaction_rate(db_name, tx_rate),
metric_rollbacks_rate(db_name, tx_rollbacks)]
else:
return []
def get_stats_seconds_since_last_vacuum_per_table(_data_dir, db_connection):
last_vacuums_data = get_seconds_since_last_vacuum_per_table(db_connection)
metrics = []
for db_name, table_name, seconds_since in last_vacuums_data:
metrics.append(metric_seconds_since_last_vacuum(db_name, table_name, seconds_since))
return metrics
def get_stats_heap_hit_statistics(_data_dir, db_connection):
db_name, heap_read, heap_hit, heap_hit_ratio = get_heap_hit_statistics(db_connection)
metrics = []
if heap_hit_ratio is not None:
metrics.append(metric_blocks_read_from_disk(db_name, heap_read))
metrics.append(metric_blocks_read_from_buffer(db_name, heap_hit))
metrics.append(metric_blocks_heap_hit_ratio(db_name, heap_hit_ratio))
return metrics
def get_stats_lock_statistics(_data_dir, db_connection):
locks_by_type, [total_locks_waiting, total_locks_granted] = get_lock_statistics(db_connection)
metrics = []
for lock_type, [locks_waiting, locks_granted] in locks_by_type.items():
metrics.append(metric_locks_granted(lock_type, locks_granted))
metrics.append(metric_locks_waiting(lock_type, locks_waiting))
metrics.append(metric_locks_granted("total", total_locks_granted))
metrics.append(metric_locks_waiting("total", total_locks_waiting))
return metrics
def get_stats_oldest_transaction_timestamp(_data_dir, db_connection):
db_name, sec_since_oldest_xact_start = get_oldest_transaction_timestamp(db_connection)
metrics = []
if sec_since_oldest_xact_start is not None:
metrics.append(metric_sec_since_oldest_xact_start(db_name, sec_since_oldest_xact_start))
return metrics
def get_stats_table_bloat(_data_dir, db_connection):
tables_with_oids = get_tables_with_oids_for_current_db(db_connection)
metrics = []
for table_oid, table_name in tables_with_oids:
db_name, table_bloat_percentage = get_table_bloat(db_connection, table_oid)
if db_name:
metrics.append(metric_table_bloat(db_name, table_name, table_bloat_percentage))
return metrics
def get_stats_index_hit_rates(_data_dir, db_connection):
index_hit_rates = get_index_hit_rates(db_connection)
metrics = []
for db_name, table_name, index_hit_ratio in index_hit_rates:
if index_hit_ratio is not None:
metrics.append(metric_index_hit_ratio(db_name, table_name, index_hit_ratio))
return metrics
def get_stats_replication_delays(_data_dir, db_connection):
replication_delays = get_replication_delays(db_connection)
metrics = []
for client_addr, delay_in_bytes in replication_delays:
metrics.append(metric_replication_delay_bytes(client_addr, delay_in_bytes))
return metrics
def _get_multixact_members(data_dir):
return get_multixact_member_files(data_dir) * MEMBERS_PER_MEMBER_FILE
def get_multixact_members_per_mxid(data_dir, db_connection):
members = _get_multixact_members(data_dir)
mxid_age = get_max_mxid_age(db_connection)
if not mxid_age:
return []
members_per_id = round(members / mxid_age, 2)
return [metric_multixact_members_per_mxid(members_per_id)]
def get_multixact_members_remaining_ratio(data_dir, _db_connection):
members = _get_multixact_members(data_dir)
ratio = round(members / MAX_MULTIXACT_MEMBERS, 2)
percentage_remaining = (1.0 - ratio) * 100
return [metric_multixact_members_remaining_ratio(percentage_remaining)]
def get_multixact_remaining_ratio(_data_dir, db_connection):
mxid_age = get_max_mxid_age(db_connection)
if not mxid_age:
return []
ratio = round(mxid_age / WRAPAROUND_LIMIT, 2)
percentage_remaining = (1.0 - ratio) * 100
return [metric_multixact_remaining_ratio(percentage_remaining)]
def get_xid_remaining_ratio(_data_dir, db_connection):
xid_age = get_max_xid_age(db_connection)
if not xid_age:
return []
ratio = round(xid_age / WRAPAROUND_LIMIT, 2)
percentage_remaining = (1.0 - ratio) * 100
return [metric_xid_remaining_ratio(percentage_remaining)]
def get_stats_wal_file_amount(data_dir, _db_connection):
return [metric_wal_file_amount(get_amount_of_wal_files(data_dir))]
def get_stats_incoming_replication_status(_data_dir, db_connection):
return [metric_incoming_replication_running(host, is_streaming)
for host, is_streaming in get_wal_receiver_status(db_connection)]