ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Prometheus lezer-promql:基于 lezer 的 PromQL 解析器文法及其在 Web UI 中的工程实现

Prometheus lezer-promql:基于 lezer 的 PromQL 解析器文法及其在 Web UI 中的工程实现 Prometheus lezer-promql基于 lezer 的 PromQL 解析器文法及其在 Web UI 中的工程实现【免费下载链接】prometheusThe Prometheus monitoring system and time series database.项目地址: https://gitcode.com/GitHub_Trending/pr/prometheuslezer-promql是 Prometheus 仓库内为前端CodeMirror 查询输入框提供的 PromQL 语法解析库它用 lezer 体系重新实现了官方 Go 端 yacc 文法定义的 PromQL 语法。读完后你将理解这个 npm 包的定位与安装方式、文法文件如何完整描述 PromQL 的运算符优先级、时长时间量、关键字上下文消歧等核心机制以及它从.grammar源文件到可发布产物、再到基于文本文件的解析测试的完整工程链路。1. 定位与边界它是谁的解析器根据 README该包是PromQL grammar for the lezer parser system灵感来源于 Prometheus 最初的、用 yacc 编写的官方文法 generated_parser.y。README 给出三条关键定位信息库是稳定的stable但不提供使用指引——因为它已经被集成进 codemirror-promql 模块。README 明确建议如果你想在项目中真正使用它应该直接使用prometheus-io/codemirror-promql而不是裸用本包。它是官方文法的 lezer 侧镜像README 特别强调This library is a lezer-based implementation of the authoritative, goyacc-based PromQL grammar官方 Go 文法promql/parser/generated_parser.y才是权威定义官方文法的任何变更都需要同步反映到这个包里。这意味着维护 lezer-promql 是一项跟随式工作后端每新增一个函数、一个修饰符前端文法都要跟进。许可协议代码遵循 Apache 2.0见仓库根目录 LICENSE与整个 Prometheus 项目一致。从仓库结构看这个包位于前端 UI 的子模块目录web/ui/module/lezer-promql/下其消费方正是同级的codemirror-promql模块。codemirror-promql/src/promql.ts 中import { parser } from prometheus-io/lezer-promql直接取用解析器实例parser/parser.ts、types/function.ts、complete/hybrid.ts 等文件则按名称导入了文法中定义的各个语法节点符号如EqlSingle、Neq、BinaryExpr用于实现查询的类型推断与自动补全。也就是说lezer-promql 负责把文本解析成语法树codemirror-promql 负责在语法树之上做智能编辑体验两者是基础解析层与应用层的分工。2. 安装方式与依赖边界README 给出的安装步骤是两条命令第二条的原因在 package.json 中可以验证npm install --save prometheus-io/lezer-promql注意lezer 的两个核心包是本包的 peer dependency需要手动安装npm install --save lezer/lr lezer/highlight查看 package.json 的peerDependencies字段即可确认这一约定lezer/highlight: ^1.1.2、lezer/lr: ^1.2.3。这是因为 lezer 的运行时分核心 LR 解析引擎lezer/lr和高亮标签体系lezer/highlight两部分而本包只负责 PromQL 文法本身不捆绑运行时把版本选择权交给使用方。包名prometheus-io/lezer-promql、当前版本0.314.0、入口dist/index.cjs/dist/index.es.js、类型声明dist/index.d.ts均定义在 package.json 的main、module、exports字段中CommonJS 与 ESM 双格式产出由打包配置保证见第 4 节。3. 文法核心src/promql.grammar 如何描述 PromQL整个解析能力的源头是 src/promql.grammar一个约 527 行的 lezer 文法文件。逐段拆解它的设计要点3.1 双入口完整表达式与裸指标名top PromQL { expr } top MetricName { Identifier }top声明了两个顶层解析入口PromQL用于解析任意完整的 PromQL 表达式MetricName用于单独解析一个指标名Identifier。这种双入口设计服务于编辑场景——用户在补全框里刚敲下node_cpu_seconds_时需要按指标名而非表达式来解析从而触发补全逻辑。3.2 优先级组精确复刻 PromQL 语义precedence { group, pow right, mul left add left, eql left, and left, or left }这与 PromQL 官方运算符优先级一一对应括号 幂右结合 乘除取模 加减 比较 集合操作and/or/unless。文法中BinaryExpr的每条产生式都带有优先级守卫!pow、!mul、!add、!eql、!and、!or例如BinaryExpr { expr !pow Pow binModifiers expr | expr !mul Div binModifiers expr | ... expr !and And binModifiers expr | expr !or Or binModifiers expr }!x的含义是仅当右操作数可能构成更高优先级时才算数这是 lezer 里避免优先级冲突、控制结合性的标准写法。测试文件 test/expression.txt 中foo[-2^2]的用例验证了幂比一元负号优先级更高这一细节-2^2被解析为-(2^2)而非(-2)^2。3.3 表达式家族14 种 Alternativeexpr规则聚合了 PromQL 的全部表达式形态expr[isGroupExpr] { AggregateExpr | BinaryExpr | FunctionCall | MatrixSelector | NumberDurationLiteral | OffsetExpr | AnchoredExpr | SmoothedExpr | ParenExpr | StringLiteral | SubqueryExpr | UnaryExpr | VectorSelector | StepInvariantExpr }其中[isGroupExpr]给整个表达式打上Expr属性标记方便上层codemirror-promql做节点分类。值得关注的是几个较新的节点OffsetExprexpr Offset OffsetDurationExprrate(foo[1m]) offset 5m的offset修饰SubqueryExprexpr [ DurationExpr : ( | DurationExpr) ]子查询如foo[1h:5m]步长可省略StepInvariantExprexpr At (NumberDurationLiteral | AtModifierPreprocessors ())时间修饰符包括 1596035063.5、 start()、 end()三种形态AnchoredExpr/SmoothedExpranchored、smoothed实验性修饰。向量选择器VectorSelector允许三种形态——Identifier LabelMatchers、裸Identifier、甚至纯标签匹配器{jobapi}不带指标名也是合法 PromQL。标签匹配器支持未加引号LabelName与加引号QuotedLabelName两种标签名匹配算子MatchOp覆盖、!、~、!~四种。3.4 时长时间量用 7 条正则保证单位顺序PromQL 的时间单位是y w d h m s ms且必须按从大到小顺序书写1h30m合法1m2h非法。文法中的durationtoken 把同一正则重复 7 遍、每遍让其中一个单位变为必选以此强制至少一个数字单位对且单位有序duration { ( ( std.digit y ) ( std.digit w )? ( std.digit d )? ( std.digit h )? ( std.digit m )? ( std.digit s )? ( std.digit ms )? ) | ( ( std.digit y )? ( std.digit w ) ( std.digit d )? ... ) | ... }这段看似冗余的正则是文法里最精巧的部分之一expression.txt 中有专门的回归用例foo[1m2h]的解析结果末尾出现⚠错误节点确认了单位乱序会被拒绝。3.5 关键字消歧始终关键字与上下文关键字PromQL 的关键字大量借用普通标识符形态sum、rate、by、on……且聚合操作与集合操作符大小写不敏感SuM BY(...)合法。lezer 用两级机制实现第一级始终关键字specialize。src/tokens.js 中specializeIdentifier处理 8 个任何位置都是关键字的词——inf、nan、bool、ignoring、on、group_left、group_right、offset通过value.toLowerCase()实现大小写不敏感const keywordTokens { inf: inf, nan: nan, bool: Bool, ignoring: Ignoring, on: On, group_left: GroupLeft, group_right: GroupRight, offset: Offset, }; export const specializeIdentifier (value, stack) { return keywordTokens[value.toLowerCase()] || -1; };第二级上下文关键字extend。sum、by、on等词只有在语法栈能够移入stack.canShift(token)对应语法位置时才被识别为关键字否则退化为普通Identifier。extendIdentifier还专门处理start/end的二义性——它们既可以是 start()/ end()里的预处理器也可能是查询函数start_timestamp()场景下紧邻的标识符代码按stack.canShift(StartFn)、stack.canShift(AtStart)分别消歧。第三级条件函数名condFn。文法末尾定义了宏condFnterm { extendIdentifier, term } DurationStep { condFnstep } DurationRange { condFnrange }所有函数名Rate、Delta…… 共 80 余个通过external propSource逐个绑定到小写字符串如condFnrate都是条件的rate出现在函数调用位置时解析为FunctionIdentifier(Rate)而rate单独出现时仍是VectorSelector(Identifier)——即函数名可以当指标名用。expression.txt 里的用例rate裸词解析为PromQL(VectorSelector(Identifier))正是对此的验证。同样的规则也约束了时长伪函数foo{range5m}中的range是标签名some_range_metric[5m]中的some_range_metric是普通标识符都不会被误判为range()时长函数。3.6 高亮映射highlight.js 的风格表src/highlight.js 用styleTags把语法节点映射到lezer/highlight的语义标签注释、标签名、字符串、数字含时长时间量各自成组全部函数名节点映射为tags.function(tags.variableName)聚合算子映射为operatorKeywordbool、on、ignoring、group_left、group_right、offset等修饰符映射为modifierand/or/unless单独归为logicOperator。文法中解析错误的位置会产生⚠字符节点映射到tags.invalid编辑器据此渲染错误波浪线。4. 构建链路从 .grammar 到 npm 产物README 的 Development 章节给出npm inpm run build实际构建由 build.shpackage.json 中build脚本的入口串起四步pnpm exec lezer-generator src/promql.grammar -o src/parser # 1. 生成 LR 解析器 cat src/parser.terms.js src/parser.js # 2. 合并词法节点常量 bash ./generate-types.sh # 3. 生成类型声明 pnpm exec rollup -c # 4. 打包产物lezer-generator读取文法输出两个源文件src/parser.jsLR 解析表与src/parser.terms.js全部语法节点 ID 的具名导出——codemirror-promql 里import { EqlSingle, BinaryExpr } from prometheus-io/lezer-promql用的正是这些导出把 terms 文件追加进parser.js使单一入口同时提供解析器与节点常量generate-types.sh生成 TypeScript 类型声明对应 package.jsonexports.types指向的dist/index.d.ts按 rollup.config.js 以src/parser.js为入口打包出dist/index.cjs与dist/index.es.js双格式产物external()函数将一切非相对路径的依赖即lezer/*标记为外部保证运行时依赖留给消费方安装——这与第 2 节的 peer dependency 约定呼应。由于src/parser.js/src/parser.terms.js是生成物阅读仓库源码时应以src/promql.grammar为权威源生成文件仅供核对。5. 测试体系用纯文本文件断言整棵语法树README 给出的测试命令是npm run testpackage.json 中对应test: NODE_OPTIONS--experimental-vm-modules jest测试驱动器是 test/promql.test.js逻辑非常简洁扫描test/目录下所有.txt文件交给 lezer 官方提供的fileTests工具逐用例执行import { fileTests } from lezer/generator/dist/test; ... for (const { name, run } of fileTests(fs.readFileSync(...), file)) it(name, () run(parser));用例格式是输入表达式 期望的语法树如 test/expression.txt约 900 行。以官方文档中最典型的查询为例sum by(job, mode) (rate(node_cpu_seconds_total[1m])) / on(job) group_left sum by(job)(rate(node_cpu_seconds_total[1m]))的期望树完整呈现了BinaryExpr → AggregateExpr(AggregateOp(Sum), AggregateModifier(By, GroupingLabels), FunctionCallBody → FunctionCall(FunctionIdentifier(Rate), MatrixSelector(VectorSelector, DurationExpr))的嵌套结构/ on(job) group_left则落在MatchingModifierClause(On, GroupingLabels, GroupLeft)节点上。测试集覆盖了文法的全部关键行为可作为本包实际支持哪些语法的权威清单字面量科学计数法0.123e3、三种字符串引号含反引号多行字符串大小写不敏感SuM BY(...) ... IGNOring(...) ... withOUT(...)、AND/UNLESS/OR全大写集合操作时长时间量运算foo[11s10s-5*2^2]、foo[-(10s-5s)20s]以及step()、range()、min_of(5m, range())、max_of(step(), 1m)四个时长伪函数在矩阵选择器、子查询步长、offset三种上下文中的解析错误检测foo[1m2h]产生⚠节点消歧回归函数名当指标名、标签名撞伪函数名等边界用例。这一文本文件即测试的体系使新增语法特性时的回归成本极低向test/*.txt追加一段输入 树即可。6. 与官方 Go 文法的同步责任README 的 Note 段落是本包最重要的维护约束lezer-promql 是权威 yacc 文法的 lezer 实现官方文法变更必须同步过来。从源码可以观察到这一约束的实际体现——lezer 文法已纳入了多个实验性/新增语法元素聚合与限制算子limitk、limit_ratio对应AggregateOp中的LimitK/LimitRatio二元修饰fill(duration)、fill_left(duration)、fill_right(duration)FillModifier规则且fill_left与fill_right可叠加使用比较算子/TrimUpper、/TrimLower、atan2函数、 start()/ end()预处理器、anchored/smoothed修饰时间函数族days_in_month、day_of_month、histogram_*、ts_of_*等。这些词同时出现在 promql.grammar 的产生式与 tokens.js 的上下文关键字表中说明文法 关键字表两处是同步修改的最小单元。另有一处值得留意的细节highlight 映射表中出现了TsOfMaxFirstTime一项且其绑定字符串是ts_of_first_over_time而FunctionIdentifier列表里并不存在TsOfMaxFirstTime这个节点对应节点实为TsOfMaxOverTime——从源码结构看这疑似高亮映射表中的一处笔误不影响解析正确性但若要引用该映射建议以实际解析节点为准。7. 小结何时用它怎么用定位lezer-promql 是 Prometheus 官方 PromQL 语法在浏览器端的 lezer 实现与 promql/parser/generated_parser.y 保持语义对齐是 codemirror-promql 查询编辑体验的解析底座使用建议按 README 指引业务集成优先选择prometheus-io/codemirror-promql确有直接解析需求如自己做语法校验、AST 消费时安装本包并手动补齐lezer/lr、lezer/highlight两个 peer 依赖即可开发循环修改 src/promql.grammar 后执行npm run build重新生成解析器再向 test/expression.txt 追加输入 语法树用例npm run test验证边界包声明 stable 但无独立使用文档行为细节以文法源文件与测试文件为准任何对它的理解都应以官方 Go 文法为最终裁判——这正是 README 反复强调的同步责任。【免费下载链接】prometheusThe Prometheus monitoring system and time series database.项目地址: https://gitcode.com/GitHub_Trending/pr/prometheus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表