
`prometheus_client`的`collectorregistry`默认不提供直接获取已注册度量指标对象(如`counter`)的公共方法,导致开发者常需通过私有属性访问。本文深入探讨了这一挑战,并提供了两种专业的解决方案:一是通过自定义类封装管理所有度量指标,适用于静态定义场景;二是通过继承`collectorregistry`并实现线程安全的`get_metric`方法,适用于更动态和健壮的度量指标管理需求。
在使用 prometheus_client 库时,我们通常会创建 Counter、Gauge、Histogram 等度量指标,并将它们注册到 CollectorRegistry 中。然而,CollectorRegistry 的设计侧重于收集和暴露指标数据,而非提供一个公共 API 来直接检索已注册的度量指标对象本身。这给需要在程序运行时获取并操作特定指标对象的场景带来了不便。例如,尝试通过 registry._names_to_collectors.get(name) 这样的私有属性来获取指标对象,虽然可行,但并不推荐,因为它依赖于库的内部实现,未来可能发生变化。
CollectorRegistry 提供了一个名为 restricted_registry 的实验性方法,但它并非用于获取度量指标对象。该方法返回一个受限的注册表,仅包含指定名称的指标样本,主要用于过滤数据暴露,且在处理带有标签的指标时需要精确指定标签值,这与获取原始指标对象的需求不符。
为了解决这一问题,我们可以采用以下两种更专业、更健壮的策略。
这种方法的核心思想是创建一个自定义的类(例如 PrometheusMetricsManager),将所有创建的度量指标对象存储在一个内部字典中。当需要访问某个指标时,只需通过这个自定义类提供的公共方法,根据指标名称从字典中检索即可。
立即学习“Python免费学习笔记(深入)”;
实现思路:
示例代码:
from prometheus_client import CollectorRegistry, Counter, Gauge, Histogram, Summary, Enum, write_to_textfile
from typing import Dict, Union
# 定义所有可能的度量指标类型
MetricType = Union[Counter, Gauge, Histogram, Summary, Enum]
class PrometheusMetricsManager:
def __init__(self):
self._registry = CollectorRegistry()
self._metrics: Dict[str, MetricType] = {}
def get_registry(self) -> CollectorRegistry:
"""获取内部的CollectorRegistry实例。"""
return self._registry
def register_metric(self, metric: MetricType):
"""
注册单个度量指标到注册表并存储在管理器中。
"""
# 注册到Prometheus的CollectorRegistry
self._registry.register(metric)
# 存储到自定义管理器中,以便后续获取
# 注意:这里我们假设metric.name在Prometheus客户端中是唯一的
# 对于带有标签的指标,name是基础名称,实际存储的可能是MetricWithLabels
# 为了简化,我们直接使用metric.name作为key
# 如果需要区分带标签和不带标签的同名指标,需要更复杂的键策略
if hasattr(metric, '_name'): # 对于Counter, Gauge等,直接访问_name
self._metrics[metric._name] = metric
else: # 对于其他可能没有直接_name属性的复杂指标,需要根据其描述获取名称
# 这是一个简化的处理,实际应用可能需要更健壮的逻辑
# 例如,通过metric.describe()获取MetricFamilySamples,再提取name
print(f"Warning: Metric {metric} might not have a direct '_name' attribute. Using fallback.")
# 尝试从describe()获取第一个样本的名称
try:
metric_name = next(iter(metric.describe())).name
self._metrics[metric_name] = metric
except Exception:
print(f"Could not determine name for metric: {metric}")
def get_metric(self, name: str) -> MetricType | None:
"""
根据名称获取已注册的度量指标对象。
"""
return self._metrics.get(name)
# 使用示例
if __name__ == "__main__":
manager = PrometheusMetricsManager()
# 创建并注册Counter
request_counter = Counter("http_requests_total", "Total HTTP requests.", registry=manager.get_registry())
manager.register_metric(request_counter)
# 创建并注册Gauge
in_progress_gauge = Gauge("http_requests_in_progress", "HTTP requests in progress.", registry=manager.get_registry())
manager.register_metric(in_progress_gauge)
# 模拟操作
request_counter.inc(5)
in_progress_gauge.set(2)
# 从管理器中获取Counter并继续操作
retrieved_counter = manager.get_metric("http_requests_total")
if retrieved_counter and isinstance(retrieved_counter, Counter):
retrieved_counter.inc(3)
print(f"Incremented http_requests_total to: {retrieved_counter._value}") # 直接访问私有属性查看值
# 从管理器中获取Gauge并继续操作
retrieved_gauge = manager.get_metric("http_requests_in_progress")
if retrieved_gauge and isinstance(retrieved_gauge, Gauge):
retrieved_gauge.set(1)
print(f"Set http_requests_in_progress to: {retrieved_gauge._value}")
# 将指标写入文件以验证
write_to_textfile("metrics_output_manager.prom", manager.get_registry())
print("Metrics written to metrics_output_manager.prom")
注意事项:
这种方法更为优雅和健壮,它通过继承 CollectorRegistry 类,并在子类中添加一个公共方法来获取度量指标。关键在于,当访问 CollectorRegistry 的内部数据结构(如 _names_to_collectors)时,必须使用其内部提供的锁 (self._lock) 来确保线程安全。
实现思路:
示例代码:
from prometheus_client import CollectorRegistry, Counter, Gauge, write_to_textfile
from prometheus_client.registry import Collector # Collector是所有指标的基类
from typing import Optional
class CustomCollectorRegistry(CollectorRegistry):
def get_metric(self, name: str) -> Optional[Collector]:
"""
线程安全地从注册表中获取已注册的度量指标对象。
"""
with self._lock: # 使用内部锁保证线程安全
# _names_to_collectors 是一个内部字典,存储了所有已注册的指标
# 键是指标的名称(不含_total, _bucket等后缀),值是指标对象本身
return self._names_to_collectors.get(name)
# 使用示例
if __name__ == "__main__":
# 创建自定义注册表实例
custom_registry = CustomCollectorRegistry()
# 创建并注册Counter
my_counter = Counter("my_app_requests_total", "Total requests for my application.", registry=custom_registry)
my_counter.inc(10)
# 创建并注册Gauge
my_gauge = Gauge("my_app_current_users", "Current active users.", registry=custom_registry)
my_gauge.set(5)
# 从自定义注册表中获取Counter并操作
retrieved_counter_obj = custom_registry.get_metric("my_app_requests_total")
if retrieved_counter_obj and isinstance(retrieved_counter_obj, Counter):
retrieved_counter_obj.inc(7)
print(f"Incremented my_app_requests_total to: {retrieved_counter_obj._value}")
# 从自定义注册表中获取Gauge并操作
retrieved_gauge_obj = custom_registry.get_metric("my_app_current_users")
if retrieved_gauge_obj and isinstance(retrieved_gauge_obj, Gauge):
retrieved_gauge_obj.set(8)
print(f"Set my_app_current_users to: {retrieved_gauge_obj._value}")
# 尝试获取一个不存在的指标
non_existent_metric = custom_registry.get_metric("non_existent_metric")
if non_existent_metric is None:
print("Successfully handled non-existent metric retrieval.")
# 将指标写入文件以验证
write_to_textfile("metrics_output_custom_registry.prom", custom_registry)
print("Metrics written to metrics_output_custom_registry.prom")
注意事项:
尽管 prometheus_client 的 CollectorRegistry 没有直接的公共 API 来获取已注册的度量指标对象,但我们可以通过上述两种专业方案来解决这一问题。
在选择方案时,应根据项目的具体需求、指标的生命周期管理方式以及对线程安全的要求进行权衡。无论选择哪种方案,都应避免直接依赖 _names_to_collectors 等私有属性,以确保代码的稳定性和可维护性。
以上就是Python Prometheus client: 高效管理与获取度量指标对象的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号