☰
highlight.io 全栈可观测性搜索查询语法完全指南:表达式、键值、通配符、正则与逻辑组合
2026/9/25 4:11:55 网站建设 项目流程
  • 可观测性
  • 后端

【免费下载链接】highlight

highlight.io: The open source, full-stack monitoring platform. Error monitoring, session replay, logging, distributed tracing, and more.

项目地址:https://gitcode.com/gh_mirrors/hi/highlight
点击查看免费下载

highlight.io 在会话回放(Session Replay)、日志(Logs)、错误监控(Errors)、分布式追踪(Traces)与事件(Events)等全部产品页面上,共享同一套统一的搜索查询语法。本文以官方 Search 文档 为骨架,系统讲解这套语法的表达式构成、键值规则、通配符与正则、比较运算符、逻辑组合与分组技巧,并结合仓库中的 ANTLR 文法、查询解析器与 SQL 监听器等源码实现,揭示每条查询在服务端是如何被解析、翻译并最终落成 ClickHouse 查询的,帮助你在各模块中写出精准、高效、可复用的搜索表达式。

基本语法:表达式与键值对

一个搜索查询由一个或多个表达式(expression)组成。每个表达式可以是"键(key)与值(value)之间的比较",也可以是多个表达式的逻辑组合。最简单的形式是:

span_name=gorm.Query

这条查询的含义是:筛选出所有span_name属性等于gorm.Query的追踪 span。键在等号左侧,值在等号右侧。

无键搜索与默认键

你还可以省略键,直接输入一个值,此时查询会作用于该数据类型的默认键(default key):

gorm.Query

不同产品模块的默认键不同(详见本文最后一节):

  • 日志(Logs):默认键是message,因此直接输入graphql request等价于message="*graphql request*";
  • 追踪(Traces):默认键是span_name,因此直接输入gorm.Query等价于span_name=*gorm.Query*;
  • 会话(Sessions):默认键会跨多个属性搜索,包括用户标识符(email、device_id、identifier)以及地理位置(city、country)等。

这一行为在源码中有明确体现:监听器在进入body_search_expr规则时会自动把当前键切换到tableConfig.BodyColumn(即各模块配置的默认键列),参见 listener/listener.go 中的EnterBody_search_expr。

自定义属性过滤

你在 SDK 中随会话、日志和追踪发送的任何自定义属性(custom attributes)都可以作为过滤键使用:

user_id=42

例如日志模块中,logger.info('Queried table', { table: 'users', query: 'hello' })产生的table:users与query:hello属性,都可以直接用table=users、query=hello来检索。

键与值的规则

键(key)是标识符,可以包含字母数字字符以及下划线(_)、句点(.)、连字符(-)和星号(*)的任意组合。这在 antlr/SearchGrammar.g4 的ID词法规则中得到了印证:

ID : [A-Z_0-9.\-*]+ ;

