antlr 表达式解析错误
作者:
| 更新日期:读到错误日志,就知道什么错误了 。
本文首发于公众号:天空的代码世界,微信号:tiankonguse
零、背景
之前在文章《STL 里面 coredump 了,如何定位?》里提到,我们用的了 antlr 库。
最近又遇到一个问题,和这个库有关,记录一下问题与分析过程。
一、问题
2024年8月19日,周一,走查线上服务时,意外发现线上服务的启动日志文件里在刷日志。
模块负责人回答说这个错误日志好像一直都有。
对于这个回答,我是挺意外的。
我的原则是不能放过任何一个错误,每个错误背后都代表存在缺陷,这个缺陷可能产生无数的连锁反应,最终间接影响业务,甚至极端情况下可能导致故障。
所以,针对这个错误日志,是肯定需要去找到原因并解决的。
2024年9月2日,周一,走查线上服务,再次遇到这个错误日志。
2024年9月12日,周四,走查线上服务,第三次发现这个错误日志。
事不过三,这周对应的模块负责人刚好休假了,所以我打算去看下这个到底是啥错误,原因是什么,以及如何解决。
二、分析
首先看错误日志,可以大概猜到含义。
line 1:0 token recognition error at: '"'
line 1:1 token recognition error at: '"'
line 1:2 mismatched input '<EOF>' expecting {IDENTIFIER, '_', UDF, 'for_each(', '(', '!', INT, STRING, FLOAT, NEWLINE}
错误日志分为三行。
第一行的含义是在第一行的第一个 token, 读取到双引号 "
,识别错误。
第二行的含义是在第一行的第二个 token, 读取到双引号 "
,识别错误。
第三行的含义是在第一行的第三个 token, 读取到 <EOF>
,预期不能结束。
根据上面的三行错误,可以反向推导出输入的表达式是一个双引号的空字符串 ""
,但不知为什么,这个 antlr 是不识别的 。
进一步推导,可以推测,这里想要表达空字符串,但是却没有被识别到。
这意味着字符串不是使用双引号表示的。
线上大量的使用了字符串,比如很多图片拼接和URL拼接,都使用到了字符串。
去看一下配置,原来使用的单引号。
由此,可以进一步推测出,所有的字符串都需要使用单引号,不能使用双引号。
线上配置搜索下双引号,有 8 条记录使用了双引号,不过这 8 个字段都是要废弃的字段。
怪不得没有业务反馈这些配置有问题的字段的数据不符合预期呢。
三、antlr 源码
现在已经确定是 antlr 的问题了,所以需要去看 antlr 的源码。
搜索 token recognition error
, 可以搜到两个文件有出现。
第一个是类的注释说明:Errors from the lexer are never passed to the parser. Either you want to keep going or you do not upon token recognition error.
第二个就是具体的代码,刚好对应输出的日志。
tokenStartLine 对应行号。
tokenStartCharPositionInLine 对应错误在行中的偏移量。
text 就是从偏移量读取到的字符。
至于具体为何表达式会解析错误,这个不归 antlr 负责。
因为 antlr 是通用的规则解析引擎,具体的规则需要使用方自己去定义。
所以还需要去看规则语法树。
三、语法规则
对于一个规则引擎,我们一般先定义一个语法树,储存在 Expr.g4 文件里。
查看下 STRING 的定义,就可以发现,定义的时候,两边是单引号。
到这里,一切都得到解释。
四、总结
字符串使用单引号定义,线上存在 8 个字段使用了双引号。
不过恰好这 8 个字段是要废弃的,即大部分都没有值。
而规则解析错误时,默认也会兜底也是返回空值。
因此业务没有反馈这些字段不符合预期。
唯一的问题是,这些字段解析时,会被打印一个错误日志。
如果线上大量的访问这些字段,大量的打印日志,还是比较影响性能的。
所以还是需要解决这个问题。
解决方案也很简单,把所有的双引号修改为单引号。
那改如何预防以后发生类似的问题呢?
事前分为两个方法。
第一:完善文档与团队内宣讲。
第二:表达式的配置要管理台化,然后管理台可以预检查合法性。
对于不走管理台的特殊场景,就可能导致配置确实配错了。
这就需要事中主动发现,主动告警。
具体来说就是服务主动识别错误,并进行监控告警以及流水上报。
这样通过事前预防,事中监控告警,事后查询流水来修复问题来完美解决了。
五、最后
整体看下来,这个其实是一个非常小的问题。
这里记录下来想表达三个东西。
第一:很多问题,耐心去看问题的表象,通过问题的表层信息,就可以推导出很多结论。
第二:问题背后模块的基本原理需要大概知道,否则只知道字符非法,但是不知道为啥非法,也不知道怎么查看源码。
第三:解决问题很简单,避免问题再次发生更为重要。
《完》
-EOF-
本文公众号:天空的代码世界
个人微信号:tiankonguse
公众号ID:tiankonguse-code
本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。