ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Nightingale V2 批量查询接口的 Elasticsearch KQL 扩展:语法、编译原理与实战指南

Nightingale V2 批量查询接口的 Elasticsearch KQL 扩展:语法、编译原理与实战指南 Nightingale V2 批量查询接口的 Elasticsearch KQL 扩展语法、编译原理与实战指南【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale本文围绕 Nightingale 开源仓库中的 doc/api/query-batch-v2-kql.md 展开完整讲解 V2 批量查询接口中 Elasticsearch 数据源的 KQLKibana Query Language过滤能力如何在请求中启用 KQL、KQL 被编译为标准 Query DSL 的底层原理、已支持的语法全集、词法细节与当前限制并结合 datasource/commons/eslike/kql.go 等源码与测试用例进行纵深验证。读完本文你将能够在一次POST /api/n9e/v2/query-batch请求中用与前端 Kibana 一致的 KQL 文法精确检索 Elasticsearch/OpenSearch 日志并理解其行为边界。一、背景V2 批量查询接口中的 Elasticsearch 查询Nightingale 的 V2 批量查询接口POST /api/n9e/v2/query-batch商业版为/api/n9e-plus/v2/query-batch支持在一次请求中查询多个数据源并用表达式组合时序结果详见 doc/api/query-batch-v2.md。接口按请求中datasource.cate与datasource.id从已初始化的数据源缓存取得插件并执行因此 Elasticsearch、OpenSearch 等 cate 天然支持logs/time_series两种结果类型。在 Elasticsearch 数据源的 query 对象中原本只支持 Lucene 风格的filter表达式query_string查询。KQL 扩展为同一个输入框提供了第二种语法传filter_language: kql即启用未传或传lucene则保持旧行为。这一设计保证了「同一面板、同一过滤输入框两种语法随意切换」与前端 Kibana 的体验对齐。从 center/router/router_query_batch_v2.go 可以看到V2 对result_typelogs的请求调用plug.QueryLog对时序请求调用plug.QueryData而 Elasticsearch 插件的这两个方法最终都汇聚到eslike包见 datasource/es/es.go。也就是说KQL 支持是在 Elasticsearch/OpenSearch 共用的eslike查询层实现的两个数据源 cate 一并受益。二、使用方式在 query 对象中启用 KQL在datasource.cate elasticsearch或 OpenSearch的 query 对象中传入以下三个字段即可{ filter_language: kql, filter: service.name: api AND log.level: \ERROR\ AND http.response.status_code 500, kql_options: { case_insensitive: false, time_zone: Asia/Shanghai } }新增字段说明如下字段类型必填说明filter_languagestring否传kql启用 KQL未传或lucene保持旧的 Lucene 行为filterstring否KQL 表达式留空与 Lucene 一致表示只按时间范围查全部kql_options.default_fieldstring否已忽略仅为请求兼容保留裸词按前端行为查询全字段kql_options.case_insensitiveboolean否已忽略仅为请求兼容保留值通配生成query_string大小写行为由字段的分析器决定kql_options.time_zonestring否仅对本请求date_field的范围条件生效用于日期文字解释时区如Asia/Shanghai或08:00几点需要特别注意的语义顶层 V2 的from/to仍是权威时间范围。KQL 中出现的任何时间条件如timestamp now-2d不会取代顶层时间范围而是与其共同作为 filter 生效最终全部合并进bool.filter。filter留空或纯空白时KQL 与 Lucene 行为一致只按时间范围查全部而不是报错。这一点在 datasource/commons/eslike/kql.go 中有明确注释切换语法但尚未填写条件的面板不应直接失败。传入filter_language之外的值如sql会返回unsupported filter_language错误。从源码看KQLOptions结构体定义了这三个选项的 JSON/mapstructure 标签DefaultField与CaseInsensitive被注释为「accepted for request compatibility and ignored」TimeZone则在编译期被真正使用见 datasource/commons/eslike/kql.go。三、编译方式KQL → AST → 标准 Query DSLKQL 并不会被直接转交给 Lucene 的query_string。整个编译链路是KQL 文本 │ 词法分析kqlParser.next ▼ ASTkqlNodeand / or / not / is / range / nested │ 前导通配校验kqlValidateLeadingWildcards ▼ 标准 Elasticsearch Query DSLkqlToDSL │ 与 V2 时间范围合并 ▼ bool.filter 查询体关键入口是 datasource/commons/eslike/kql.go 的CompileKQL先以 rune 为单位做词法切分递归下降解析出表达式树确认 token 流已消费完毕kqlEOF再做前导通配符校验最后调用kqlToDSL产出 DSL。GetFilterQuery则负责将编译产物与时间范围elastic.RangeQuery拼进同一个bool.filterdatasource/commons/eslike/kql.go。这样设计的目的很明确避免 KQL 与 Lucene 在范围比较、字段存在、转义和布尔语义上的差异。项目支持 ES 7.10 及以上版本从 datasource/commons/eslike/eslike.go 中按 ES 大/小版本选择fixed_interval或interval的逻辑可见版本兼容策略因此不能把仅较新 ES 才提供的原生kqlQuery DSL 作为通用依赖 —— 后端自行编译是保证跨版本可用的稳妥路径。一个值得注意的实现细节编译器内部使用哨兵字符串kuery-wildcard携带未转义的*见 datasource/commons/eslike/kql.go在生成 DSL 或报错时再替换回*避免通配符在词法/语法处理中被误转义或丢失。对应的回归测试在 datasource/commons/eslike/kql_test.goTestCompileKQLWildcardMarkerLiteralFieldDoesNotBecomeWildcard与错误文本还原测试TestCompileKQLErrorsQuoteTheOriginalText中均有覆盖。四、已支持语法全集下表完整列出 KQL 编译器支持的语法形态及其生成的 DSLKQL生成的 DSL示例字段匹配matchstatus: 200、message: timeout error精确短语match_phrasemessage: timeout error字段存在existstrace.id: *值通配query_stringservice: api*字段名通配与前端默认模式相同的字段名 DSLdatastream.*: logs范围比较rangebytes 1024、timestamp now-2d布尔运算boola: 1 AND b: 2、NOT status: 200括号与同字段多值bool.shouldstatus: (200 OR 201)nested 作用域nesteduser:{ first: Alice AND last: White }全量匹配match_all*:*从 datasource/commons/eslike/kql.go 的kqlIsDSL可以看出每种形态的精确映射规则字段匹配未加引号的单值→match双引号值 →match_phrase值为单独*→exists字段存在性检测值带通配符 → 该字段上的query_string裸词无字段名→multi_match未加引号用best_fields加引号用phrase且带lenient: true字段名与值均为单独*即*:*或*: (*)→match_allOR分支生成bool.should并带minimum_should_match: 1AND生成bool.filterNOT生成bool.must_notnested 作用域生成nested查询并带score_mode: none子路径逐层拼接user.names.first这种多级嵌套同样支持。这些映射在 datasource/commons/eslike/kql_test.goTestCompileKQLFrontendCompatibility中以「输入 KQL → 期望 DSL」的表格形式逐条锁定是整个兼容性契约的测试证据。五、词法细节与转义规则KQL 的语法细节决定了它能表达什么、不能表达什么本小节逐条展开布尔关键字不区分大小写AND、OR、NOT大小写均可如a: 1 or b: 2但字段条件之间必须显式使用AND或OR。status: 200 level: ERROR这种「空格隐含 AND」的写法会直接编译失败datasource/commons/eslike/kql_test.go 中被列入 rejected 列表。这是与前端文法一致的刻意取舍。括号与多词值可使用括号明确优先级字段后的括号可以容纳多词值例如message: (timeout error)整体作为值匹配。括号内也可以继续放布尔逻辑message: (timeout AND error)、message: (NOT timeout)都是合法形态。未加引号的多词值空格是值的一部分其中可以带通配符。例如message: foo bar*会整体作为一个通配值下发为query_string而不是拆成两个词。其词法依据在parseLiteralTaildatasource/commons/eslike/kql.go只有单独一个引号字符串才独立成词。双引号内的*是普通字符message: foo*会按短语匹配match_phrase不会展开通配。未加引号的/、~、^、[]是普通值内容例如message: /timeout.*/会作为query_string值下发测试中可见其被转义为\/timeout.*\/message: foo~2、message: foo^2、message: [one TO two]等同样按字面值处理不做 Lucene 特殊语法解释。反斜杠转义支持\t、\r、\n、\uXXXXUnicode 转义4 位十六进制以及任意普通字符的\x转义。例如http.request.referrer: https\://example.com实际匹配https://example.commessage: \u4e2d匹配中。类型化字面量未加引号的值除true/false/null外一律按字符串下发kqlLiteralValue见 datasource/commons/eslike/kql.go。因此bytes 1024生成{gte: 1024}字符串形式数值与日期含 epoch 毫秒字段由 Elasticsearch 按 mapping 解析。field: true、field: null则分别生成布尔true与null值。通配符逃逸内部哨兵kuery-wildcard是「恰好包含该子串的字面量会被还原为通配符」的唯一例外实际内容中出现该字符串的概率极低测试TestCompileKQLWildcardMarkerLiteralFieldDoesNotBecomeWildcard也验证了字段名中出现该字面量不会被误判。六、mapping 无关的编译策略当前编译器不读取 Elasticsearch mapping严格复现前端默认转换器buildESQueryFromKuery的 no-mapping 分支不会按text/keyword字段类型切换查询类型如keyword走term、text走match这类优化不会发生。这意味着裸值统一走match值通配统一走query_string行为完全由 ES 端的分析器决定这正是kql_options.case_insensitive被忽略的原因 —— 无 mapping 分支本身不生成case_insensitive选项大小写是否敏感取决于字段分析器kql_options.default_field被忽略同理 —— 前端文法对裸词总是查询全字段。字段名通配包括*: value与*prefix: value会按前端默认转换器原样传入 DSL不会受值通配的前导*限制测试TestCompileKQLFieldLeadingWildcardsMatchesFrontendDefault验证了*: foo、*timeout: foo均可编译通过。范围值中的通配符同样原样传给range如bytes foo*、bytes *foo这可能因字段类型而被 Elasticsearch 拒绝或得到非预期结果建议仅在确有兼容需求时使用。七、当前限制与前端导出函数allowLeadingWildcardsfalse的默认值一致编译器对非单独*的前导通配默认返回INVALID_QUERYmessage: *timeout、*timeout、message: **均编译失败错误信息包含Leading wildcards are disabled.field: *字段存在与*:*全量匹配不受此限制校验只发生在「值is转换」这一处见kqlValidateLeadingWildcards的注释datasource/commons/eslike/kql.go字段名与范围值保留前导通配。此外KQL 的括号、连续NOT与 nested 嵌套最多 64 层常量kqlMaxNestingDepth 64超出时报KQL nesting exceeds maximum depth 64。测试TestCompileKQLNestingDepthLimit用 65 层括号、65 层NOT、65 层a:{嵌套分别验证了上限并用 63 层验证了「上限之内正常通过」。八、完整请求示例下面是一次完整的 V2 批量查询请求在 1 小时时间窗内从logs-*索引族检索service.nameapi、log.levelERROR、HTTP 状态码 ≥ 500 的日志按timestamp倒序取前 100 条{ from: 1784971200, to: 1784974800, queries: [ { kind: query, ref_id: ES_LOGS, datasource: {cate: elasticsearch, id: 7}, result_type: logs, query: { index_type: index, index: logs-*, date_field: timestamp, limit: 100, ascending: false, filter_language: kql, filter: service.name: api AND log.level: ERROR AND http.response.status_code 500, kql_options: {time_zone: Asia/Shanghai} } } ] }要点回顾filter中http.response.status_code 500是数值范围比较编译为range查询因为目标字段不是date_fieldtime_zone不会注入该 range若把时间条件如timestamp 2026-07-29T00:00:00写入 KQL且该字段等于date_field此处为timestamptime_zone才会生效 —— 测试TestCompileKQLTimeZoneOnlyForDateField精确验证了「数值 range 不带 time_zone、日期 range 才带」的规则time_zone支持Asia/Shanghai这类 IANA 名称也支持08:00这类偏移写法。九、查询成功后的响应行为KQL 查询成功后的日志记录遵循 V2 通用响应信封HTTP 200、dat.results[]按请求顺序返回单项失败不影响其他查询。针对 Elasticsearch/OpenSearch 的 DSL 分支有两点与 KQL 直接相关的行为需要了解命中文档的_source会直接成为records[].fieldsresult_typelogs时每条记录的字段就是文档原始_source内容不会返回_id、_index或sortV2 对日志结果做了裁剪避免把 Elasticsearch 内部元数据泄漏给下游。底层实现在 center/router/router_query_batch_v2.goplug.QueryLog返回的 SearchHit 列表经queryBatchV2Records转换后写入RecordsDSL 路径与 XPack SQL 路径在此处做了区分queryBatchV2ElasticsearchSQLPayload。在 datasource/commons/eslike/eslike.go 的QueryLog中可以看到 ES 6 与 7 版本在_source扁平化处理上的差异V2 在 7 路径直接透传 hit。十、源码导读与可继续深入的入口如果你想继续深挖本功能的实现推荐按以下路径阅读datasource/commons/eslike/kql.goKQL 编译器全量实现 —— 词法next/readQuoted/readAtom/readEscape、语法parseOr/parseAnd/parseUnary/parsePrimary/parseFieldValue/parseLiteralTail、DSL 生成kqlToDSL/kqlIsDSL/kqlRangeKey、转义器kqlLuceneEscaper与前端escapeQueryString转义同一字符类与入口CompileKQL/GetFilterQuerydatasource/commons/eslike/kql_test.go兼容性契约测试覆盖语法矩阵、前导通配、范围操作符、时区、嵌套深度、错误文本等全部关键行为datasource/commons/eslike/eslike.goQuery参数结构FilterLanguage、KQLOptions字段的 JSON 标签、QueryData/QueryLog中调用GetFilterQuery并拼入时间范围的主流程datasource/es/es.goElasticsearch 插件如何把 V2 的 payload 委托给eslikeMakeLogQuery/MakeTSQuery/QueryData/QueryLogcenter/router/router_query_batch_v2.goV2 执行器如何按result_type分发到QueryLog/QueryData并组装统一响应center/router/router_query_batch_v2_test.goV2 接口级测试中直接出现的 KQL 请求示例filter_language:kql,filter:message: timeout*doc/api/query-batch-v2.mdV2 通用请求/响应协议、表达式引用序列的方式、完整错误码表INVALID_QUERY、DATASOURCE_TIMEOUT、EXPRESSION_*、DEPENDENCY_*等。结语Nightingale 的 KQL 支持不是简单地把 KQL 字符串透传给 Elasticsearch而是在后端完整实现了与 Kibana 前端buildESQueryFromKuery默认行为对齐的词法/语法/DSL 编译链路并以测试矩阵锁定兼容性。对使用者而言这意味着同一套 KQL 文法在 Kibana 与 Nightingale 查询界面之间可以无缝迁移对二次开发者而言eslike/kql.go提供了一份结构清晰、测试完备的参考实现可以在此基础上扩展新的语法形态如更复杂的值类型而不破坏既有契约。【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表