logstash nested内嵌字段 field protobuf解码 codec 的解决办法

主要需求

logstash-codec 下
https://www.elastic.co/guide/en/logstash/6.3/codec-plugins.html
此类解码器

只能应用在原始数据上
比如 https://www.elastic.co/guide/en/logstash/6.3/plugins-codecs-protobuf.html
kafka

{
zk_connect => "127.0.0.1"
topic_id => "your_topic_goes_here"
codec => protobuf
{
class_name => "Animal::Unicorn"
include_path => ['/path/to/protobuf/definitions/UnicornProtobuf.pb.rb']
}
}

对从kafka获取的原始数据进行解码

无法对应原始数据内部的某个字段解码

现在有一个应用场景 内嵌的某个字段是

encode_str= base64_encode(protobuf_encode())

{
"name":"cclient",
"encode_str":"....."
}

codec-plugins 只能应用在完整数据上,而无法应用在encode_str字段上

官方提供一个相关功能的filter
https://www.elastic.co/guide/en/logstash/6.3/plugins-filters-json.html

对内嵌的json字段串 进行json解析

首选的办法

1 自已实现相关插件,完成特定解码工作,实际工作量不大,因为logstash的官方插件都开源

https://github.com/logstash-plugins/logstash-codec-protobuf
https://github.com/logstash-plugins/logstash-filter-json
以我的需求为例,结合logstash-codec-protobuf 和 logstash-filter-json
就能实现一套对内嵌protobuf字段的解码工具
这也是首先想到的办法,但没有执行,主要考虑到,这种场景有一定的通用性
这里是protobuf,如果换成avro,又需要另写一个插件
所以这个方案先放一放(放了一个晚上,同事就已经实现,一共也只花了几小时)

2 这个方案初期只是猜想,并未验证,思路是,既然官方已经提供了许多的codec,同时提供了ruby filter https://www.elastic.co/guide/en/logstash/6.3/plugins-filters-ruby.html,可否通过ruby filter,调用相应的codec来实现相应的解码功能?

由于同事已经实现了方案一,我主要就把精力放在方案2的验证上,先放结果

*可行。

毕竟不是资深的ruby开发,踩了一些坑,多花了些时间

验证步骤

1 首先是验证ruby是否可引用包

网上有很多引用包的资料,但主要是官方包

首先验证json解析

def json_decode(bin)
return LogStash::Json.load(bin)
end

* 官方包可行。
* 第三方未知。

2 尝试引用Protobuf的包,并看可否解码

这里参考了logstash-codec-protobuf的源码
实现
https://github.com/logstash-plugins/logstash-codec-protobuf/blob/master/lib/logstash/codecs/protobuf.rb

def register
@metainfo_messageclasses = {}
@metainfo_enumclasses = {}
@metainfo_pb2_enumlist = []
include_path.each { |path| load_protobuf_definition(path) }
if @protobuf_version ==
@pb_builder = Google::Protobuf::DescriptorPool.generated_pool.lookup(class_name).msgclass
else
@pb_builder = pb2_create_instance(class_name)
end
end def decode(data)
if @protobuf_version ==
decoded = @pb_builder.decode(data.to_s)
h = pb3_deep_to_hash(decoded)
else
decoded = @pb_builder.parse(data.to_s)
h = decoded.to_hash
end
yield LogStash::Event.new(h) if block_given?
rescue => e
@logger.warn("Couldn't decode protobuf: #{e.inspect}.")
if stop_on_error
raise e
end
end # def decode

测试用例
https://github.com/logstash-plugins/logstash-codec-protobuf/blob/master/spec/codecs/protobuf_spec.rb

    let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new("class_name" => "Animal::Unicorn", "include_path" => [pb_include_path + '/pb2/unicorn.pb.rb'])  }
before do
plugin_unicorn.register
end it "should return an event from protobuf encoded data" do data = {:colour => 'rainbow', :horn_length => , :last_seen => , :has_wings => true}
unicorn = Animal::Unicorn.new(data) plugin_unicorn.decode(unicorn.serialize_to_string) do |event|
expect(event.get("colour") ).to eq(data[:colour] )
expect(event.get("horn_length") ).to eq(data[:horn_length] )
expect(event.get("last_seen") ).to eq(data[:last_seen] )
expect(event.get("has_wings") ).to eq(data[:has_wings] )
end
end # it

看到这里就觉得方案大概率能行的通

尝试引用第三方包

