Rust监控指标库prometheus-static-metric的使用:静态Prometheus指标生成与高性能采集

Rust监控指标库prometheus-static-metric的使用:静态Prometheus指标生成与高性能采集

为什么需要这个库?

MetricVec(如CounterVecGaugeVecHistogramVec)性能较低。但如果知道标签的所有可能值,可以将MetricVec中的每个指标缓存起来以避免运行时开销。

例如,以下代码在多次调用时可能很慢:

some_counter_vec.with_label_values(&["label_1_foo", "label_2_bar"]).inc();

这是因为每次都要根据值检索特定的Counter,并且为了确保线程安全,内部有一个锁,这使情况更糟。

示例代码

基础用法示例

use prometheus_static_metric::make_static_metric;

make_static_metric! {
    pub struct MyStaticCounterVec: Counter {
        "method" => {
            post,
            get,
            put,
            delete,
        },
        "product" => {
            foo,
            bar,
        },
    }
}

fn main() {
    let vec = CounterVec::new(Opts::new("foo", "bar"), &["method", "product"]).unwrap();
    let static_counter_vec = MyStaticCounterVec::from(&vec);

    static_counter_vec.post.foo.inc();
    static_counter_vec.delete.bar.inc_by(4.0);
    assert_eq!(static_counter_vec.post.bar.get(), 0.0);
    assert_eq!(vec.with_label_values(&["post", "foo"]).get(), 1.0);
    assert_eq!(vec.with_label_values(&["delete", "bar"]).get(), 4.0);
}

自动刷新的本地线程指标

对于更重的场景,共享的静态指标可能不够高效,可以使用make_auto_flush_static metric!宏,它将数据存储在本地线程存储中,并以自定义速率刷新到全局MetricVec

#[macro_use]
extern crate lazy_static;
extern crate prometheus;
extern crate prometheus_static_metric;

use prometheus::*;
use prometheus_static_metric::auto_flush_from;
use prometheus_static_metric::make_auto_flush_static_metric;

make_auto_flush_static_metric! {
    pub label_enum FooBar {
        foo,
        bar,
    }

    pub label_enum Methods {
        post,
        get,
        put,
        delete,
    }

    pub struct Lhrs: LocalIntCounter {
        "product" => FooBar,
        "method" => Methods,
        "version" => {
            http1: "HTTP/1",
            http2: "HTTP/2",
        },
    }
}

lazy_static! {
    pub static ref HTTP_COUNTER_VEC: IntCounterVec =
        register_int_counter_vec!(
            "http_requests_total",
            "Number of HTTP requests.",
            &["product", "method", "version"]    // label顺序不重要
        ).unwrap();
}

lazy_static! {
    // 也可以使用默认的1秒刷新间隔
    // pub static ref TLS_HTTP_COUNTER: Lhrs = auto_flush_from!(HTTP_COUNTER_VEC, Lhrs);
    pub static ref TLS_HTTP_COUNTER: Lhrs = auto_flush_from!(HTTP_COUNTER_VEC, Lhrs, std::time::Duration::from_secs(1));
}

fn main() {
    TLS_HTTP_COUNTER.foo.post.http1.inc();
    TLS_HTTP_COUNTER.foo.post.http1.inc();

    assert_eq!(
        HTTP_COUNTER_VEC
            .with_label_values(&["foo", "post", "HTTP/1"])
            .get(),
        0
    );

    ::std::thread::sleep(::std::time::Duration::from_secs(2));

    TLS_HTTP_COUNTER.foo.post.http1.inc();
    assert_eq!(
        HTTP_COUNTER_VEC
            .with_label_values(&["foo", "post", "HTTP/1"])
            .get(),
        3
    );
}

完整示例Demo

静态指标完整示例

use prometheus::{Opts, CounterVec};
use prometheus_static_metric::make_static_metric;

// 定义静态指标结构
make_static_metric! {
    pub struct HttpRequestMetrics: Counter {
        "method" => {
            get,
            post,
            put,
            delete,
        },
        "status" => {
            success: "200",
            client_error: "400",
            server_error: "500",
        },
    }
}

fn main() {
    // 创建原始的CounterVec
    let http_requests = CounterVec::new(
        Opts::new("http_requests_total", "Total HTTP requests"),
        &["method", "status"]
    ).unwrap();
    
    // 转换为静态指标
    let static_metrics = HttpRequestMetrics::from(&http_requests);

    // 使用静态指标
    static_metrics.get.success.inc();
    static_metrics.post.client_error.inc_by极速3.0;
    
    // 验证指标值
    assert_eq!(http_requests.with_label_values(&["get", "200"]).get(), 1.0);
    assert_eq!(http_requests.with_label_values(&["post", "400"]).get(), 3.0);
}

自动刷新指标完整示例

#[macro_use]
extern crate lazy_static;
use prometheus::{IntCounterVec, register_int_counter_vec};
use prometheus_static_metric::{make_auto_flush_static_metric, auto_flush_from};
use std::time::Duration;

// 定义自动刷新指标
make_auto_flush_static_metric! {
    pub label_enum ApiName {
        user_api,
        order_api,
        payment_api,
    }

    pub label_enum Env {
        production,
        staging,
        development,
    }

    pub struct ApiMetrics: LocalIntCounter {
        "api" => ApiName,
        "env" => Env,
    }
}

