解析PromQL

目前对Prometheus 的promQL 的解析文章比较少,且Prometheus官方也没有提供一个公共的库来对齐进行解析。下面实现对promQL的解析,并实现注入label功能。

表达式类型

AggregateExpr

对应聚合操作,如sum without (instance) (http_requests_total),定义可以查看Aggregation operators。源码定义在prometheus/promql/parser/lex.go

	// Aggregators.
"sum": SUM,
"avg": AVG,
"count": COUNT,
"min": MIN,
"max": MAX,
"group": GROUP,
"stddev": STDDEV,
"stdvar": STDVAR,
"topk": TOPK,
"bottomk": BOTTOMK,
"count_values": COUNT_VALUES,
"quantile": QUANTILE,

结构体如下,label位于AggregateExpr.Expr中:

type AggregateExpr struct {
Op ItemType // The used aggregation operation.
Expr Expr // The Vector expression over which is aggregated.
Param Expr // Parameter used by some aggregators.
Grouping []string // The labels by which to group the Vector.
Without bool // Whether to drop the given labels rather than keep them.
PosRange PositionRange
}

Call

对应函数调用,如absent(nonexistent{job="myjob"}),函数定义可以查看function。源码定义在Prometheus/promql/parser/function.go文件中,

// Functions is a list of all functions supported by PromQL, including their types.
var Functions = map[string]*Function{
"abs": {
Name: "abs",
ArgTypes: []ValueType{ValueTypeVector},
ReturnType: ValueTypeVector,
},
"absent": {
Name: "absent",
ArgTypes: []ValueType{ValueTypeVector},
ReturnType: ValueTypeVector,
},
"absent_over_time": {
Name: "absent_over_time",
ArgTypes: []ValueType{ValueTypeMatrix},
ReturnType: ValueTypeVector,
},
"avg_over_time": {
Name: "avg_over_time",
ArgTypes: []ValueType{ValueTypeMatrix},
ReturnType: ValueTypeVector,
},

结构体如下,label位于Call.Args中:

type Call struct {
Func *Function // The function that was called.
Args Expressions // Arguments used in the call. PosRange PositionRange
}

ParenExpr

圆括号表达式,即表达式外面加了圆括号,如(up)(3*1),一般集成在BinaryExpr中,如1 + 2/(3*1),但根据ParenExpr

的定义,(1 + 2/(3*1))又变成了ParenExpr

结构体如下,label位于ParenExpr.Expr中:

type ParenExpr struct {
Expr Expr
PosRange PositionRange
}

UnaryExpr

一元表达式,如-some_metric+some_metric-1^2UnaryExpr只适用于获取标量结果的表达式。

结构体如下,label位于UnaryExpr.Expr中:

type UnaryExpr struct {
Op ItemType
Expr Expr StartPos Pos
}

BinaryExpr

多元表达式,使用二元运算符组合成的表达式,被运算符分割的表达式被保存到LHS和RHS树中

结构体如下,label位于BinaryExpr.LHSBinaryExpr.RHS中:

type BinaryExpr struct {
Op ItemType // The operation of the expression.
LHS, RHS Expr // The operands on the respective sides of the operator. // The matching behavior for the operation if both operands are Vectors.
// If they are not this field is nil.
VectorMatching *VectorMatching // If a comparison operator, return 0/1 rather than filtering.
ReturnBool bool
}

NumberLiteral

数字表达式,如10xc5e-3,该类型的表达式与UnaryExpr类似,也是集成到其他类型的表达式中使用的,单独使用并没有意义。

结构体如下,无label

type NumberLiteral struct {
Val float64 PosRange PositionRange
}

StringLiteral

字符表达式,如"version",与NumberLiteral类似,一般会集成到其他表达式中,如count_values("version", build_version)

结构体如下,无label

type StringLiteral struct {
Val string
PosRange PositionRange
}

VectorSelector

瞬时向量,无需指定时间范围,如http_requests_total{job=~".*",method="get"}

结构体如下,label位于VectorSelector.LabelMatchers中:

type VectorSelector struct {
Name string
Offset time.Duration
LabelMatchers []*labels.Matcher // The unexpanded seriesSet populated at query preparation time.
UnexpandedSeriesSet storage.SeriesSet
Series []storage.Series PosRange PositionRange
}

MatrixSelector

区间向量,需指定时间范围,如http_requests_total[1m]{job=~".*",method="get"}[1m]

结构体如下,label位于MatrixSelector.LabelMatchers中:

type MatrixSelector struct {
Name string
Range time.Duration
Offset time.Duration
LabelMatchers []*labels.Matcher // The series are populated at query preparation time.
series []storage.Series
}

SubqueryExpr

子查询表达式,支持指定查询范围和精度,其实就是MatrixSelector加了精度功能。格式为<instant_query> '[' <range> ':' [<resolution>] ']',如rate(http_requests_total[5m])[30m:1m],官方定义参见Subquery

结构体如下,label位于SubqueryExpr.Expr中:

type SubqueryExpr struct {
Expr Expr
Range time.Duration
Offset time.Duration
Step time.Duration EndPos Pos
}

小结:一般日常中使用的表达式可以分为多元表达式和非多元表达式两种。多元表达式里面是被二元运算符分隔的非多元表达式。一般使用的非多元表达式有:AggregateExprCallVectorSelectorMatrixSelectorSubqueryExpr用的比较少

PromQl的解析

从上面分析可以看出,Prometheus的查询语句的基本类型为:NumberLiteralStringLiteralVectorSelectorMatrixSelector,前两个本身就没有任何标签,后两个有明确的结构体来保存标签。其他类型只是这四种基本类型的组合。

Prometheus源码的eval函数(位于Prometheus/promql/engine.go文件中)对分别不同类型的promQL进行了处理,可以参考此处代码。

解析代码如下:

func injectLabels(expr parser.Expr, match labels.MatchType, name,value string){
switch e := expr.(type) {
case *parser.AggregateExpr:
injectLabels(e.Expr,match,name,value)
case *parser.Call:
for _,v := range e.Args{
injectLabels(v,match,name,value)
}
case *parser.ParenExpr:
injectLabels(e.Expr,match,name,value)
case *parser.UnaryExpr:
injectLabels(e.Expr,match,name,value)
case *parser.BinaryExpr:
injectLabels(e.LHS,match,name,value)
injectLabels(e.RHS,match,name,value)
case *parser.VectorSelector:
l := genMetricLabel(match,name,value)
e.LabelMatchers = append(e.LabelMatchers, l)
return
case *parser.MatrixSelector:
injectLabels(e.VectorSelector,match,name,value)
case *parser.SubqueryExpr:
injectLabels(e.Expr,match,name,value)
case *parser.NumberLiteral,*parser.StringLiteral:
return
default:
panic(errors.Errorf("unhandled expression of type: %T", expr))
}
return
} func genMetricLabel(match labels.MatchType, name,value string) *labels.Matcher{
m,err := labels.NewMatcher(match,name,value)
if nil != err {
return nil
} return m
}

使用方式如下,在rate(http_requests_total[5m])[30m:1m]种注入"appName !~ testAppName"的表达式,输出结果为:rate(http_requests_total{appname!~"1111"}[5m])[30m:1m]

func main() {
ql := `rate(http_requests_total[5m])[30m:1m]`
expr,_ := parser.ParseExpr(ql)
injectLabels(expr,labels.MatchNotRegexp,"appName","testAppName")
fmt.Println(expr.String())
}

Prometheus支持如下四种匹配模式:

MatchEqual:     "=",
MatchNotEqual: "!=",
MatchRegexp: "=~",
MatchNotRegexp: "!~",

TIPs

  • 只有较高版本的Prometheus库才能支持解析SubqueryExpr,但直接通过go mod tidy命令(目前)只能获取到 v2.5.0版本,该版本并不支持解析SubqueryExpr。可以在GitHub上找到对应版本的tag,然后执行go get github.com/prometheus/prometheus@${commitId}来获得该版本。需要注意的是执行之后go.mod中的Prometheus版本为v1.8.2,可以忽略此版本标记。具体参见该issue

解析Prometheus PromQL的更多相关文章

  1. prometheus学习系列七: Prometheus promQL查询语言

    Prometheus promQL查询语言 Prometheus提供了一种名为PromQL (Prometheus查询语言)的函数式查询语言,允许用户实时选择和聚合时间序列数据.表达式的结果既可以显示 ...

  2. Prometheus PromQL 基础

    目录 时序 4 种类型 Counter Gauge Histogram Summary Histogram vs Summary 操作符 时序 4 种类型 Prometheus 时序数据分为 Coun ...

  3. Prometheus学习系列(七)之Prometheus PromQL说明

    前言 本文来自Prometheus官网手册1.2.3 和 Prometheus简介1.2.3 PromQL操作符 一.二元操作符 Prometheus的查询语言支持基本的逻辑运算和算术运算.对于两个瞬 ...

  4. Prometheus PromQL 简单用法

    目录 说明 CPU 内存 磁盘监控 磁盘空间利用率百分比 预计饱和 说明 基于上一篇文章的基础,这里做一些关于 CPU.内存.磁盘的一些基础查询语句. CPU 通过查询 metric值为 node_c ...

  5. prometheus(7)之数据类型与PromQL语法

    Prometheus的四种数据类型 counter (只增不减 计数) Gauge (常规数值 可变化大小) histogram (柱状图 小于上边界的 总数与次数) summary (分位数  小于 ...

  6. Prometheus 【目录】

    正在陆续更新,内容大体包括: rule.标签重置.cAdversior.自动发现(File 自动发现.DNS自动发现.k8s环境自动发现)等... 目录: prometheus[第一篇] Promet ...

  7. Prometheus + Grafana 部署说明之「安装」

    说明 在前面的Prometheus学习系列文章里,大致介绍说明了Prometheus和Grafana的一些使用,现在开始介绍如何从头开始部署Prometheus+Grafana,来监控各个相关的指标数 ...

  8. Kubernetes 监控--Prometheus

    在早期的版本中 Kubernetes 提供了 heapster.influxDB.grafana 的组合来监控系统,在现在的版本中已经移除掉了 heapster,现在更加流行的监控工具是 Promet ...

  9. K8s容器资源限制

    在K8s中定义Pod中运行容器有两个维度的限制: 1. 资源需求:即运行Pod的节点必须满足运行Pod的最基本需求才能运行Pod. 如: Pod运行至少需要2G内存,1核CPU    2. 资源限额: ...

随机推荐

  1. Leetcode_丑数问题

    1. 263. 丑数 给你一个整数 n ,请你判断 n 是否为 丑数 .如果是,返回 true :否则,返回 false . 丑数 就是只包含质因数 2.3 或 5 的正整数. 示例 1: 输入:n ...

  2. 作为有经验的程序员如果不懂Lambda表达式就说不过去了吧,建议收藏!!!

      最近刚好有空给大家整理下JDK8的特性,这个在实际开发中的作用也是越来越重了,本文重点讲解下Lambda表达式 Lambda表达式   Lambda 表达式,也可称为闭包,它是推动 Java 8 ...

  3. Java的几种创建实例方法的性能对比(二)

    上一篇里对几种书写方式进行了简单的测试,得出了一些初步的结论.这次简单了解Lambda原理后,对测试做了一些调整,发现得到不一样的结果,而这个调整,明显更契合实际开发的场景. 暂时还没有亲自去验证,主 ...

  4. Docker部署ELK之部署kibana7.6.0(2)

    1. 拉取kibana镜像 sudo docker pull kibana:7.6.0 2. 输入命令构建kibana容器,关于挂载kibana配置文件的问题,也可以先构建一个容器,然后把配置文件co ...

  5. HttpClient调用doGet、doPost、JSON传参及获得返回值

    调用 doPost:map传参 Map<String,Object> map = new HashMap<>(); map.put("test"," ...

  6. ARM—异常中断处理

    文章目录 一. ARM程序执行流程 二. ARM工作模式 三. ARM中异常中断的种类 四. 异常中断向量表及优先级 五. 异常中断用到的寄存器 六. ARM进入处理异常中断时的操作 七. ARM退出 ...

  7. SpringBoot 整合 SpringSecurity 梳理

    文档 Spring Security Reference SpringBoot+SpringSecurity+jwt整合及初体验 JSON Web Token 入门教程 - 阮一峰 JWT 官网 Sp ...

  8. Spring系列之HikariCP连接池

    上两篇文章,我们讲到了Spring中如何配置单数据源和多数据源,配置数据源的时候,连接池有很多选择,在SpringBoot 1.0中使用的是Tomcat的DataSource,在SpringBoot ...

  9. NOIP 模拟 $28\; \rm 割海成路之日$

    题解 \(by\;zj\varphi\) 用两个集合分别表示 \(1\) 边联通块,\(1,2\) 边联通块 . \(\rm son_x\) 表示当前节点通过 \(3\) 类边能到的 \(2\) 联通 ...

  10. 题解 数列 及exgcd总结

    传送门 自闭了--考场上exgcd打错然后对着屏幕自闭了一个小时不知道它为什么解得不对 开始恶补: 对于方程 \(a*x+b*y=c\) ,就等价于 \(a*x \equiv c\pmod{b}\) ...