zoukankan      html  css  js  c++  java
  • 解析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
  • 相关阅读:
    读写文件
    c++ 中的 -> :: : .
    CDH安装步骤
    MySQL安全配置向导mysql_secure_installation详解
    Linux下彻底卸载mysql详解
    安装mysql警告: Header V3 DSA/SHA1 Signature, key ID 5072e1f5: NOKEY
    为Hadoop集群选择合适的硬件配置
    利用cm压缩包手动安装cm和cdh
    CM5.11与CDH5.11安装使用说明
    法的本体
  • 原文地址:https://www.cnblogs.com/charlieroro/p/15261026.html
Copyright © 2011-2022 走看看