解析Prometheus PromQL

本文涉及的产品
EMR Serverless StarRocks,5000CU*H 48000GB*H
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
可观测监控 Prometheus 版,每月50GB免费额度
简介: 解析Prometheus PromQL

解析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进行了处理,可以参考此处代码。


解析代码如下:

import (
  "fmt"
  "github.com/pkg/errors"
  "github.com/prometheus/prometheus/promql/parser"
  "github.com/prometheus/prometheus/pkg/labels"
)
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!~"testAppName"}[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
目录
相关文章
|
10月前
|
Prometheus 监控 Cloud Native
Prometheus PromQL语法
Prometheus PromQL语法
309 0
|
13天前
|
存储 Prometheus Cloud Native
prometheus学习笔记之PromQL
prometheus学习笔记之PromQL
|
13天前
|
Prometheus 监控 Cloud Native
Prometheus 查询语言(PromQL):深入解析
【8月更文第29天】Prometheus 是一款开源的监控系统和时间序列数据库,广泛应用于各种系统的监控和告警。PromQL(Prometheus Query Language)是 Prometheus 用来查询和聚合时间序列数据的一种强大语言。本文将详细介绍 PromQL 的功能和语法,包括基本查询、向量操作、聚合函数等,并提供具体的代码示例。
19 2
|
2月前
|
存储 Prometheus Cloud Native
SLS Prometheus存储问题之为什么SLS时序引擎最终选择了使用C++实现PromQL的部分算子
SLS Prometheus存储问题之为什么SLS时序引擎最终选择了使用C++实现PromQL的部分算子
|
2月前
|
存储 Prometheus 监控
深入解析Prometheus监控技术
7月更文挑战第16天
29 1
|
4月前
|
存储 Prometheus Cloud Native
「译文」Prometheus 查询语言 PromQL 简介
「译文」Prometheus 查询语言 PromQL 简介
|
存储 Prometheus 自然语言处理
听GPT 讲Prometheus源代码--promql/promdb
听GPT 讲Prometheus源代码--promql/promdb
115 0
|
存储 Prometheus Kubernetes
对比开源丨Prometheus 服务多场景存储压测全解析
作为国内领先的云服务提供商,阿里云提供了优秀的可观测全套解决方案,阿里云 Prometheus 服务正是其中重要一环,相比于开源版本 Prometheus,阿里云的 Prometheus 服务无论是易用性、扩展性、性能均有大幅度提升。
对比开源丨Prometheus 服务多场景存储压测全解析
|
Prometheus 监控 Cloud Native
【云原生监控系列第二篇】Prometheus进阶——PromQL数据类型
【云原生监控系列第二篇】Prometheus进阶——PromQL数据类型
352 0
【云原生监控系列第二篇】Prometheus进阶——PromQL数据类型
|
Prometheus 监控 Cloud Native
Prometheus:Java调用PromQL工具类(瞬时数据查询)
Prometheus:Java调用PromQL工具类(瞬时数据查询)
1424 0