解析PromQL并修改添加Label

最近做的项目中用到了Prometheus做预警服务,其中Prometheus使用promql语言来查询。项目中用户通过UI或者自己手动输入PromQL时候是缺少一些系统参数的,所以需要在用户输入完成以后同步到Prometheus时候将这部分缺失的信息给添加回去,这里就需要修改用户写的PromQL了。

实现思路是通过Antlr4来解析PromQL并修改。

https://github.com/antlr/grammars-v4

在这里插入图片描述

上述url是antlr官方提供的各个语言的语法定义文件,其中就包含我需要PromQL,将上述代码中的promql包中的两个g4文件拷贝到自己项目中,我对拷贝的PromQLLexer.g4文件中的最后的空格处做了处理改成如下内容否则重写以后会丢失原语句中的空格。

WS: [\r\t\n ]+ -> channel(HIDDEN);

新建maven项目,此处我就叫promql-parser。

pom部分内容如下:

    <properties><antlr4.version>4.13.1</antlr4.version></properties><dependencies><dependency><groupId>org.antlr</groupId><artifactId>antlr4-runtime</artifactId><version>${antlr4.version}</version></dependency></dependencies><build><plugins><plugin><groupId>org.antlr</groupId><artifactId>antlr4-maven-plugin</artifactId><version>${antlr4.version}</version><executions><execution><phase>generate-sources</phase><goals><goal>antlr4</goal></goals></execution></executions><configuration><visitor>true</visitor><sourceDirectory>src/main/antlr4/com/swan</sourceDirectory><outputDirectory>src/main/java/com/swan</outputDirectory></configuration></plugin></plugins></build>

在这里插入图片描述