值(value)可以包含任意字符。如果值中包含空格或特殊字符,必须用引号("或')包裹。需要说明的是,从文法定义看,除双引号、单引号外,反引号(`)同样被识别为字符串定界符:

STRING : ('"' ( '\\"' | ~["] )* '"' | '\'' ( '\\\'' | ~['] )* '\'') | '`' ( '\\`' | ~[`] )* '`' ;

对应的反引号示例:

`some value with spaces`

通配符匹配

你可以使用*匹配值的部分模式。例如:

  • span_name=gorm.*—— 匹配所有以gorm.开头的span_name值;
  • span_name=*.Query—— 匹配所有以.Query结尾的span_name值;
  • span_name=*orm*—— 匹配所有包含orm的值。

如果通配符值中包含空格或特殊字符,同样需要加引号:

tag="*query error*" visited-url="https://app.highlight.io/*"

源码层面的实现细节:在 listener/listener.go 的appendRules中,包含*的值会进入通配符分支,调用wildcardValue进行转换:

func wildcardValue(value string) string { value = strings.ReplaceAll(strings.ReplaceAll(value, "_", "\\_"), "*", "%") if !strings.HasPrefix(value, "%") { value = "%" + value } if !strings.HasSuffix(value, "%") { value = value + "%" } return value }

也就是说:*会被替换为 SQL 的%,下划线_会被转义为\_(避免误匹配),并且前后未显式写%的位置会被自动补齐。最终生成的查询是ILIKE形式的包含匹配,例如visited-url="https://app.highlight.io/*"最终会变成类似visited-url ILIKE '%https://app.highlight.io/%'的条件。

正则表达式匹配

你可以使用 matches 查询运算符=/[your regex here]/来执行正则搜索。值的开头与结尾各加一个/即表示正则模式:

  • clickTextContent=/\w.+\w/—— 匹配所有以任意单词字符开头和结尾的clickTextContent;
  • browser_version=/\d\.\d\.\d/—— 匹配所有形如[0-9].[0-9].[0-9]的浏览器版本。

包含空格或特殊字符的正则同样需要引号包裹:

tag="/\w \w/" visited-url="/https://app.highlight.io/\d/.+/"

源码层面的实现细节:在监听器中,当值以/开头并以/结尾时,会被识别为正则分支:去除首尾/后,对固定列生成column REGEXP value,对扩展属性生成getAttributeFilterExpr(..., OperatorRegExp, ...)表达式(参见 listener/listener.go 的appendRules)。仓库自带的Unquote单测覆盖了引号与转义的处理逻辑,见 listener/listener_test.go。

比较运算符

比较通过运算符完成。支持以下运算符:

运算符含义
=等于
!=不等于
<小于
<=小于或等于
>大于
>=大于或等于

这些运算符在 antlr/SearchGrammar.g4 中被定义为bin_op规则(!单独出现时不会被当作合法运算符,从而避免解析错误):

bin_op : WS* (BANG | EQ | NEQ | GT | GTE | LT | LTE | COLON) WS* ;

其中COLON(:)也被接受为比较运算符。在监听器中,:与=、!=走同一套等值处理逻辑(见appendRules中s.currentOp == ":" || s.currentOp == "=" || s.currentOp == "!="的分支)。仓库中的 backend/queryparser/queryparser.go 同样演示了key:value形式查询的拆分方式,其测试覆盖了多值、含冒号的值、引号空格值、通配符等场景(见 queryparser_test.go)。

数值与时间后缀

对于duration、length这类时长属性,运算符右侧可以使用时间后缀。从 listener/listener.go 的实现看,支持的后缀及其纳秒换算因子如下:

后缀含义
h小时
m分钟
s秒
ms毫秒
us微秒
ns纳秒

不同列的基准单位由timeMetrics表决定:Duration以纳秒(ns)为基准,Length与ActiveLength以毫秒(ms)为基准。NumericValue会把带后缀的值按列基准单位换算为纯数字,例如:

duration>1s length>10m active_length>5m

前者匹配所有时长超过 1 秒的 span,后两者筛选时长超过 10 分钟/5 分钟的活动会话。NumericValue的换算行为由 listener/listener_test.go 中的TestNumericValue用例逐项验证(例如10s在Duration列下换算为10000000000,在Length列下换算为10000)。

存在与不存在:exists / not exists

你可以用exists运算符判断某个键是否存在。例如,想找出所有关联了会话的追踪,可以写:

secure_session_id exists

exists还可以与not关键字组合使用。例如在追踪中只想看根级 span(没有父 span 的 span):

parent_span_id not exists

源码层面的实现细节:文法中exists_op同时接受EXISTS与NOT EXISTS两种形式(见 antlr/SearchGrammar.g4)。在监听器ExitExists_op中,EXISTS被转换为等值判断的反向语义(对应!= "",即存在非空值),NOT EXISTS被转换为= ""(即值为空/不存在),并进一步包裹为NOT (...)规则。

逻辑组合:AND、OR、NOT

表达式之间可以使用逻辑运算符AND、OR、NOT进行组合:

  • AND—— 两侧表达式都必须为真;
  • OR—— 至少一个表达式为真;
  • NOT—— 后随的表达式必须为假。

注意隐式 AND:除非你显式书写OR,否则所有过滤器之间默认是AND关系。例如:

service_name=private-graph span_name=gorm.Query

完全等价于:

service_name=private-graph AND span_name=gorm.Query

从文法看,search_expr规则显式包含implicit_and_op(空产生式)分支,即相邻表达式之间默认按 AND 结合(见 antlr/SearchGrammar.g4 的implicit_and_search_expr)。此外文法声明了options { caseInsensitive = true; },因此AND、OR、NOT、EXISTS等关键字大小写不敏感。

监听器在ExitAnd_col_expr、ExitOr_col_expr、ExitNegated_col_expr等回调中,把收集到的 SQL 规则分别用And(...)、Or(...)、NOT (...)合并,并同步构造出对应的FilterOperation树(Operator 为OperatorAnd/OperatorOr/OperatorNot),供上层程序化地读取和复用过滤条件。

分组表达式

表达式可以用圆括号(和)分组,从而控制运算优先级:

(key1=value1 AND key2=value2) OR key3=value3

你也可以用括号把某个键的多个取值分组:

service_name=(private-graph OR public-graph)

后者等价于service_name=private-graph OR service_name=public-graph,在需要针对同一个键筛选多个候选值时非常实用。

查询示例汇总

以下都是合法的高质量搜索查询示例,覆盖了本文介绍的大部分语法要素:

  • service_name=private-graph
  • service_name=public-graph AND span_name!=gorm.Query
  • service_name=worker OR span_name=gorm.Query
  • service_name!=private-graph
  • (service_name=public-graph AND span_name=gorm.Query) OR duration>=100000

第 5 条将"服务 + span 名"作为一组,再与"时长大于等于 100000 纳秒"做 OR,演示了括号在复杂业务场景中的用法。

搜索分段(Search Segments)

Highlight 的所有搜索页面都允许你保存搜索并在之后随时复用,这类已保存的搜索被称为segments(搜索分段)。你可以把常用的过滤器组合(例如"线上环境 + 出错的会话"、"private-graph 服务的慢 span")保存为 segment,在会话、日志、错误、追踪等页面之间统一复用,避免每次重新输入查询条件。

特殊字符处理

当值中包含特殊字符时,必须用引号包裹。特殊字符包括:

  • 空格;
  • 运算符字符:!、=、:、<、>;
  • 圆括号(和)。

例如 URL 中通常同时包含:与=,直接写会干扰解析,因此要写成:

visited-url="https://app.highlight.io/sessions"

带括号或比较符号的属性值同样建议引号包裹,例如tag="(error)"、message="code=500"。

源码级解析原理:从查询字符串到 ClickHouse SQL

了解完语法之后,我们来打通"输入 → 解析 → SQL"这条完整的调用链,这部分逻辑集中在 backend/parser 目录。

1. 文法定义(ANTLR)

查询语言由 antlr/SearchGrammar.g4 定义,规则覆盖了本文介绍的全部语法要素:search_expr(带显式/隐式 AND、OR、NOT、括号)、key_val_search_expr(键 + 二元运算符 + 值)、exists_search_expr(exists / not exists)、body_search_expr(无键表达式)等。该文法在编译期生成 Go 版词法/语法分析器(backend/parser/antlr/目录下的searchgrammar_lexer.go、searchgrammar_parser.go等)。

2. 入口函数

backend/parser/parser.go 提供了两个核心入口:

  • GetSearchFilters(query, tableConfig, listener):创建 ANTLR 输入流、词法分析器、语法分析器,用ParseTreeWalkerDefault遍历语法树,驱动监听器逐步构建过滤条件,最后返回listener.Filters;
  • Parse(query, tableConfig):便捷封装,内部先构造一个临时的SelectBuilder再调用AssignSearchFilters。

其中值得注意的一点是:每个表的配置(TableConfig)会附带一个默认过滤条件。在GetSearchFilters中,如果查询里没有出现指标名保留键,就会自动拼上tableConfig.DefaultFilter:

if !strings.Contains(query, string(modelInputs.ReservedTraceKeyMetricName)) { query = query + " " + tableConfig.DefaultFilter }

这正是"会话页面默认只看已完成会话(completed=true)"这类行为的实现来源。

3. 表配置与键映射

TableConfig定义于 backend/model/model.go#L2494,它决定了键到实际列/属性的映射方式:

type TableConfig struct { TableName string BodyColumn string SeverityColumn string AttributesColumns []ColumnMapping // A prefix -> column mapping AttributesTable string MetricColumn *string KeysToColumns map[string]string ArrayColumns map[string]bool ReservedKeys []string SelectColumns []string DefaultFilter string IgnoredFilters map[string]bool }
  • KeysToColumns把已知键映射到 ClickHouse 固定列;
  • 未映射到的键会被当作扩展属性(extended attribute key)处理,走属性过滤分支;
  • AttributesColumns通过前缀匹配(GetAttributesColumn)把自定义属性解析到对应的列。

4. 过滤条件到 SQL 的翻译

在 listener/listener.go 的appendRules中,不同类型键值的翻译策略各不相同:

  • 默认键(BodyColumn):纯字母数字的值生成hasTokenCaseInsensitive(column, value)(ClickHouse 分词级不区分大小写包含匹配);含特殊字符的值则走wildcardValue+ILIKE;
  • 固定列:=生成toString(column) = value(保证字符串语义),大小比较直接使用>,>=,<,<=;
  • 扩展属性列:调用getAttributeFilterExpr生成基于 ClickHousearrayFilter的表达式,如notEmpty(arrayFilter((k, v) -> k = key AND v = value, column)),大小比较还会用toFloat64OrNull(...)包裹以保证数值语义;
  • !=运算符:在ExitKey_val_search_expr中被转换为NOT (...)包裹形式,语义上等价于"键值不等于"。

5. 测试验证

解析逻辑有完整的单测支撑:

  • listener/listener_test.go:覆盖Unquote(引号剥离与转义还原)与NumericValue(时间后缀换算)等关键函数;
  • queryparser/queryparser_test.go:覆盖无键正文、通配符转%、多值属性、含冒号值、引号空格值等解析场景。

各产品模块的专属搜索

上述语法在所有模块通用,但每个模块都结合自己的数据结构与自动注入属性做了定制。各模块的完整指南见:

  • Session Search(会话搜索)
  • Error Search(错误搜索)
  • Log Search(日志搜索)
  • Trace Search(追踪搜索)
  • Event Search(事件搜索)

下面提炼几个模块的关键差异点,方便快速上手。

会话搜索

  • 默认键跨多属性:无键输入会同时作用于email、device_id、identifier、city、country等多个属性,例如输入highlight等价于email=*highlight* OR city=*highlight*;
  • 点击行为检索:SDK 记录clickSelector(元素 tag/id/class 拼接的选择器)与clickInnerText(元素文本,最多前 2000 字符),可搜索clickSelector=svg、clickTextContent="Last 30 days";
  • 访问 URL 检索:使用visited-url过滤键,例如visited-url="https://app.highlight.io/",配合通配符/正则可实现visited-url=*sessions*、visited-url=/.+\d/sessions.+/;
  • 常用自动注入属性:active_length、browser_name、browser_version、city、completed、country、device_id、environment、first_time、has_comments、has_errors、has_rage_clicks、identified、identifier、ip、length、os_name、os_version、pages_visited、sample、service_version、state、viewed_by_anyone、viewed_by_me等。其中completed=false可查看实时(live)会话。

日志搜索

  • 默认键为message:输入excluding session due to no user interaction events即可找到log.info("excluding session due to no user interaction events")这条日志;
  • 常用自动注入属性:code.filepath、code.function、code.lineno、environment、host.name、level、message、os.description、os.type、secure_session_id、service_name、service_version、source、span_id、trace_id等;
  • 实用技巧:用secure_session_id EXISTS过滤出所有与会话关联的日志。

追踪搜索

  • 默认键为span_name;
  • 常用自动注入属性:duration(纳秒)、environment、has_errors、highlight.type、parent_span_id、secure_session_id、service_name、service_version、span_kind、span_name、trace_id等;
  • 实用技巧:用trace_id过滤可看到单个 trace 的所有 span 表格视图,点击 span 可查看含火焰图的信息;用duration>1s筛选超过 1 秒的慢 span;用secure_session_id EXISTS只看与会话关联的 span。在搜索框输入时,界面会给出可用属性键的联想建议。

小结

highlight.io 的搜索语法在表达能力与易用性之间做了很好的平衡:key=value的直观键值比较、*通配符与/regex/正则的灵活匹配、exists / not exists的存在性判断、AND / OR / NOT与括号的完备逻辑组合,再加上各模块的默认键与自动注入属性,几乎可以覆盖全栈监控场景下的一切检索需求。而在服务端,这段查询字符串会经由 ANTLR 文法解析、SearchListener 规则回调、TableConfig 键映射,最终被翻译为针对 ClickHouse 列与属性数组的精确 SQL 条件——理解这层实现,能帮助你预测查询的执行形态,写出更符合预期的表达式。

  • 可观测性
  • 后端

【免费下载链接】highlight

highlight.io: The open source, full-stack monitoring platform. Error monitoring, session replay, logging, distributed tracing, and more.

项目地址:https://gitcode.com/gh_mirrors/hi/highlight
点击查看免费下载

相关推荐

上一篇:一台旧电视盒子搞定全家打印:用 amlogic-s9xxx-armbian 搭建 CUPS 网络打印服务器的完整教程
下一篇:MNN 内置 FlatBuffers 二进制格式内部原理:偏移、vtable 与 FlexBuffers 编码全解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询