自定义导出器 #

一、开发概述 #

1.1 开发方式 #

text
自定义导出器开发方式:

┌─────────────────────────────────────────────┐
│ 1. 使用客户端库                             │
├─────────────────────────────────────────────┤
│ • 应用内集成                                │
│ • 直接暴露指标                              │
│ • 推荐:长期运行的服务                      │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│ 2. 独立导出器                               │
├─────────────────────────────────────────────┤
│ • 独立程序                                  │
│ • 连接外部系统获取指标                      │
│ • 推荐:第三方系统监控                      │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│ 3. Pushgateway                              │
├─────────────────────────────────────────────┤
│ • 推送指标到Pushgateway                     │
│ • Prometheus从Pushgateway拉取               │
│ • 推荐:短期任务、批处理任务                │
└─────────────────────────────────────────────┘

1.2 客户端库 #

text
官方客户端库:

┌─────────────────────────────────────────────┐
│ 语言      │ 仓库地址                        │
├───────────┼──────────────────────────────────┤
│ Go        │ github.com/prometheus/client_golang │
│ Java      │ github.com/prometheus/client_java │
│ Python    │ github.com/prometheus/client_python │
│ Ruby      │ github.com/prometheus/client_ruby │
│ Rust      │ github.com/prometheus/client_rust │
└─────────────────────────────────────────────┘

社区客户端库:
• .NET, Node.js, PHP, C++, 等等

二、Python客户端 #

2.1 安装 #

bash
pip install prometheus-client

2.2 基本示例 #

python
from prometheus_client import Counter, Gauge, Histogram, start_server
import random
import time

# 创建Counter
REQUEST_COUNT = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
)

# 创建Gauge
QUEUE_LENGTH = Gauge(
    'queue_length',
    'Current queue length',
    ['queue_name']
)

# 创建Histogram
REQUEST_LATENCY = Histogram(
    'http_request_duration_seconds',
    'HTTP request latency',
    ['method', 'endpoint'],
    buckets=[0.1, 0.5, 1.0, 2.0, 5.0, 10.0]
)

def simulate_requests():
    while True:
        # 模拟请求
        method = random.choice(['GET', 'POST', 'PUT', 'DELETE'])
        endpoint = random.choice(['/api/users', '/api/orders', '/api/products'])
        status = random.choice(['200', '201', '400', '404', '500'])
        
        # 增加Counter
        REQUEST_COUNT.labels(method=method, endpoint=endpoint, status=status).inc()
        
        # 设置Gauge
        QUEUE_LENGTH.labels(queue_name='email').set(random.randint(0, 100))
        
        # 记录Histogram
        latency = random.uniform(0.01, 2.0)
        REQUEST_LATENCY.labels(method=method, endpoint=endpoint).observe(latency)
        
        time.sleep(1)

if __name__ == '__main__':
    # 启动HTTP服务器
    start_server(8000)
    print("Exporter started on port 8000")
    
    # 模拟请求
    simulate_requests()

2.3 Flask集成 #

python
from flask import Flask, Response
from prometheus_client import Counter, Histogram, generate_latest, CONTENT_TYPE_LATEST
import time

app = Flask(__name__)

# 创建指标
REQUEST_COUNT = Counter(
    'http_requests_total',
    'Total HTTP requests',
    ['method', 'endpoint', 'status']
)

REQUEST_LATENCY = Histogram(
    'http_request_duration_seconds',
    'HTTP request latency',
    ['method', 'endpoint']
)

@app.route('/metrics')
def metrics():
    return Response(generate_latest(), mimetype=CONTENT_TYPE_LATEST)

@app.route('/api/users')
def users():
    start_time = time.time()
    
    # 业务逻辑
    time.sleep(0.1)
    
    # 记录指标
    REQUEST_COUNT.labels(method='GET', endpoint='/api/users', status='200').inc()
    REQUEST_LATENCY.labels(method='GET', endpoint='/api/users').observe(time.time() - start_time)
    
    return {'users': []}

if __name__ == '__main__':
    app.run(port=5000)

三、Go客户端 #

3.1 安装 #

bash
go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promhttp

3.2 基本示例 #

go
package main

import (
    "math/rand"
    "net/http"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    requestCount = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total HTTP requests",
        },
        []string{"method", "endpoint", "status"},
    )

    queueLength = prometheus.NewGaugeVec(
        prometheus.GaugeOpts{
            Name: "queue_length",
            Help: "Current queue length",
        },
        []string{"queue_name"},
    )

    requestLatency = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request latency",
            Buckets: []float64{0.1, 0.5, 1.0, 2.0, 5.0, 10.0},
        },
        []string{"method", "endpoint"},
    )
)

func init() {
    prometheus.MustRegister(requestCount)
    prometheus.MustRegister(queueLength)
    prometheus.MustRegister(requestLatency)
}

