metrics

package
v1.1.20 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2024 License: MIT Imports: 8 Imported by: 0

README

Prometheuse 指标体系构建

任何一个服务需要有完整有效的指标体系来做自观测,本文说明如何使用 Prometheuse 方式来构建服务的指标体系。以下以 Datakit 为例来做说明。

如何给模块增加自身指标

对 Datakit 而言,自身的指标有几大类:

  • 计数:某些操作的执行次数(如日志文件 rotate 次数)、某些物理量的计数(如字节数)
  • 耗时:某些操作的耗时
  • 报错信息:当某个模块报错时,也能通过 Prometheuse 方式来暴露,这种有点像日志。可以直接使用全局 metrics 模块提供的接口

计数(counter)

对于计数性质的指标,一般通过如下方式构建:

dwAPIVec = prometheus.NewCounterVec(
    prometheus.CounterOpts{
        Namespace : "datakit", // 所有 Datakit 指标,都统一用这个 Namespace
        Subsystem : "dataway", // 视具体模块而定,比如这里的 dataway 指 io 中的 dataway 模块
        Name      : "api_total", // 具体的指标名,比如这里指 HTTP API 的发送次数(当前是一个 counter)

        // 这样之后,就能看到一个完整的指标名:datakit_dataway_api_total

        Help: "The dataway API request count", // 该指标的说明文档
    },
    []string{"api", "status"}, // 可能的 label(tag) 名称
)

// 将该指标注册到 datakit 全局指标体系中
// NOTE: 注意,不要注册同样的指标名,不然这里会崩溃
metrics.MustRegister(dwAPIVec)

注意这里的 label 列表,对 dataway HTTP 请求而言,除了区分不同的 API 调用,每个 API 请求有不同的结果状态,故需要将状态这个维度添加进去,以区分不同结果状态下的指标。

注册完成后,在该模块代码中,就能无脑使用了:

// 请求 /v1/write/metric 成功
dwAPIVec.WithLabelValues("/v1/write/metric", "Status OK").Inc()

注意:

  • WithLabelValues 的时候,label 值的顺序,必须跟注册该指标时指定的 label 列表顺序一致,不然会导致数据错乱
  • WithLabelValues 中的 label 个数不能少于注册时的个数,否则会崩溃

在最终的 /metrics 接口返回中,能看到类似如下的返回:

# HELP datakit_dataway_api_total The dataway API request count
# TYPE datakit_dataway_api_total counter
datakit_dataway_api_total{api="/v1/write/metric",status="Status OK"} 169

计量(counter)

计量用于表示一些忽高忽低的指标,比如温度、CPU 使用率等,它不是单调递增的,

概要(summary)

概要用来表示一种自启动以来的总次数和总量之间的关系,比如网络请求总耗时和总次数,API 调用的总 Body 大小和次数,它自带两个字段:

  • Sample Count:表示总次数
  • Sample Sum:表示总量

比如,以 HTTP 请求为例,一个指标维度就是总的请求次数和总的请求耗时,即可组成一个 summary 指标:

httpLatencyVec = prometheuse.NewSummaryVec(
    prometheus.SummaryOpts{
        Namespace : "datakit",
        Subsystem : "http",

        Name : "api_cost_seconds", // 具体的指标名
        Help : "API request cost",
    },
    []string{"api", "status"}, // 这里的维度跟上面的基本一致
)

summary 可以这么用:

httpLatencyVec.WithLabelValues("/v1/write/metric", "Status OK").Observe(float64(time.Since(start))/float64(time.Second))

在最终的 /metrics 接口返回中,能看到类似如下的返回:

# HELP datakit_dataway_api_latency Dataway API request latency(ms)
# TYPE datakit_dataway_api_latency summary
datakit_http_api_cost_seconds_sum{api="/v1/write/metric",status="Status OK"}"} 3.1415926
datakit_http_api_cost_seconds_count{api="/v1/write/metric",status="Status OK"}"} 42

它表示「在 API /v1/write/metric 上总共有 42 次请求,总的请求耗时为 3.1415926 秒」,通过简单的除法,我们即可知道该 API 上的平均耗时。

概要的百分位

上面的方式只能计算平均值,但是我们可以在 summary 中设置一定的百分位,来获取最近一段时间的数据:

httpLatencyVec = prometheuse.NewSummaryVec(
    prometheus.SummaryOpts{
        Namespace : "datakit",
        Subsystem : "http",

        Name : "api_cost_seconds", // 具体的指标名
        Help : "API request cost",

        Objectives: map[float64][float64] {
            0.5:  0.05,
            0.75: 0.0075,
            0.95: 0.005,
        },
        MaxAge: 10 * time.Minute,
        AgeBuckets: 5,
    },
    []string{"api", "status"}, // 这里的维度跟上面的基本一致
)