将g4文件放置到项目的src/main/antlr4/packages中,这样后续生成的代码也自动会生成到src/main/java/packages中,`替换为自己的java包名,如下图:

在这里插入图片描述

执行生成代码:
在这里插入图片描述
生成的java代码,没有产生包名,需要手动修改一下代码,把包路径添加上。

现在开始编写解析PromQL并修改PromQL的代码:

public class ParserUtil {public static String addLabels(String promQL, Map<String, String> labels) {PromQLLexer lexer = new PromQLLexer(CharStreams.fromString(promQL));CommonTokenStream tokenStream = new CommonTokenStream(lexer);final TokenStreamRewriter rewriter = new TokenStreamRewriter(tokenStream);PromQLParser parser = new PromQLParser(tokenStream);ParseTreeWalker.DEFAULT.walk(new LabelMatcherListener(rewriter, labels), parser.expression());return rewriter.getText();}private static class LabelMatcherListener extends PromQLParserBaseListener {private final TokenStreamRewriter rewriter;private final Map<String, String> labels;public LabelMatcherListener(TokenStreamRewriter rewriter, Map<String, String> labels) {this.rewriter = rewriter;this.labels = new LinkedHashMap<>(labels);}@Overridepublic void enterInstantSelector(PromQLParser.InstantSelectorContext ctx) {if (labels.isEmpty()) {return;}PromQLParser.LabelMatcherListContext matcherListContext = ctx.labelMatcherList();if (null == matcherListContext) {if (null == ctx.LEFT_BRACE()) {rewriter.insertAfter(ctx.METRIC_NAME().getSymbol(),labels.entrySet().stream().map(e -> String.format("%s=\"%s\"", e.getKey(), e.getValue())).collect(Collectors.joining(",", "{", "}")));} else {rewriter.insertAfter(ctx.LEFT_BRACE().getSymbol(),labels.entrySet().stream().map(e -> String.format("%s=\"%s\"", e.getKey(), e.getValue())).collect(Collectors.joining(",")));}} else {for (PromQLParser.LabelMatcherContext matcherContext :matcherListContext.labelMatcher()) {String labelName = matcherContext.labelName().getText();if (labels.containsKey(labelName)) {rewriter.replace(matcherContext.STRING().getSymbol(),String.format("\"%s\"", labels.get(labelName)));labels.remove(labelName);}}if (!labels.isEmpty()) {rewriter.insertBefore(ctx.RIGHT_BRACE().getSymbol(),labels.entrySet().stream().map(e -> String.format("%s=\"%s\"", e.getKey(), e.getValue())).collect(Collectors.joining(",", ",", "")));}}}}
}

上述代码对应的单元测试代码:

public class ParserUtilTest {@Testpublic void test1() {Assertions.assertEquals("hello{a=\"b\"}",ParserUtil.addLabels("hello", Collections.singletonMap("a", "b")));}@Testpublic void test1_1() {HashMap<String, String> map = new LinkedHashMap<>();map.put("a", "b");map.put("c", "d");Assertions.assertEquals("hello{a=\"b\",c=\"d\"}", ParserUtil.addLabels("hello", map));}@Testpublic void test2() {Assertions.assertEquals("hello{a=\"b\"}",ParserUtil.addLabels("hello{}", Collections.singletonMap("a", "b")));}@Testpublic void test2_1() {HashMap<String, String> map = new LinkedHashMap<>();map.put("a", "b");map.put("c", "d");Assertions.assertEquals("hello{a=\"b\",c=\"d\"}", ParserUtil.addLabels("hello{}", map));}@Testpublic void test3() {Assertions.assertEquals("hello{a=\"b\",c=\"d\"}",ParserUtil.addLabels("hello{a=\"b\"}", Collections.singletonMap("c", "d")));}@Testpublic void test3_1() {HashMap<String, String> map = new LinkedHashMap<>();map.put("a", "b1");map.put("c", "d");Assertions.assertEquals("hello{a=\"b1\",c=\"d\"}", ParserUtil.addLabels("hello{a=\"b\"}", map));}
}

检查promQL表达式是否正确

 /*** 检查表示式是否正确** @param promql* @return*/public static boolean validatePromQL(String promql) {CharStream input = CharStreams.fromString(promql);PromQLLexer lexer = new PromQLLexer(input);CommonTokenStream tokens = new CommonTokenStream(lexer);PromQLParser parser = new PromQLParser(tokens);// 使用正确的入口规则进行解析ParserRuleContext tree = parser.expression();// 如果没有遇到任何语法错误,则认为查询是合法的return parser.getNumberOfSyntaxErrors() == 0;}

提取promeQL的指标名称

@Slf4j
public class MetricNameExtractor extends PromQLParserBaseListener {private Set<String> metricNames = new HashSet<>();@Overridepublic void enterEveryRule(ParserRuleContext ctx) {// 获取当前规则的名字String ruleName = PromQLParser.ruleNames[ctx.getRuleIndex()];if ("instantSelector".equals(ruleName)) {// 假设 metric_name 规则只有一个子节点,即标识符TerminalNode terminalNode = (TerminalNode) ctx.getChild(0);String metricName = terminalNode.getText();metricNames.add(metricName);log.info("enterEveryRule Found: {}==={}", metricName, terminalNode.getClass().getSimpleName());}}@Overridepublic void enterInstantSelector(PromQLParser.InstantSelectorContext ctx) {ParseTree parseTree = ctx.getChild(0);if (parseTree != null) {String metricName = parseTree.getText();metricNames.add(metricName);log.info("enterInstantSelector Found: {}==={}", metricName, parseTree.getClass().getSimpleName());}}@Overridepublic void enterMatrixSelector(PromQLParser.MatrixSelectorContext ctx) {ParseTree parseTree = ctx.getChild(0);if (parseTree != null) {int childCount = parseTree.getChildCount();if (childCount > 0) {parseTree = parseTree.getChild(0);String metricName = parseTree.getText();log.info("enterMatrixSelector Found: {}==={}", metricName, parseTree.getClass().getSimpleName());metricNames.add(metricName);}}}public Set<String> getMetricNames() {return metricNames;}}
public static Set<String> findPromQLMetricNames(String promql){// 初始化词法分析器和解析器PromQLLexer lexer = new PromQLLexer(CharStreams.fromString(promql));PromQLParser parser = new PromQLParser(new CommonTokenStream(lexer));// 解析输入并获取解析树ParseTree tree = parser.expression();// 创建监听器实例并遍历解析树MetricNameExtractor extractor = new MetricNameExtractor();ParseTreeWalker walker = new ParseTreeWalker();walker.walk(extractor, tree);// 输出所有找到的指标名称return extractor.getMetricNames();
}
https://blog.csdn.net/catcher92/article/details/122205513

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/907345.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习安装配置vue

1.先将nodejs下载2.在我们的安装目录下,创建名为node_cache和node_global的两个文件夹 3.打开cmd窗口,执行如下命令,将npm的全局模块目录和缓存目录配置到刚才创建的那两个目录。 npm config set prefix “D:\soft2024.7.6\nodejs\node_global” npm config set cache “D:\…

K8s Ingress, 你这个老6

Ingress 这个老6,结合nodeport和ClusterIp两种服务类型,你在引流这一块玩的花啊。 入口一夫当关,对内如鱼得水。本文是有态度马甲的第185篇原创。 本文记录了k8s中核心对象Ingress的产生背景和实现机制。 我们都知道k8s Service是一种将Pods通过网络暴露出来的抽象,每个服务…

C# .NET core 中处理图像,SkiaSharp,ImageSharp,NetVips,Magick.net多维度对比

2025年有哪些图像处理库,我们可以在项目中使用哪些库?本文列出了最流行的现有库。 .NET Core图片处理库SkiaSharp(https://github.com/mono/SkiaSharp) Magick.net(https://github.com/dlemstra/Magick.NET) ImageSharp(https://github.com/SixLabors/ImageSharp) NetV…

20243317 实验二《Python程序设计》实验报告

课程:《Python程序设计》 班级: 2433 姓名: 邓雅文 学号:20243317 实验教师:王志强老师 实验日期:2025年3月26日 必修/选修: 公选课 一、实验内容 1、掌握python中函数定义与调用相关知识点 大致框架与C语言相同,同样有实参,形参,可能有返回值,形式如下: def 函数名…

MEBCY-v2

MERCY-v2 信息收集 查找目标主机ip ┌──(root㉿kali)-[~] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:84:b2:cc, IPv4: 192.168.158.143 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.158.1 00:50:56:c…

人群密度分析预警摄像机

人群密度分析预警摄像机是可以实时地统计出一个指定区域内的总人数。当所监视区域的人员数量达到设定的阀值时摄像机输出报警信号。可设置人数阈值和时间阈值。用于设置触发进入区域内的人数值,达到该设定的阈值则摄像机输出报警信号。人数阈值可以手动设置,系统默认值为5人,…

CloudFlare DNS实现根域名跳转WWW域名,301跳转

0. 目的 托管在CloudFlare上的域名,已配置好www.bktai.com,想在用户访问根域名https://bktai.com时,重定向到 https://www.bktai.com. 为什么是重定向而不是同时可以访问?搜索引擎会搜到重复的内容,且维护两套路径都能正常工作,会造成混乱。 1. 配置根域名 点击自己要设置…

day:32 jmeter及性能测试——介绍

一、性能测试介绍 1、什么叫做性能测试? (1)通过某些工具或手段来检测软件的某些指标是否达到了要求,这就是性能测试 (2)指通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试 2、性能测试的时间? 在功能测试完成后才能进行性能测试…

第六周第五天

所用时间:405分钟 代码量(行):689 博客量(篇):20 了解到的知识点: 1.VLAN的创建与划分 今天进行了计算机网络的实验一,在昨天下载的packet tracer上进行,实现了VLAN的创建与划分,进行跨交换机的相同vlan之间的计算机和不同vlan之间的计算机的通信实验2.树状结构查询…

独立按键控制LED数码管

前言 通过1个独立按键,控制LED数码管显示字符。 结合之前我的两篇文章独立按键控制LED流水灯方向 https://www.cnblogs.com/luckydoog/p/18796974数码管静态显示 https://www.cnblogs.com/luckydoog/p/18797690效果原理 提前在程序里存储共阴极数码管的编码表,能表示的字符范…

day:32 jmeter操作数据库——参数化

一、数据库通过用户参数设置变量 1、建一个查询的jdbs请求2、前置处理器中添加用户参数3、修改线程数4、查看结果二、txt文档实现参数化 1.编辑sql语句中导入变量${变量名}新建一个txt文档:data 命名导入txt文档运行以上内容是将sql语句写入到txt文档中引用 2. 将数据写入txt文…