编写通过命令行终端获取指定时间范围的 Prometheus 查询工具

编写通过命令行终端获取指定时间范围的 Prometheus 查询工具

前言:

当我们有数据查询需求时,可以通过查询 API 请求监控数据。

在公司中调用Prometheus 查询较为频繁,但是有个问题就是 Prometheus 时间戳默认调用的时 UTC (协调世界时)相对国内的时间就需要 -8 小时,所以不是特别方便,并且传统的查询得到的数据也比较复杂,所以需要通过工具将数据和时间进行处理

GitHub 项目地址:https://github.com/As9530272755/Tool-set/tree/v0.2/promctl

1 API 查询接口

GET /api/v1/query
POST /api/v1/query

查询参数

query=<string>: Prometheus:查询表达式。

time=<rfc3339 | unix_timestamp>: 时间戳, 可选。

timeout=<duration>:检测超时时间, 可选。 默认由 -query.timeout 参数指定。

简单查询示例:

我们可以使用下面的例子进行 API 数据查询,查询服务地址和认证信息可以在相应实例控制台查看:

curl -u "appid:token" 'http://IP:PORT/api/v1/query?query=up'

如果返回状态码为 401 请检查认证信息是否正确。

< HTTP/1.1 401 Unauthorized
< Content-Length: 0

范围查询:

GET /api/v1/query_range
POST /api/v1/query_range

根据时间范围查询我们需要的数据是我们面临的最多的场景,这时我们需要用到 /api/v1/query_range 接口,示例如下:

# curl -G "http://10.0.0.140:9090/api/v1/query_range?query=node_memory_Active_anon_bytes/1024/1024/1024&start=2023-08-17T09:20:30.781Z&end=2023-08-17T09:25:00.781Z&step=1m"
{"status":"success","data":{"resultType":"matrix","result":[{"metric":{"instance":"10.0.0.140:9100","job":"node"},"values":[[1692264030.781,"0.28753662109375"],[1692264090.781,"0.2889556884765625"],[1692264150.781,"0.28792572021484375"],[1692264210.781,"0.2896842956542969"],[1692264270.781,"0.318145751953125"]]}]}}

# api 解释:
# http://10.0.0.140:9090/api/v1/query_range? 通过时间做区间的范围查询 API
# query=node_memory_Active_anon_bytes/1024/1024/1024 promQL语句这里是查询内存并以 G 为单位
# &start=2023-08-17T09:20:30.781Z&end=2023-08-17T09:25:00.781Z 起始时间和结束时间,记得加 8 小时才是国内的正确时间
# &step=1m 以每分钟为计步做查询

注意:

在 Prometheus 时间查询中,.781Z 是一个表示时间戳的字符串。这个字符串遵循 ISO 8601 格式,在末尾的 "Z" 表示 UTC(协调世界时)。而前面的 ".781" 表示毫秒部分,表示时间戳的小数部分,表示某个时间点的具体毫秒数。因此,.781Z 表示一个 UTC 时间的时间戳,精确到毫秒。

2 代码实现:

这里我只写一段示例代码其他功能以此类推将上述代码保存为 main.go 文件,并替换代码中的 http://prometheus-server:9090 为你实际的 Prometheus 服务器地址,以及 your_query_expression 为你的查询表达式。

package controller

import (
    "context"
    "demo3/config"
    "demo3/logs"
    "fmt"
    "github.com/360EntSecGroup-Skylar/excelize"
    api "github.com/prometheus/client_golang/api"
    v1 "github.com/prometheus/client_golang/api/prometheus/v1"
    "github.com/prometheus/common/model"
    "log"
    "os"
    "strconv"
    "time"
)