这样,我们就能获取最近 10min 每个 API 上几个百分位(P50/P75/P95)的响应情况:

# HELP datakit_dataway_api_latency Dataway API request latency(ms)
# TYPE datakit_dataway_api_latency summary
datakit_http_api_cost_seconds{api="/v1/write/metric",status="Status OK",quantile="0.5"} 1.002858834
datakit_http_api_cost_seconds{api="/v1/write/metric",status="Status OK",quantile="0.75"} 1.002858834
datakit_http_api_cost_seconds{api="/v1/write/metric",status="Status OK",quantile="0.95"} 1.002858834
datakit_http_api_cost_seconds_sum{api="/v1/write/metric",status="Status OK"}"} 3.1415926
datakit_http_api_cost_seconds_count{api="/v1/write/metric",status="Status OK"}"} 42

指标命名规范

Prometheus 指标有自身的命名规范,参见官方文档

报错

报错信息的处理,还需进一步考虑一下,暂时不建议使用。

AddLastErr("my-module", "I got some error message")

在最终的 /metrics 接口返回中,能看到类似如下的返回:

# HELP datakit_lasterr Datakit internal errors(with error occurred unix timestamp)
# TYPE datakit_lasterr gauge
datakit_lasterr{message="I got some error message",source="my-module"} 1.678531768e+09

测试指标效果

如果如下代码,可以直接在自己的代码中获取 /metrics 接口返回的数据效果:


import (
    "github.com/prometheus/common/expfmt"
    "github.com/prometheus/client_golang/prometheus"
    "github.com/GuanceCloud/cliutils/metrics"
)

apiLatencyVec = prometheuse.NewSummaryVec(
    prometheus.SummaryOpts{
        Namespace : "datakit",
        Subsystem : "dataway",

        Name : "api_cost_seconds",
        Help : "Dataway API request latency",
    },
    []string{"api", "status"},
)

// 构建一个 registry
reg := prometheus.NewRegistry()
reg.MustRegister(apiLatencyVec)

// 塞进去一个指标
apiLatencyVec.WithLabelValues("/v1/write/metric", "Status OK").Observe(float64(time.Since(start))/float64(time.Second))

// 获取 reg 上所有指标
mfs, err := reg.Gather()

// 此处即可看到 /metrics 接口返回的效果
fmt.Println(metrics.MetricFamily2Text(mfs))

使用用例

参见 diskcache 的指标暴露实现

Documentation

Overview

Package metrics implements datakit's Prometheus metrics

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Gather

func Gather() ([]*dto.MetricFamily, error)

Gather collect all metrics within global registry.

func GetMetric

func GetMetric(mfs []*dto.MetricFamily, name string, idx int) *dto.Metric

GetMetric with specific idx.

func GetMetricOnLabels

func GetMetricOnLabels(mfs []*dto.MetricFamily, name string, wanted ...string) *dto.Metric

GetMetricOnLabels search mfs with wanted labels. wanted values order must be same as label names.

func HTTPGinHandler

func HTTPGinHandler(opt promhttp.HandlerOpts) gin.HandlerFunc

HTTPGinHandler wrap promhttp handler as gin hander. We can attach url /metrics to a exist gin router like this:

router := gin.New()
router.GET("/metrics", HTTPGinHandler(promhttp.HandlerOpts{}))

func MetricFamily2Text

func MetricFamily2Text(mfs []*dto.MetricFamily) string

MetricFamily2Text convert metrics to text format.

func MustAddGolangMetrics

func MustAddGolangMetrics()

MustAddGolangMetrics enable Golang runtime metrics.

func MustGather

func MustGather() []*dto.MetricFamily

MustGather collect all metrics within global registry and panic on any error.

func MustRegister

func MustRegister(c ...prometheus.Collector)

MustRegister add c to global registry and panic on any error.

func Register

func Register(c prometheus.Collector) error

Register add c to global registry.

func Unregister

func Unregister(c prometheus.Collector) bool

Unregister remove c from global registry.

Types

type MetricServer

type MetricServer struct {
	// Metrics request path.
	URL string `toml:"url" json:"url"`

	// HTTP server address, default to localhost:9090.
	Listen string `toml:"listen" json:"listen"`

	// Enable or disable the http server.
	Enable bool `toml:"enable" json:"enable"`

	// Enable or disable Golang related metrics in metrics URL.
	DisableGoMetrics bool `toml:"disable_go_metrics" json:"disable_go_metrics"`
}

MetricServer used to export metrics via HTTP /metrics request.

func NewMetricServer

func NewMetricServer() *MetricServer

NewMetricServer create default metric server.

func (*MetricServer) Start

func (ms *MetricServer) Start() error

Start create HTTP server to serving /metrics request.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL