注册ErrorCollector或拦截连线格式的解析错误

register ErrorCollector or intercept parse errors for wire format?

本文关键字:格式 错误 ErrorCollector 注册      更新时间:2023-10-16

当可以定义自定义ErrorCollector类来处理google::protobuf解析错误

struct ErrorCollector : ::google::protobuf::io::ErrorCollector
{
    void AddError(int line, int column, const std::string& message) override
    {
        // log error
    }
    void AddWarning(int line, int column, const std::string& message) override
    {
        // log warning
    }
};

在解析文本文件时,可以使用protobuf TextFormat类并注册自定义ErrorCollector

::google::protobuf::io::IstreamInputStream input_stream(&file);
::google::protobuf::TextFormat::Parser parser;
ErrorCollector error_collector;
parser.RecordErrorsTo(&error_collector);
if (parser.Parse(&input_stream, &msg))
{
    // handle msg
}

解析有线格式,我目前使用Message::ParseFromArray

if (msg.ParseFromArray(data, data_len))
{
    // handle msg
}

这并不允许我指定一个自定义的ErrorCollector

我已经搜索了源代码,但到目前为止还无法找到这是否可能。

  • 是否可以使用ErrorCollector解析线格式?
  • 是否有另一种方法来拦截解析错误并使其可用于客户端代码?

基本上有两种方式会导致连线格式解析失败:

  1. 字节不是有效的protobuf(例如,它们被损坏,或以完全不同的格式)。
  2. 缺少一个必填字段。

对于情形1,protobuf除了"it's invalid"之外不会给你更多的信息。这部分是为了代码的简单性(和速度),但也部分是因为任何提供更多信息的尝试通常都是误导而不是帮助。详细的错误报告对文本格式很有用,因为文本通常是由人类编写的,但机器会产生非常不同的错误。在某些语言中,protobuf实际上会报告特定的错误,比如"end-group标签不匹配start-group标签"。在绝大多数情况下,这个错误实际上只是意味着"字节损坏",但不可避免地,人们认为这个错误试图告诉他们一些他们不理解的更深层次的东西。然后,他们会向堆栈溢出提出问题,比如"我如何确保起始组和结束组标签匹配?",而实际上他们应该比较源和目的地之间的字节数,以缩小它们被损坏的地方。甚至报告发生解析错误的字节位置也不是很有用:protobuf是一种密集编码,这意味着许多随机损坏的字节序列将被解析成功,这意味着解析器可能只会在稍后的某个地方注意到问题,而不是在实际出错的地方。

有一种情况显然是可以用来区分的,那就是情况2(缺少必填字段)——至少,如果您使用了必填字段(我个人建议避免使用它们)。这里有几个选项:

  • 通常,必需的字段检查将错误写入控制台(在stderr上)。你可以拦截这些并使用SetLogHandler以你自己的方式记录它们,但这不会给你结构化的信息,只有文本信息。
  • 要更程序化地检查必填字段,可以将必填字段检查与解析分开。使用MessageLite::ParsePartialFromArray()或其他Partial解析方法之一来解析消息,同时忽略必要字段的缺失。然后可以使用MessageLite::IsInitialized()检查是否设置了所有必需的字段。如果返回false,则使用Message::FindInitializationErrors()获取所有缺失的必填字段的路径列表。