func simulateMetrics() {
    for {
        methods := []string{"GET", "POST", "PUT", "DELETE"}
        endpoints := []string{"/api/users", "/api/orders", "/api/products"}
        statuses := []string{"200", "201", "400", "404", "500"}

        method := methods[rand.Intn(len(methods))]
        endpoint := endpoints[rand.Intn(len(endpoints))]
        status := statuses[rand.Intn(len(statuses))]

        requestCount.WithLabelValues(method, endpoint, status).Inc()
        queueLength.WithLabelValues("email").Set(float64(rand.Intn(100)))
        requestLatency.WithLabelValues(method, endpoint).Observe(rand.Float64() * 2)

        time.Sleep(time.Second)
    }
}

func main() {
    go simulateMetrics()

    http.Handle("/metrics", promhttp.Handler())
    http.ListenAndServe(":8000", nil)
}

3.3 HTTP中间件 #

go
package main

import (
    "net/http"
    "strconv"
    "time"

    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
    httpRequestsTotal = prometheus.NewCounterVec(
        prometheus.CounterOpts{
            Name: "http_requests_total",
            Help: "Total HTTP requests",
        },
        []string{"method", "path", "status"},
    )

    httpRequestDuration = prometheus.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "http_request_duration_seconds",
            Help:    "HTTP request duration",
            Buckets: []float64{0.1, 0.5, 1.0, 2.0, 5.0},
        },
        []string{"method", "path"},
    )
)

func init() {
    prometheus.MustRegister(httpRequestsTotal)
    prometheus.MustRegister(httpRequestDuration)
}

func prometheusMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()

        next.ServeHTTP(w, r)

        duration := time.Since(start).Seconds()
        status := strconv.Itoa(http.StatusOK)

        httpRequestsTotal.WithLabelValues(r.Method, r.URL.Path, status).Inc()
        httpRequestDuration.WithLabelValues(r.Method, r.URL.Path).Observe(duration)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte(`{"users": []}`))
    })
    mux.Handle("/metrics", promhttp.Handler())

    http.ListenAndServe(":5000", prometheusMiddleware(mux))
}

四、Java客户端 #

4.1 Maven依赖 #

xml
<dependency>
    <groupId>io.prometheus</groupId>
    <artifactId>simpleclient</artifactId>
    <version>0.16.0</version>
</dependency>
<dependency>
    <groupId>io.prometheus</groupId>
    <artifactId>simpleclient_httpserver</artifactId>
    <version>0.16.0</version>
</dependency>

4.2 基本示例 #

java
import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.Histogram;
import io.prometheus.client.exporter.HTTPServer;

public class ExporterExample {
    static final Counter requestCount = Counter.build()
            .name("http_requests_total")
            .help("Total HTTP requests")
            .labelNames("method", "endpoint", "status")
            .register();

    static final Gauge queueLength = Gauge.build()
            .name("queue_length")
            .help("Current queue length")
            .labelNames("queue_name")
            .register();

    static final Histogram requestLatency = Histogram.build()
            .name("http_request_duration_seconds")
            .help("HTTP request latency")
            .labelNames("method", "endpoint")
            .register();

    public static void main(String[] args) throws Exception {
        HTTPServer server = new HTTPServer(8000);

        while (true) {
            String method = "GET";
            String endpoint = "/api/users";
            String status = "200";

            requestCount.labels(method, endpoint, status).inc();
            queueLength.labels("email").set(Math.random() * 100);
            requestLatency.labels(method, endpoint).observe(Math.random() * 2);

            Thread.sleep(1000);
        }
    }
}

五、最佳实践 #

5.1 命名规范 #

text
指标命名规范:

┌─────────────────────────────────────────────┐
│ 1. 使用snake_case                           │
├─────────────────────────────────────────────┤
│ 好的:http_requests_total                   │
│ 不好的:httpRequestsTotal                   │
├─────────────────────────────────────────────┤
│ 2. 包含单位                                 │
├─────────────────────────────────────────────┤
│ 好的:http_request_duration_seconds         │
│ 不好的:http_request_duration               │
├─────────────────────────────────────────────┤
│ 3. Counter使用_total后缀                    │
├─────────────────────────────────────────────┤
│ 好的:http_requests_total                   │
│ 不好的:http_requests_count                 │
└─────────────────────────────────────────────┘

5.2 标签设计 #

text
标签设计原则:

┌─────────────────────────────────────────────┐
│ 1. 避免高基数                               │
├─────────────────────────────────────────────┤
│ 不要使用:user_id, request_id               │
│ 使用:method, status, endpoint              │
├─────────────────────────────────────────────┤
│ 2. 标签值有限                               │
├─────────────────────────────────────────────┤
│ 好的:status="200", method="GET"            │
│ 不好的:timestamp="1700000000"              │
├─────────────────────────────────────────────┤
│ 3. 保持一致性                               │
├─────────────────────────────────────────────┤
│ 所有服务使用相同的标签名                    │
└─────────────────────────────────────────────┘

六、总结 #

客户端库选择:

语言 客户端库
Go client_golang
Python client_python
Java client_java

开发要点:

要点 说明
命名规范 snake_case,包含单位
标签设计 避免高基数
指标类型 选择合适的类型

下一步,让我们学习告警管理!

最后更新:2026-03-27