4.2.1 匹配器
匹配器是作用于标签上的,标签匹配器可以对时间序列进行过滤,Prometheus支持完全匹配和正则匹配两种模式。
1.相等匹配器(=)
相等匹配器(Equality Matcher),用于选择与提供的字符串完全相同的标签。下面介绍的例子中就会使用相等匹配器按照条件进行一系列过滤。
http_requests_total{job="HelloWorld",status="200",method="POST",handler="/api/ comments"}
需要注意的是,如果标签为空或者不存在,那么也可以使用Label=""的形式。对于不存在的标签,比如demo标签,图4-7所示的go_gc_duration_seconds_count和图4-8所示的go_gc_duration_seconds_count{demo=""}效果其实是一样的。
图4-7 go_gc_duration_seconds_count
图4-8 go_gc_duration_seconds_count{demo=""}
2.不相等匹配器(!=)
不相等匹配器(Negative Equality Matcher),用于选择与提供的字符串不相同的标签。它和相等匹配器是完全相反的。举个例子,如果想要查看job并不是HelloWorld的HTTP请求总数,可以使用如下不相等匹配器。
http_requests_total{job!="HelloWorld"}
3.正则表达式匹配器(=~)
正则表达式匹配器(Regular Expression Matcher),用于选择与提供的字符串进行正则运算后所得结果相匹配的标签。Prometheus的正则运算是强指定的,比如正则表达式a只会匹配到字符串a,而并不会匹配到ab或者ba或者abc。如果你不想使用这样的强指定功能,可以在正则表达式的前面或者后面加上“.*”。比如下面的例子表示job是所有以Hello开头的HTTP请求总数。
http_requests_total{job=~"Hello.*"}
http_requests_total直接等效于{__name__="http_requests_total"},后者也可以使用和前者一样的4种匹配器(=,!=,=~,!~)。比如下面的案例可以表示所有以Hello开头的指标。
{__name__=~"Hello.*"}
如果想要查看job是以Hello开头的,且在生产(prod)、测试(test)、预发布(pre)等环境下响应结果不是200的HTTP请求总数,可以使用这样的方式进行查询。
http_requests_total{job=~"Hello.*",env=~"prod|test|pre",code!="200"}
由于所有的PromQL表达式必须至少包含一个指标名称,或者至少有一个不会匹配到空字符串的标签过滤器,因此结合Prometheus官方文档,可以梳理出如下非法示例。
{job=~".*"} # 非法! {job=""} # 非法! {job!=""} # 非法!
相反,如下表达式是合法的。
{job=~".+"} # 合法!.+表示至少一个字符 {job=~".*",method="get"} # 合法!.*表示任意一个字符 {job="",method="post"} # 合法!存在一个非空匹配 {job=~".+",method="post"} # 合法!存在一个非空匹配
4.正则表达式相反匹配器(!~)
正则表达式相反匹配器(Negative Regular Expression Matcher),用于选择与提供的字符串进行正则运算后所得结果不匹配的标签。因为PromQL的正则表达式基于RE2的语法,但是RE2不支持向前不匹配表达式,所以!~的出现是作为一种替代方案,以实现基于正则表达式排除指定标签值的功能。在一个选择器当中,可以针对同一个标签来使用多个匹配器。比如下面的例子,可以实现查找job名是node且安装在/prometheus目录下,但是并不在/prometheus/user目录下的所有文件系统并确定其大小。
node_filesystem_size_bytes{job="node",mountpoint=~"/prometheus/.*", mountpoint!~ "/prometheus/user/.*"}
PromQL采用的是RE2[1]引擎,支持正则表达式。RE2来源于Go语言,它被设计为一种线性时间的模式,非常适合用于PromQL这种时间序列的方式。但是就像我们前文描述的RE2那样,其不支持向前不匹配表达式(向前断言),也不支持反向引用,同时还缺失很多高级特性。
思考拓展
=、!=、=~、!~这4个匹配器在实战中非常有用,但是如果频繁为标签施加正则匹配器,比如HTTP状态码有1xx、2xx、3xx、4xx、5xx,在统计所有返回值是5xx的HTTP请求时,PromQL语句就会变成http_requests_total{job="HelloWorld",status=~"500",status=~"501",status=~"502",status=~"503",status=~"504",status=~"505",status=~"506"…}
但是,我们都知道5xx代表服务器错误,这些状态代码表示服务器在尝试处理请求时发生了内部错误。这些错误可能来自服务器本身,而不是请求。
1)500:服务器遇到错误,无法完成请求(服务器内部错误)。
2)501:服务器不具备完成请求的功能。例如,当服务器无法识别请求方法时可能会返回此代码(尚未实施)。
3)502:服务器作为网关或代理,从上游服务器收到无效响应(错误网关)。
4)503:服务器目前无法使用(由于超载或停机维护),通常只是暂时状态(服务不可用)。
5)504:服务器作为网关或代理,但是没有及时从上游服务器收到请求(网关超时)。
6)505:服务器不支持请求中所用的HTTP协议版本(HTTP版本不受支持)。
7)506:由《透明内容协商协议》(RFC 2295)扩展而来,代表服务器存在内部配置错误。
8)507:服务器无法存储完成请求所必需的内容。这个状况被认为是临时的。
9)509:服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用。
10)510:获取资源所需要的策略并没有被满足。
为了消除这样的错误,可以进行如下优化。
优化一 多个表达式之间使用“|”进行分割:http_requests_total{job="HelloWorld",status=~"500|501|502|503|504|505|506|507|509|510"}。
优化二 将这些返回值包装为5xx,这样就可以直接使用正则表达式匹配器对http_requests_total{job="HelloWorld",status=~"5xx"}进行优化。
优化三 如果要选择不以4xx开头的所有HTTP状态码,可以使用http_requests_total{status!~"4.."}。
[1] RE2:https://github.com/google/re2/wiki/Syntax。