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. GWCTF 2019]我有一个数据库

    0x00 知识点 phpMyadmin(CVE-2018-12613)后台任意文件包含漏洞 影响版本:4.8.0--4.8.1 payload:/phpmyadmin/?target=db_datad ...

  2. CodeForces - 748F Santa Clauses and a Soccer Championship

    题意:有k对队伍,每对队伍之间将举行两次比赛,两支队伍各主办一次.住宿的地方要求在两支队伍家乡的最短路的结点上或者在两支队伍的家乡.问在选择住宿处最少的情况下,怎么组成这k对队伍? 分析: 1.因为n ...

  3. XML--XML Schema Definition(一)

    参考 https://blog.csdn.net/wangw2008/article/details/83195283 https://blog.csdn.net/lmj623565791/artic ...

  4. hdu 6581 Vacation【思维】

    原题链接: http://acm.hdu.edu.cn/showproblem.php?pid=6581 VacationTime Limit: 10000/5000 MS (Java/Others) ...

  5. 学生信息的添加 Java web简单项目初试(修改)

    错误原因: 1.Java web 的Servlet类没有配置好,并且缺少一个 Dao类(Date Access Object通常用于操作数据库的). 2.代码的某些名称错误,导致数据库数据存储错误. ...

  6. Java 接口理解

    学习Spring有一段时间了,对java也有了一点了解,最不能理解的就是接口, 即使是写了接口并实现了它,依然无法理解它到底有什么用?看了其他几篇博客,总结了一下自己的理解. 在JAVA编程语言中是一 ...

  7. CSS(3)之 less 和rem

    less 预编译脚本语言. LESS 语法 less语法2 LESS中文 rem rem的适配原理 rem 是相对于页面根源素html的字体大小的一个尺寸单位 页面内容可以使用rem为单位,那么htm ...

  8. spring boot打成可执行jar

    第一步 添加 <packaging>jar</packaging> 第二步添加 <build> <plugins> <plugin> < ...

  9. java笔记之输入输出流

    做统计单词个数时的笔记

  10. C语言-再论指针与数组

    指针与数组的天生姻缘1.以指针方式来访问数组元素(1).数组元素使用时不能整体访问,只能是单个访问.访问形式有两种:数组形式和指针形式.(2).数组形式访问数组元素:数组名[下标]:(下标从0开始(3 ...