lazy_static! {
    static ref GLOBAL_API_METRICS: IntCounterVec = register_int_counter_vec!(
        "api_calls_total",
        "Total API calls",
        &["api", "env"]
    ).unwrap();
    
    static ref TLS_API_METRICS: ApiMetrics = auto_flush_from!(
        GLOBAL_API_METRICS, 
        ApiMetrics, 
        Duration::from_secs(2) // 每2秒刷新一次
    );
}

fn main() {
    // 线程局部指标会自动累加
    TLS_API_METRICS.user_api.production.inc();
    TLS_API_METRICS.order_api.staging.inc_by(5);
    
    // 初始时全局指标还未更新
    assert_eq!(GLOBAL_API_METRICS.with_label_values(&["user_api", "production"]).get(), 0);
    
    // 等待刷新间隔
    std::thread::sleep(Duration::from_secs(3));
    
    // 现在全局指标已更新
    assert_eq!(GLOBAL_API_METRICS.with_label_values(&["user_api", "production"]).get(), 1);
    assert_eq!(GLOBAL_API_METRICS.with_label_values(&["order_api", "staging"]).get(), 5);
}

安装

在项目的Cargo.toml中添加依赖:

[dependencies]
prometheus-static-metric = "0.5.1"

或者在项目目录中运行:

cargo add prometheus-static-metric

1 回复

以下是基于您提供的内容整理的完整示例demo,包含HTTP请求监控和温度监控两个完整示例:

HTTP请求监控示例

use prometheus::Registry;
use prometheus_static_metric::{make_static_metric, StaticMetric};

// 1. 定义静态HTTP请求指标
make_static_metric! {
    pub struct HttpRequestStatistics: Counter {
        "method" => {
            get,
            post,
            put,
            delete,
        },
        "status" => {
            ok: "200",
            bad_request: "400",
            not_found: "404",
            server_error: "500",
        },
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 2. 创建指标实例
    let http_requests = HttpRequestStatistics::default();
    
    // 3. 注册到Prometheus
    let registry = Registry::new();
    registry.register(Box::new(http_requests.clone()))?;
    
    // 4. 模拟记录HTTP请求
    // GET /api/users 200 OK
    http_requests.get.ok.inc();
    // POST /api/orders 400 Bad Request
    http_requests.post.bad_request.inc();
    // DELETE /api/products 404 Not Found
    http_requests.delete.not_found.inc();
    // PUT /api/items 500 Server Error
    http_requests.put.server_error.inc_by(3.0);
    
    // 5. 采集并打印指标
    let metric_families = registry.gather();
    for mf in metric_families {
        println!("{}", mf);
    }
    
    Ok(())
}

温度监控示例

use prometheus::Registry;
use prometheus_static_metric::{make_static_metric, StaticMetric};

// 1. 定义静态温度指标
make_static_metric! {
    pub struct TemperatureMetrics: Gauge {
        "location" => {
            kitchen,
            living_room,
            bedroom,
            outdoor,
        },
        "unit" => {
            celsius,
            fahrenheit,
        },
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 2. 创建指标实例
    let temperatures = TemperatureMetrics::default();
    
    // 3. 注册到Prometheus
    let registry = Registry::new();
    registry.register(Box::new(temperatures.clone()))?;
    
    // 4. 设置温度值
    temperatures.kitchen.celsius.set(22.5);
    temperatures.living_room.celsius.set(20.0);
    temperatures.bedroom.fahrenheit.set(68.0);
    temperatures.outdoor.celsius.set(15.3);
    
    // 5. 修改温度值(模拟温度变化)
    temperatures.kitchen.celsius.inc(); // 温度+1
    temperatures.living_room.celsius.dec(); // 温度-1
    
    // 6. 采集并打印指标
    let metric_families = registry.gather();
    for mf in metric_families {
        println!("{}", mf);
    }
    
    Ok(())
}

数据库操作监控示例

use prometheus::Registry;
use prometheus_static_metric::{make_static_metric, StaticMetric};

// 1. 定义数据库操作指标
make_static_metric! {
    pub struct DatabaseMetrics: Counter {
        "operation" => {
            select,
            insert,
            update,
            delete,
        },
        "table" => {
            users,
            products,
            orders,
        },
        "result" => {
            success,
            failure,
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 2. 创建指标实例
    let db_metrics = DatabaseMetrics::default();
    
    // 3. 注册到Prometheus
    let registry = Registry::new();
    registry.register(Box::new(db_metrics.clone()))?;
    
    // 4. 模拟数据库操作
    // SELECT * FROM users 成功
    db_metrics.select.users.success.inc();
    // INSERT INTO products 失败
    db_metrics.insert.products.failure.inc();
    // UPDATE orders 成功
    db_metrics.update.orders.success.inc_by(5.0);
    // DELETE FROM users 失败
    db_metrics.delete.users.failure.inc();
    
    // 5. 采集并打印指标
    let metric_families = registry.gather();
    for mf in metric_families {
        println!("{}", mf);
    }
    
    Ok(())
}

这些示例展示了如何:

  1. 使用make_static_metric!宏定义静态指标
  2. 初始化指标实例并注册到Prometheus
  3. 记录各种指标值
  4. 采集并输出指标数据

每个示例都包含了完整的错误处理和指标操作,可以直接运行测试。

回到顶部