- 可观测性
- 后端
【免费下载链接】highlight
highlight.io: The open source, full-stack monitoring platform. Error monitoring, session replay, logging, distributed tracing, and more.
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 existsexists还可以与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-graphservice_name=public-graph AND span_name!=gorm.Queryservice_name=worker OR span_name=gorm.Queryservice_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.
相关推荐
Hound正则表达式搜索:5个高级查询语法完全指南
Hound正则表达式搜索:5个高级查询语法完全指南 Hound是一款闪电般快速的代码搜索引擎,专门为开发者提供高效的正则表达式搜索功能。这款开源工具基于Go语言
搜索引擎开发者工具后端前端Kibana搜索语法:正则表达式与模糊查询
Kibana搜索语法:正则表达式与模糊查询 在日常数据检索中,你是否遇到过拼写错误导致搜索结果为空?或者需要查找具有相似格式的数据却不知从何下手?本文将详细介绍
前端数据可视化数据分析后端可观测性掌握Sourcebot搜索语法:正则表达式与布尔逻辑完全指南
掌握Sourcebot搜索语法:正则表达式与布尔逻辑完全指南 Sourcebot是一款自托管工具,帮助开发者和AI智能体快速理解代码库。其强大的搜索功能支持正则
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考