require "logstash/codecs/protobuf"
plugin_unicorn = LogStash::Codecs::Protobuf.new("class_name" => "Tutorial::Person", "include_path" => ["/usr/share/logstash/protobuf.pb.rb","/usr/share/logstash/addressbook.pb.rb"],"protobuf_version" => )
plugin_unicorn.register def base64_decode(bin)
return Base64.decode64(bin)
end def protobuf_decode(bin)
return protobuf_event=plugin_unicorn.decode(bin)
end

启动成功
plugin_unicorn.register 是方法调用

*第三方包引用正常(前提是安装了这个包,例require "logstash/codecs/protobuf" 前提是 logstash-plugin install logstash-codec-protobuf)

但执行时报错,没有plugin_unicorn 空引用,猜测是作用域问题

查了下ruby的资料,plugin_unicorn 改为$plugin_unicorn(表示全局作用域),后没有空引用错误

require "logstash/codecs/protobuf"
$plugin_unicorn = LogStash::Codecs::Protobuf.new("class_name" => "Tutorial::Person", "include_path" => ["/usr/share/logstash/protobuf.pb.rb","/usr/share/logstash/addressbook.pb.rb"],"protobuf_version" => )
$plugin_unicorn.register def base64_decode(bin)
return Base64.decode64(bin)
end def protobuf_decode(bin)
return $protobuf_event=plugin_unicorn.decode(bin)
end

新问题是protobuf_decode返回为null,主要时间都花在这个问题上

首先样例数据是同事给的,确认了数据没有问题,调整验证了protobuf的版本,不行,最后试着自已生成了样例数据,问题依旧

注意力都在decode 方法上

def decode(data)
if @protobuf_version ==
decoded = @pb_builder.decode(data.to_s)
h = pb3_deep_to_hash(decoded)
else
decoded = @pb_builder.parse(data.to_s)
h = decoded.to_hash
end
yield LogStash::Event.new(h) if block_given?
rescue => e
@logger.warn("Couldn't decode protobuf: #{e.inspect}.")
if stop_on_error
raise e
end
end # def decode

试了多种情况都不成功,参照decode实现了decode_str

def decode_str(data_str)
@logger.info("cdp source: #{data_str}.")
if @protobuf_version ==
decoded = @pb_builder.decode(data_str)
h = pb3_deep_to_hash(decoded)
return h
else
decoded = @pb_builder.parse(data_str)
h = decoded.to_hash
@logger.info("cdp decoded: #{h}.")
return h
end
end # def decode

decode_str 成功解码

就原始需求来说,问题解决了,但说白了这是在原始插件上作定制,仍然是方案1,方案2的验证,还未结束

测试改代码的时候突然注意到了一个小区别

yield 和 return

ruby的yield细节还不清楚,但任python和nodejs的经验,yield和return的行为不一样是一定的,yield不会返回结果,并通常有个类似next的方法,yield应该是结果null的原因

简单了解ruby yield的相关资料

def protobuf_decode(bin)
return $protobuf_event=plugin_unicorn.decode(bin)
end

改为

def protobuf_decode(bin)
$plugin_unicorn.decode(bin) do |event|
return event.to_hash
end
end

方案二可行,完美

回头再看,yield的坑,完全可以避免

官方源码里的测试用例,有明显的do |event|,因为不熟悉ruby 直觉的写成了return,导致多花了很多时间排查

      plugin_unicorn.decode(unicorn.serialize_to_string) do |event|
expect(event.get("colour") ).to eq(data[:colour] )
expect(event.get("horn_length") ).to eq(data[:horn_length] )
expect(event.get("last_seen") ).to eq(data[:last_seen] )
expect(event.get("has_wings") ).to eq(data[:has_wings] )
end

方案三
方案一是对单独的codec进行插件开发和定制
方案二验证可行,已经完全满足要求,且有一定的通用性,但是虽然不必为每种codec作定制,仍需简单修改相关代码,作插件的初始化(每个插件的参数并不相同)

plugin_unicorn = LogStash::Codecs::Protobuf.new("class_name" => "Tutorial::Person", "include_path" => ["/usr/share/logstash/protobuf.pb.rb","/usr/share/logstash/addressbook.pb.rb"],"protobuf_version" => )

是否可以开发一个过滤器,通过配置(配置沿用官方插件),完成对所有codes的解析?

这个待有精力再研究