// 基于起始时间和结束时间做全量查询
func AllQuery(allquery, allstartTime, allendTime string) {
    layout := "2006-01-02 15:04:05"

    // 上海时区
    loc, err := time.LoadLocation("Asia/Shanghai")
    if err != nil {
        fmt.Println("无效的时区")
        os.Exit(1)
    }

    // 时区转换
    start, _ := time.ParseInLocation(layout, allstartTime, loc)
    end, _ := time.ParseInLocation(layout, allendTime, loc)

    // 读取配置文件中的地址
    API, err := config.ParseConfig()
    if err != nil {
        log.Panic(err)
    }

    // Prometheus API 拼接
    promURL := API.Prometheus.Addr + ":" + API.Prometheus.Port

    fmt.Println(start, end)
    fmt.Println(promURL)

    // 创建 Prometheus 客户端配置
    cfg := api.Config{
        Address:      promURL, // 替换为 Prometheus 服务器地址
        RoundTripper: api.DefaultRoundTripper,
    }

    // 创建 Prometheus 客户端
    client, err := api.NewClient(cfg)
    if err != nil {
        log.Fatal(err)
    }

    // 创建 Prometheus API
    Papi := v1.NewAPI(client)

    // 执行查询
    result, warnings, err := Papi.QueryRange(context.Background(), allquery, v1.Range{
        Start: start,
        End:   end,
        Step:  time.Second * 30, // 计步 30s 做一次间隔
    })
    if err != nil {
        log.Fatal(err)
    }
    if len(warnings) > 0 {
        log.Printf("Warnings: %v", warnings)
    }
    if result.Type() != model.ValMatrix {
        log.Fatalf("Expected a matrix result, got %v", result.Type())
    }

    // 创建一个新的Excel文件
    f := excelize.NewFile()
    // 设置表头
    // 设置单元格A1查询时间
    f.SetCellValue("Sheet1", "A1", "查询时间")
    // 设置单元格B1查询语句
    f.SetCellValue("Sheet1", "B1", "查询语句")
    // 设置单元格C1查询数据
    f.SetCellValue("Sheet1", "C1", "查询数据")

    // 设置表列 index
    index := 0

    // 处理查询结果
    matrix := result.(model.Matrix)
    if matrix.Len() == 0 {
        fmt.Println("注意:当前查询时间段指标没有数据!")
        return
    } else {
        for _, sample := range matrix {
            if len(sample.Values) > 0 {
                value := sample.Values[len(sample.Values)-1].Value
                fmt.Println("指标:", len(sample.Values))
                fmt.Printf("查询时间:%v\n查询指标:%s\n查询数据:%s\n", start, sample.Metric, value)

                //日志调用
                logs.WithFields(sample.Metric, value)
            }

            // 每循环一次表列 +1
            index += 1

            // 数据指标从第二列开始写入,并转换数据类型
            sindex := strconv.Itoa(index + 1)
            fmt.Println("查询指标条数:", sindex)
            // 查询时间
            f.SetCellValue("Sheet1", "A"+sindex, start)
            // 设置单元格的值
            f.SetCellValue("Sheet1", "B"+sindex, sample.Metric)
            // 设置单元格的值
            f.SetCellValue("Sheet1", "C"+sindex, sample.Values[len(sample.Values)-1].Value)
        }
    }
    // 保存文件
    err = f.SaveAs("Prometheus查询.xlsx")
    if err != nil {
        fmt.Println("保存文件出错:", err)
        return
    }

    fmt.Println("Prometheus查询.xlsx Excel 文件创建并保存成功。")
}

在代码中,sample.Values 是一个时间序列的值列表(按照时间排序),我们显示最新的值,因此使用了 sample.Values[len(sample.Values)-1]。你可以根据查询结果的数据结构和需要进行调整,例如选择特定时间点的值或使用其他方式访问值字段。最后,使用 fmt.Println() 函数将值打印出来。

请注意,示例代码仅打印出值字段,并没有处理其他有关样本的信息。如果你需要更多关于样本的信息,可以查看 model.SampleValue 结构体的其他字段,并根据需要进行处理。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