编写通过命令行终端获取指定时间范围的 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
结构体的其他字段,并根据需要进行处理。