logstash nested内嵌字段 field protobuf解码 codec 的解决办法的更多相关文章

  1. 一处折腾笔记:Android内嵌html5加入原生微信分享的解决的方法

    有一段时间没有瞎折腾了. 这周一刚上班萌主过来反映说:微信里面打开聚客宝.分享功能是能够的(这里是用微信自身的js-sdk实现的).可是在android应用里面打开点击就没反应了:接下来狡猾的丁丁在产 ...

  2. sybase数据库和oracle数据库中字段中含有换行符的解决办法

    最近在做数据库从sybase到oracle的迁移工作,sybase数据库表bcp导出后,通过sqlldr导入到oracle数据库,然后oracle数据库通过spool按照sybase数据库bcp的格式 ...

  3. DIV内英文或者数字不换行的问题 解决办法

    word-wrap:break-word; word-break:break-all;

  4. IIS发布的网站,内网和外网不能访问的解决办法

    A.关闭防火墙.控制面板-Windows防火墙-打开或关闭Windows防火墙(不推荐) B.打开:控制面板-Windows防火墙-高级设置-入站规则,在入站规则窗口中找到”BranchCache内容 ...

  5. IntelliJ IDEA的jsp中内置对象方法无法被解析的解决办法

    主要原因是因为缺乏依赖 可以通过添加依赖的方式 导入servlet-api.jar,jsp-api.jar,tomcat-api.jar 这三个jar即可 这三个jar在tomcat的lib目录下有 ...

  6. phpexcel 导出数字类型字段导出错误或者为空解决办法 (原)

    跟我们写excel时候一样,手机号或者较长的数字类型,或被科学计数法和谐,但是如果类型是字符串,长一些的数字就不受影响了. 解决导出被和谐的最简单易懂的,就是最前面拼接‘ ’ 空格,或者字母符号之类, ...

  7. iOS 内嵌 View 的响应

    遇到一个问题就是我有一个 UITextField,点击后不能编辑而是会显示一个自定义的 dialog. 但发现问题是,UITextField 的对点击事件的反应非常吃顿,有时候好使有时候不好使. 后来 ...

  8. MongoDB内嵌文档操作

    实体定义: [BsonIgnoreExtraElements] public class Person : BaseEntity { public string FirstName { get; se ...

  9. 匿名字段 内嵌结构体 interface作为struct field 匿名接口

    interface作为struct field,谈谈golang结构体中的匿名接口 - Go语言中文网 - Golang中文社区 https://studygolang.com/articles/19 ...

随机推荐

  1. [BJDCTF2020]Mark loves cat

    0x00 知识点 GitHack读取源码 $$会导致变量覆盖漏洞 0x01解题 dirsearch扫描一下,发现/.git目录,用githack获取一下源码. <?php include 'fl ...

  2. JavaScipt 动画引擎

    队列操作 jquery中有一个Queue队列的接口,这个模块没有单独拿出来作为一个章节是因为这个是内部专门为动画服务的,Queue队列如同data数据缓存与Deferred异步模型一样,都是jQuer ...

  3. javaweb05 文件的上传一

    2.使用fileupload组件完成文件的上传应用 1).需求: I. 上传 >在upload.jsp页面上使用jQuery实现"新增一个附件","删除附件&quo ...

  4. [Java-基础] 什么是ORM

    ORM简介 ORM:对象关系映射:Object Relational Mapping 用于实现面向对象编程语言里不同类型系统的数据之间的转换 ​ 一般的,数据库绝大部分是面向关系的数据库,但是写代码的 ...

  5. php随机生成国内IP

    public function rand_ip(){ $ip_long = array( array('607649792', '608174079'), //36.56.0.0-36.63.255. ...

  6. dp--背包--开心的金明

    题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间.更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过N元钱就行”.今天 ...

  7. IPO套路

    日前,温州市冠盛汽车零部件集团股份有限公司(以下简称:冠盛集团)在证监会官网更新了招股说明书,距离上会仅一步之遥.值得注意的是,这已经是公司第四次披露招股说明书,2018年6月,公司曾在IPO审核最严 ...

  8. 吴裕雄--天生自然ShellX学习笔记:Shell 函数

    linux shell 可以用户定义函数,然后在shell脚本中可以随便调用. shell中函数的定义格式如下: [ function ] funname [()] { action; [return ...

  9. 吴裕雄--天生自然Linux操作系统:Linux 用户和用户组管理

    Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统. 用户的账号一方面可以帮助系统管理员对使用系统的用户进行 ...

  10. 微服务项目开发学成在线_day03 CMS页面管理开发

    springboot引入mangodb依赖坐标:在spring-boot集成条件下,使用mongodb的DAO层开发. swagger查看接口文档,请求地址:http://localhost:3100 ...