logstash-codec-protobuf 1.0.5 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1 -1
- data/README.md +55 -28
- data/lib/logstash/codecs/protobuf.rb +238 -201
- data/logstash-codec-protobuf.gemspec +3 -2
- data/spec/codecs/protobuf3_spec.rb +143 -10
- data/spec/codecs/protobuf_spec.rb +17 -16
- data/spec/helpers/pb3/ProbeResult_pb.rb +26 -0
- data/spec/helpers/pb3/dnsmessage_pb.rb +82 -0
- data/spec/helpers/pb3/integertest_pb.rb +20 -0
- metadata +24 -30
- data/lib/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0.jar +0 -0
- data/lib/org/apache/kafka/kafka-clients/0.11.0.0/kafka-clients-0.11.0.0.jar +0 -0
- data/lib/org/apache/logging/log4j/log4j-api/2.8.2/log4j-api-2.8.2.jar +0 -0
- data/lib/org/apache/logging/log4j/log4j-slf4j-impl/2.8.2/log4j-slf4j-impl-2.8.2.jar +0 -0
- data/lib/org/slf4j/slf4j-api/1.7.24/slf4j-api-1.7.24.jar +0 -0
- data/lib/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar +0 -0
- data/lib/org/xerial/snappy/snappy-java/1.1.2.6/snappy-java-1.1.2.6.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/kafka-clients-0.11.0.0.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/log4j-api-2.8.2.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/log4j-slf4j-impl-2.8.2.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/lz4-1.3.0.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/slf4j-api-1.7.24.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/slf4j-api-1.7.25.jar +0 -0
- data/vendor/jar-dependencies/runtime-jars/snappy-java-1.1.2.6.jar +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c7759d64f37dcbb075892f198b46795d1b35964b66341e70107d13a9065de70b
|
4
|
+
data.tar.gz: db8bfbab2b9cdd7f3dbdbdaa8c0f8b12ab46b7ae4ef024cbc0b35706f3dc08e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72147c0788aed161306b74f8537259b04c615313c85d803e51eca96abdfd0bfdee2fb0f415fbd2eb56e66f08e69a46e8e43403175966ec9178dcfa0133e90e4c
|
7
|
+
data.tar.gz: 1872037c0dff0b05cfaba1d9ac6fe663b12d8130cdab8931d5a3e8b4fc03af9b48720120835bf5acd941a1c0814276a797f9f7a1081ee0d5e4bdfdc63d444fab
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -4,51 +4,74 @@ This is a codec plugin for [Logstash](https://github.com/elastic/logstash) to pa
|
|
4
4
|
|
5
5
|
# Prerequisites and Installation
|
6
6
|
|
7
|
-
* prepare your ruby versions of the protobuf definitions
|
7
|
+
* prepare your ruby versions of the protobuf definitions
|
8
|
+
** For protobuf 2 use the [ruby-protoc compiler](https://github.com/codekitchen/ruby-protocol-buffers).
|
9
|
+
** For protobuf 3 use the [official google protobuf compiler](https://developers.google.com/protocol-buffers/docs/reference/ruby-generated).
|
8
10
|
* install the codec: `bin/logstash-plugin install logstash-codec-protobuf`
|
9
11
|
* use the codec in your logstash config file. See details below.
|
10
12
|
|
11
13
|
## Configuration
|
12
14
|
|
13
|
-
include_path (required): an array of strings with filenames
|
15
|
+
include_path (required): an array of strings with filenames where logstash can find your protobuf definitions. Please provide absolute paths. For directories it will only try to import files ending on .rb
|
14
16
|
|
15
|
-
class_name (required): the name of the protobuf class that is to be decoded or encoded.
|
17
|
+
class_name (required): the name of the protobuf class that is to be decoded or encoded. For protobuf 2 separate the modules with ::. For protobuf 3 use single dots. See examples below.
|
18
|
+
|
19
|
+
protobuf_version (optional): set this to 3 if you want to use protobuf 3 definitions. Defaults to 2.
|
16
20
|
|
17
21
|
## Usage example: decoder
|
18
22
|
|
19
23
|
Use this as a codec in any logstash input. Just provide the name of the class that your incoming objects will be encoded in, and specify the path to the compiled definition.
|
20
|
-
Here's an example for a kafka input:
|
24
|
+
Here's an example for a kafka input with protobuf 2:
|
25
|
+
|
26
|
+
kafka
|
27
|
+
{
|
28
|
+
zk_connect => "127.0.0.1"
|
29
|
+
topic_id => "unicorns_protobuffed"
|
30
|
+
codec => protobuf
|
31
|
+
{
|
32
|
+
class_name => "Animals::Unicorn"
|
33
|
+
include_path => ['/path/to/pb_definitions/Animal.pb.rb', '/path/to/pb_definitions/Unicorn.pb.rb']
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
Example for protobuf 3:
|
21
38
|
|
22
39
|
kafka
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
40
|
+
{
|
41
|
+
zk_connect => "127.0.0.1"
|
42
|
+
topic_id => "unicorns_protobuffed"
|
43
|
+
codec => protobuf
|
44
|
+
{
|
45
|
+
class_name => "Animals.Unicorn"
|
46
|
+
include_path => ['/path/to/pb_definitions/Animal_pb.rb', '/path/to/pb_definitions/Unicorn_pb.rb']
|
47
|
+
protobuf_version => 3
|
48
|
+
}
|
49
|
+
}
|
32
50
|
|
33
|
-
###
|
51
|
+
### Class loading order
|
34
52
|
|
35
|
-
Imagine you have the following protobuf relationship: class
|
53
|
+
Imagine you have the following protobuf version 2 relationship: class Unicorn lives in namespace Animal::Horse and uses another class Wings.
|
36
54
|
|
37
|
-
module
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
55
|
+
module Animal
|
56
|
+
module Horse
|
57
|
+
class Unicorn
|
58
|
+
set_fully_qualified_name "Animal.Horse.Unicorn"
|
59
|
+
optional ::Animal::Bodypart::Wings, :wings, 1
|
60
|
+
optional :string, :name, 2
|
61
|
+
# here be more field definitions
|
44
62
|
|
45
|
-
Make sure to put the referenced
|
63
|
+
Make sure to put the referenced wings class first in the include_path:
|
46
64
|
|
47
|
-
include_path => ['/path/to/
|
65
|
+
include_path => ['/path/to/pb_definitions/wings.pb.rb','/path/to/pb_definitions/unicorn.pb.rb']
|
48
66
|
|
49
67
|
Set the class name to the parent class:
|
50
68
|
|
51
|
-
class_name => "
|
69
|
+
class_name => "Animal::Horse::Unicorn"
|
70
|
+
|
71
|
+
for protobuf 2. For protobuf 3 use
|
72
|
+
|
73
|
+
class_name => "Animal.Horse.Unicorn"
|
74
|
+
|
52
75
|
|
53
76
|
## Usage example: encoder
|
54
77
|
|
@@ -59,14 +82,18 @@ The configuration of the codec for encoding logstash events for a protobuf outpu
|
|
59
82
|
|
60
83
|
## Troubleshooting
|
61
84
|
|
62
|
-
###
|
85
|
+
### Protobuf 2
|
86
|
+
#### "uninitialized constant SOME_CLASS_NAME"
|
63
87
|
|
64
88
|
If you include more than one definition class, consider the order of inclusion. This is especially relevant if you include whole directories. A definition might refer to another definition that is not loaded yet. In this case, please specify the files in the include_path variable in reverse order of reference. See 'Example with referenced definitions' above.
|
65
89
|
|
66
|
-
|
90
|
+
#### no protobuf output
|
91
|
+
|
92
|
+
Maybe your protobuf definition does not fullfill the requirements and needs additional fields. Run logstash with the --debug flag and search for error messages.
|
67
93
|
|
68
|
-
|
94
|
+
### Protobuf 3
|
69
95
|
|
96
|
+
Tba.
|
70
97
|
|
71
98
|
## Limitations and roadmap
|
72
99
|
|
@@ -8,7 +8,7 @@ require 'protocol_buffers' # https://github.com/codekitchen/ruby-protocol-buffer
|
|
8
8
|
#
|
9
9
|
# Requires the protobuf definitions as ruby files. You can create those using the [ruby-protoc compiler](https://github.com/codekitchen/ruby-protocol-buffers).
|
10
10
|
#
|
11
|
-
# The following shows a usage example for decoding events from a kafka stream:
|
11
|
+
# The following shows a usage example for decoding protobuf 2 encoded events from a kafka stream:
|
12
12
|
# [source,ruby]
|
13
13
|
# kafka
|
14
14
|
# {
|
@@ -21,21 +21,39 @@ require 'protocol_buffers' # https://github.com/codekitchen/ruby-protocol-buffer
|
|
21
21
|
# }
|
22
22
|
# }
|
23
23
|
#
|
24
|
+
# Same example for protobuf 3:
|
25
|
+
# [source,ruby]
|
26
|
+
# kafka
|
27
|
+
# {
|
28
|
+
# zk_connect => "127.0.0.1"
|
29
|
+
# topic_id => "your_topic_goes_here"
|
30
|
+
# codec => protobuf
|
31
|
+
# {
|
32
|
+
# class_name => "Animal.Unicorn"
|
33
|
+
# include_path => ['/path/to/protobuf/definitions/UnicornProtobuf_pb.rb']
|
34
|
+
# protobuf_version => 3
|
35
|
+
# }
|
36
|
+
# }
|
37
|
+
#
|
24
38
|
|
25
39
|
class LogStash::Codecs::Protobuf < LogStash::Codecs::Base
|
26
40
|
config_name 'protobuf'
|
27
41
|
|
28
42
|
# Name of the class to decode.
|
29
|
-
# If your protobuf definition contains modules, prepend them to the class name with double colons like so:
|
43
|
+
# If your protobuf 2 definition contains modules, prepend them to the class name with double colons like so:
|
30
44
|
# [source,ruby]
|
31
|
-
# class_name => "
|
45
|
+
# class_name => "Animal::Horse::Unicorn"
|
32
46
|
#
|
33
47
|
# This corresponds to a protobuf definition starting as follows:
|
34
48
|
# [source,ruby]
|
35
|
-
# module
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
49
|
+
# module Animal
|
50
|
+
# module Horse
|
51
|
+
# class Unicorn
|
52
|
+
# # here are your field definitions.
|
53
|
+
#
|
54
|
+
# For protobuf 3 separate the modules with single dots.
|
55
|
+
# [source,ruby]
|
56
|
+
# class_name => "Animal.Horse.Unicorn"
|
39
57
|
#
|
40
58
|
# If your class references other definitions: you only have to add the main class here.
|
41
59
|
config :class_name, :validate => :string, :required => true
|
@@ -44,19 +62,19 @@ class LogStash::Codecs::Protobuf < LogStash::Codecs::Base
|
|
44
62
|
# When using more than one file, make sure to arrange the files in reverse order of dependency so that each class is loaded before it is
|
45
63
|
# refered to by another.
|
46
64
|
#
|
47
|
-
# Example: a class
|
65
|
+
# Example: a class _Unicorn_ referencing another protobuf class _Wings_
|
48
66
|
# [source,ruby]
|
49
|
-
# module
|
50
|
-
# module
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
67
|
+
# module Animal
|
68
|
+
# module Horse
|
69
|
+
# class Unicorn
|
70
|
+
# set_fully_qualified_name "Animal.Horse.Unicorn"
|
71
|
+
# optional ::Animal::Bodypart::Wings, :wings, 1
|
72
|
+
# optional :string, :name, 2
|
73
|
+
# # here be more field definitions
|
56
74
|
#
|
57
75
|
# would be configured as
|
58
76
|
# [source,ruby]
|
59
|
-
# include_path => ['/path/to/protobuf/definitions/
|
77
|
+
# include_path => ['/path/to/protobuf/definitions/Wings.pb.rb','/path/to/protobuf/definitions/Unicorn.pb.rb']
|
60
78
|
#
|
61
79
|
# When using the codec in an output plugin:
|
62
80
|
# * make sure to include all the desired fields in the protobuf definition, including timestamp.
|
@@ -66,17 +84,20 @@ class LogStash::Codecs::Protobuf < LogStash::Codecs::Base
|
|
66
84
|
#
|
67
85
|
config :include_path, :validate => :array, :required => true
|
68
86
|
|
69
|
-
# Protocol buffer version switch.
|
87
|
+
# Protocol buffer version switch. Defaults to version 2. Please note that the behaviour for enums varies between the versions.
|
70
88
|
# For protobuf 2 you will get integer representations for enums, for protobuf 3 you'll get string representations due to a different converter library.
|
71
89
|
# Recommendation: use the translate plugin to restore previous behaviour when upgrading.
|
72
|
-
config :
|
90
|
+
config :protobuf_version, :validate => [2,3], :default => 2, :required => true
|
73
91
|
|
92
|
+
# To tolerate faulty messages that cannot be decoded, set this to false. Otherwise the pipeline will stop upon encountering a non decipherable message.
|
93
|
+
config :stop_on_error, :validate => :boolean, :default => false, :required => false
|
74
94
|
|
75
95
|
def register
|
76
96
|
@metainfo_messageclasses = {}
|
77
97
|
@metainfo_enumclasses = {}
|
98
|
+
@metainfo_pb2_enumlist = []
|
78
99
|
include_path.each { |path| load_protobuf_definition(path) }
|
79
|
-
if @
|
100
|
+
if @protobuf_version == 3
|
80
101
|
@pb_builder = Google::Protobuf::DescriptorPool.generated_pool.lookup(class_name).msgclass
|
81
102
|
else
|
82
103
|
@pb_builder = pb2_create_instance(class_name)
|
@@ -85,24 +106,24 @@ class LogStash::Codecs::Protobuf < LogStash::Codecs::Base
|
|
85
106
|
|
86
107
|
|
87
108
|
def decode(data)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
109
|
+
if @protobuf_version == 3
|
110
|
+
decoded = @pb_builder.decode(data.to_s)
|
111
|
+
h = pb3_deep_to_hash(decoded)
|
112
|
+
else
|
113
|
+
decoded = @pb_builder.parse(data.to_s)
|
114
|
+
h = decoded.to_hash
|
115
|
+
end
|
116
|
+
yield LogStash::Event.new(h) if block_given?
|
117
|
+
rescue => e
|
118
|
+
@logger.warn("Couldn't decode protobuf: #{e.inspect}.")
|
119
|
+
if stop_on_error
|
99
120
|
raise e
|
100
121
|
end
|
101
122
|
end # def decode
|
102
123
|
|
103
124
|
|
104
125
|
def encode(event)
|
105
|
-
if @
|
126
|
+
if @protobuf_version == 3
|
106
127
|
protobytes = pb3_encode_wrapper(event)
|
107
128
|
else
|
108
129
|
protobytes = pb2_encode_wrapper(event)
|
@@ -113,22 +134,23 @@ class LogStash::Codecs::Protobuf < LogStash::Codecs::Base
|
|
113
134
|
|
114
135
|
private
|
115
136
|
def pb3_deep_to_hash(input)
|
116
|
-
|
137
|
+
case input
|
138
|
+
when Google::Protobuf::MessageExts # it's a protobuf class
|
117
139
|
result = Hash.new
|
118
140
|
input.to_hash.each {|key, value|
|
119
141
|
result[key] = pb3_deep_to_hash(value) # the key is required for the class lookup of enums.
|
120
142
|
}
|
121
|
-
|
143
|
+
when ::Array
|
122
144
|
result = []
|
123
145
|
input.each {|value|
|
124
146
|
result << pb3_deep_to_hash(value)
|
125
147
|
}
|
126
|
-
|
148
|
+
when ::Hash
|
127
149
|
result = {}
|
128
150
|
input.each {|key, value|
|
129
151
|
result[key] = pb3_deep_to_hash(value)
|
130
152
|
}
|
131
|
-
|
153
|
+
when Symbol # is an Enum
|
132
154
|
result = input.to_s.sub(':','')
|
133
155
|
else
|
134
156
|
result = input
|
@@ -137,65 +159,66 @@ class LogStash::Codecs::Protobuf < LogStash::Codecs::Base
|
|
137
159
|
end
|
138
160
|
|
139
161
|
def pb3_encode_wrapper(event)
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
end
|
162
|
+
data = pb3_encode(event.to_hash, @class_name)
|
163
|
+
pb_obj = @pb_builder.new(data)
|
164
|
+
@pb_builder.encode(pb_obj)
|
165
|
+
rescue ArgumentError => e
|
166
|
+
k = event.to_hash.keys.join(", ")
|
167
|
+
@logger.debug("Encoding error 2. Probably mismatching protobuf definition. Required fields in the protobuf definition are: #{k} and the timestamp field name must not include an @.")
|
168
|
+
raise e
|
169
|
+
rescue => e
|
170
|
+
@logger.debug("Couldn't generate protobuf: #{e.inspect}")
|
171
|
+
raise e
|
151
172
|
end
|
152
173
|
|
153
174
|
|
154
175
|
def pb3_encode(datahash, class_name)
|
155
|
-
|
156
|
-
|
157
|
-
# Preparation: the data cannot be encoded until certain criteria are met:
|
158
|
-
# 1) remove @ signs from keys.
|
159
|
-
# 2) convert timestamps and other objects to strings
|
160
|
-
datahash = datahash.inject({}){|x,(k,v)| x[k.gsub(/@/,'').to_sym] = (should_convert_to_string?(v) ? v.to_s : v); x}
|
176
|
+
if datahash.is_a?(::Hash)
|
177
|
+
|
161
178
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
179
|
+
|
180
|
+
# Preparation: the data cannot be encoded until certain criteria are met:
|
181
|
+
# 1) remove @ signs from keys.
|
182
|
+
# 2) convert timestamps and other objects to strings
|
183
|
+
datahash = datahash.inject({}){|x,(k,v)| x[k.gsub(/@/,'').to_sym] = (should_convert_to_string?(v) ? v.to_s : v); x}
|
184
|
+
|
185
|
+
# Check if any of the fields in this hash are protobuf classes and if so, create a builder for them.
|
186
|
+
meta = @metainfo_messageclasses[class_name]
|
187
|
+
if meta
|
188
|
+
meta.map do | (field_name,class_name) |
|
189
|
+
key = field_name.to_sym
|
190
|
+
if datahash.include?(key)
|
191
|
+
original_value = datahash[key]
|
192
|
+
datahash[key] =
|
193
|
+
if original_value.is_a?(::Array)
|
194
|
+
# make this field an array/list of protobuf objects
|
195
|
+
# value is a list of hashed complex objects, each of which needs to be protobuffed and
|
196
|
+
# put back into the list.
|
197
|
+
original_value.map { |x| pb3_encode(x, class_name) }
|
198
|
+
original_value
|
199
|
+
else
|
200
|
+
r = pb3_encode(original_value, class_name)
|
201
|
+
builder = Google::Protobuf::DescriptorPool.generated_pool.lookup(class_name).msgclass
|
202
|
+
builder.new(r)
|
203
|
+
end # if is array
|
204
|
+
end # if datahash_include
|
205
|
+
end # do
|
206
|
+
end # if meta
|
207
|
+
|
208
|
+
# Check if any of the fields in this hash are enum classes and if so, create a builder for them.
|
209
|
+
meta = @metainfo_enumclasses[class_name]
|
210
|
+
if meta
|
211
|
+
meta.map do | (field_name,class_name) |
|
212
|
+
key = field_name.to_sym
|
213
|
+
if datahash.include?(key)
|
214
|
+
original_value = datahash[key]
|
215
|
+
datahash[key] = case original_value
|
216
|
+
when ::Array
|
174
217
|
original_value.map { |x| pb3_encode(x, class_name) }
|
175
218
|
original_value
|
176
|
-
|
177
|
-
r = pb3_encode(original_value, class_name)
|
178
|
-
builder = Google::Protobuf::DescriptorPool.generated_pool.lookup(class_name).msgclass
|
179
|
-
builder.new(r)
|
180
|
-
end # if is array
|
181
|
-
end # if datahash_include
|
182
|
-
end # do
|
183
|
-
end # if meta
|
184
|
-
# Check if any of the fields in this hash are enum classes and if so, create a builder for them.
|
185
|
-
meta = @metainfo_enumclasses[class_name]
|
186
|
-
if meta
|
187
|
-
meta.map do | (field_name,class_name) |
|
188
|
-
key = field_name.to_sym
|
189
|
-
if datahash.include?(key)
|
190
|
-
original_value = datahash[key]
|
191
|
-
datahash[key] =
|
192
|
-
if original_value.is_a?(::Array)
|
193
|
-
original_value.map { |x| pb3_encode(x, class_name) }
|
194
|
-
original_value
|
195
|
-
else
|
196
|
-
if original_value.is_a?(Fixnum)
|
219
|
+
when Fixnum
|
197
220
|
original_value # integers will be automatically converted into enum
|
198
|
-
else
|
221
|
+
# else
|
199
222
|
# feature request: support for providing integers as strings or symbols.
|
200
223
|
# not fully tested yet:
|
201
224
|
# begin
|
@@ -204,62 +227,59 @@ class LogStash::Codecs::Protobuf < LogStash::Codecs::Base
|
|
204
227
|
# mod.const_get(class_name)
|
205
228
|
# end # do
|
206
229
|
# rescue => e
|
207
|
-
# @logger.debug("Encoding error 3: could not translate #{original_value} into enum.
|
230
|
+
# @logger.debug("Encoding error 3: could not translate #{original_value} into enum. #{e}")
|
208
231
|
# raise e
|
209
232
|
# end
|
210
|
-
end
|
211
|
-
end # if
|
212
|
-
end #
|
213
|
-
end #
|
214
|
-
end
|
233
|
+
end
|
234
|
+
end # if datahash_include
|
235
|
+
end # do
|
236
|
+
end # if meta
|
237
|
+
end
|
215
238
|
datahash
|
216
239
|
end
|
217
240
|
|
218
241
|
def pb2_encode_wrapper(event)
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
raise e
|
229
|
-
end
|
242
|
+
data = pb2_encode(event.to_hash, @class_name)
|
243
|
+
msg = @pb_builder.new(data)
|
244
|
+
msg.serialize_to_string
|
245
|
+
rescue NoMethodError => e
|
246
|
+
@logger.debug("Encoding error 2. Probably mismatching protobuf definition. Required fields in the protobuf definition are: " + event.to_hash.keys.join(", ") + " and the timestamp field name must not include a @. ")
|
247
|
+
raise e
|
248
|
+
rescue => e
|
249
|
+
@logger.debug("Encoding error 1: #{e.inspect}")
|
250
|
+
raise e
|
230
251
|
end
|
231
252
|
|
232
253
|
|
233
|
-
def pb2_encode(datahash, class_name)
|
234
|
-
next unless datahash.is_a?(::Hash)
|
235
|
-
|
236
|
-
# Preparation: the data cannot be encoded until certain criteria are met:
|
237
|
-
# 1) remove @ signs from keys.
|
238
|
-
# 2) convert timestamps and other objects to strings
|
239
|
-
datahash = ::Hash[datahash.map{|(k,v)| [k.to_s.dup.gsub(/@/,''), (should_convert_to_string?(v) ? v.to_s : v)] }]
|
240
|
-
|
241
|
-
# Check if any of the fields in this hash are protobuf classes and if so, create a builder for them.
|
242
|
-
meta = @metainfo_messageclasses[class_name]
|
243
|
-
if meta
|
244
|
-
meta.map do | (k,class_name) |
|
245
|
-
if datahash.include?(k)
|
246
|
-
original_value = datahash[k]
|
247
|
-
p
|
248
|
-
datahash[k] =
|
249
|
-
if original_value.is_a?(::Array)
|
250
|
-
# make this field an array/list of protobuf objects
|
251
|
-
# value is a list of hashed complex objects, each of which needs to be protobuffed and
|
252
|
-
# put back into the list.
|
253
|
-
original_value.map { |x| pb2_encode(x, class_name) }
|
254
|
-
original_value
|
255
|
-
else
|
256
|
-
proto_obj = pb2_create_instance(class_name)
|
257
|
-
proto_obj.new(pb2_encode(original_value, class_name))
|
258
|
-
end # if is array
|
259
|
-
end # if datahash_include
|
260
|
-
end # do
|
261
|
-
end # if meta
|
262
254
|
|
255
|
+
def pb2_encode(datahash, class_name)
|
256
|
+
if datahash.is_a?(::Hash)
|
257
|
+
# Preparation: the data cannot be encoded until certain criteria are met:
|
258
|
+
# 1) remove @ signs from keys.
|
259
|
+
# 2) convert timestamps and other objects to strings
|
260
|
+
datahash = ::Hash[datahash.map{|(k,v)| [k.to_s.dup.gsub(/@/,''), (should_convert_to_string?(v) ? v.to_s : v)] }]
|
261
|
+
|
262
|
+
# Check if any of the fields in this hash are protobuf classes and if so, create a builder for them.
|
263
|
+
meta = @metainfo_messageclasses[class_name]
|
264
|
+
if meta
|
265
|
+
meta.map do | (k,c) |
|
266
|
+
if datahash.include?(k)
|
267
|
+
original_value = datahash[k]
|
268
|
+
datahash[k] =
|
269
|
+
if original_value.is_a?(::Array)
|
270
|
+
# make this field an array/list of protobuf objects
|
271
|
+
# value is a list of hashed complex objects, each of which needs to be protobuffed and
|
272
|
+
# put back into the list.
|
273
|
+
original_value.map { |x| pb2_encode(x, c) }
|
274
|
+
original_value
|
275
|
+
else
|
276
|
+
proto_obj = pb2_create_instance(c)
|
277
|
+
proto_obj.new(pb2_encode(original_value, c)) # this line is reached in the colourtest for an enum. Enums should not be instantiated. Should enums even be in the messageclasses? I dont think so! TODO bug
|
278
|
+
end # if is array
|
279
|
+
end # if datahash_include
|
280
|
+
end # do
|
281
|
+
end # if meta
|
282
|
+
end
|
263
283
|
datahash
|
264
284
|
end
|
265
285
|
|
@@ -270,104 +290,121 @@ class LogStash::Codecs::Protobuf < LogStash::Codecs::Base
|
|
270
290
|
|
271
291
|
|
272
292
|
def pb2_create_instance(name)
|
273
|
-
|
274
|
-
|
275
|
-
name.split('::').inject(Object) { |n,c| n.const_get c }
|
276
|
-
end
|
293
|
+
@logger.debug("Creating instance of " + name)
|
294
|
+
name.split('::').inject(Object) { |n,c| n.const_get c }
|
277
295
|
end
|
278
296
|
|
279
297
|
|
280
298
|
def pb3_metadata_analyis(filename)
|
281
299
|
regex_class_name = /\s*add_message "(?<name>.+?)" do\s+/ # TODO optimize both regexes for speed (negative lookahead)
|
282
300
|
regex_pbdefs = /\s*(optional|repeated)(\s*):(?<name>.+),(\s*):(?<type>\w+),(\s*)(?<position>\d+)(, \"(?<enum_class>.*?)\")?/
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
if
|
297
|
-
field_name =
|
298
|
-
|
299
|
-
|
300
|
-
if type == "message"
|
301
|
-
@metainfo_messageclasses[class_name][field_name] = field_class_name
|
302
|
-
elsif type == "enum"
|
303
|
-
@metainfo_enumclasses[class_name][field_name] = field_class_name
|
304
|
-
end
|
301
|
+
class_name = ""
|
302
|
+
type = ""
|
303
|
+
field_name = ""
|
304
|
+
File.readlines(filename).each do |line|
|
305
|
+
if ! (line =~ regex_class_name).nil?
|
306
|
+
class_name = $1
|
307
|
+
@metainfo_messageclasses[class_name] = {}
|
308
|
+
@metainfo_enumclasses[class_name] = {}
|
309
|
+
end # if
|
310
|
+
if ! (line =~ regex_pbdefs).nil?
|
311
|
+
field_name = $1
|
312
|
+
type = $2
|
313
|
+
field_class_name = $4
|
314
|
+
if type == "message"
|
315
|
+
@metainfo_messageclasses[class_name][field_name] = field_class_name
|
316
|
+
elsif type == "enum"
|
317
|
+
@metainfo_enumclasses[class_name][field_name] = field_class_name
|
305
318
|
end
|
306
|
-
end
|
307
|
-
|
308
|
-
@logger.warn("Error 3: unable to read pb definition from file " + filename+ ". Reason: #{e.inspect}. Last settings were: class #{class_name} field #{field_name} type #{type}. Backtrace: " + e.backtrace.inspect.to_s)
|
309
|
-
raise e
|
310
|
-
end
|
319
|
+
end # if
|
320
|
+
end # readlines
|
311
321
|
if class_name.nil?
|
312
322
|
@logger.warn("Error 4: class name not found in file " + filename)
|
313
323
|
raise ArgumentError, "Invalid protobuf file: " + filename
|
314
|
-
end
|
324
|
+
end
|
325
|
+
rescue Exception => e
|
326
|
+
@logger.warn("Error 3: unable to read pb definition from file " + filename+ ". Reason: #{e.inspect}. Last settings were: class #{class_name} field #{field_name} type #{type}. Backtrace: " + e.backtrace.inspect.to_s)
|
327
|
+
raise e
|
315
328
|
end
|
329
|
+
|
330
|
+
|
316
331
|
|
317
332
|
def pb2_metadata_analyis(filename)
|
318
|
-
|
319
|
-
|
333
|
+
regex_class_start = /\s*set_fully_qualified_name \"(?<name>.+)\".*?/
|
334
|
+
regex_enum_name = /\s*include ..ProtocolBuffers..Enum\s*/
|
320
335
|
regex_pbdefs = /\s*(optional|repeated)(\s*):(?<type>.+),(\s*):(?<name>\w+),(\s*)(?<position>\d+)/
|
321
336
|
# now we also need to find out which class it contains and the protobuf definitions in it.
|
322
337
|
# We'll unfortunately need that later so that we can create nested objects.
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
+
|
339
|
+
class_name = ""
|
340
|
+
type = ""
|
341
|
+
field_name = ""
|
342
|
+
is_enum_class = false
|
343
|
+
|
344
|
+
File.readlines(filename).each do |line|
|
345
|
+
if ! (line =~ regex_enum_name).nil?
|
346
|
+
is_enum_class= true
|
347
|
+
end
|
348
|
+
|
349
|
+
if ! (line =~ regex_class_start).nil?
|
350
|
+
class_name = $1.gsub('.',"::").split('::').map {|word| word.capitalize}.join('::')
|
351
|
+
if is_enum_class
|
352
|
+
@metainfo_pb2_enumlist << class_name.downcase
|
338
353
|
end
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
354
|
+
is_enum_class= false # reset when next class starts
|
355
|
+
end
|
356
|
+
if ! (line =~ regex_pbdefs).nil?
|
357
|
+
type = $1
|
358
|
+
field_name = $2
|
359
|
+
if type =~ /::/
|
360
|
+
clean_type = type.gsub(/^:/,"")
|
361
|
+
e = @metainfo_pb2_enumlist.include? clean_type.downcase
|
362
|
+
|
363
|
+
if e
|
364
|
+
if not @metainfo_enumclasses.key? class_name
|
365
|
+
@metainfo_enumclasses[class_name] = {}
|
366
|
+
end
|
367
|
+
@metainfo_enumclasses[class_name][field_name] = clean_type
|
368
|
+
else
|
369
|
+
if not @metainfo_messageclasses.key? class_name
|
370
|
+
@metainfo_messageclasses[class_name] = {}
|
371
|
+
end
|
372
|
+
@metainfo_messageclasses[class_name][field_name] = clean_type
|
345
373
|
end
|
346
374
|
end
|
347
375
|
end
|
348
|
-
rescue Exception => e
|
349
|
-
@logger.warn("Error 3: unable to read pb definition from file " + filename+ ". Reason: #{e.inspect}. Last settings were: class #{class_name} field #{field_name} type #{type}. Backtrace: " + e.backtrace.inspect.to_s)
|
350
|
-
raise e
|
351
376
|
end
|
352
377
|
if class_name.nil?
|
353
378
|
@logger.warn("Error 4: class name not found in file " + filename)
|
354
379
|
raise ArgumentError, "Invalid protobuf file: " + filename
|
355
|
-
end
|
380
|
+
end
|
381
|
+
rescue LoadError => e
|
382
|
+
raise ArgumentError.new("Could not load file: " + filename + ". Please try to use absolute pathes. Current working dir: " + Dir.pwd + ", loadpath: " + $LOAD_PATH.join(" "))
|
383
|
+
rescue => e
|
384
|
+
|
385
|
+
@logger.warn("Error 3: unable to read pb definition from file " + filename+ ". Reason: #{e.inspect}. Last settings were: class #{class_name} field #{field_name} type #{type}. Backtrace: " + e.backtrace.inspect.to_s)
|
386
|
+
raise e
|
356
387
|
end
|
388
|
+
|
357
389
|
|
358
390
|
def load_protobuf_definition(filename)
|
359
|
-
|
360
|
-
if filename.
|
361
|
-
@logger.debug("Including protobuf file: " + filename)
|
391
|
+
if filename.end_with? ('.rb')
|
392
|
+
if (Pathname.new filename).absolute?
|
362
393
|
require filename
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
394
|
+
else
|
395
|
+
require_relative filename # needed for the test cases
|
396
|
+
r = File.expand_path(File.dirname(__FILE__))
|
397
|
+
filename = File.join(r, filename) # make the path absolute
|
398
|
+
end
|
399
|
+
|
400
|
+
if @protobuf_version == 3
|
401
|
+
pb3_metadata_analyis(filename)
|
402
|
+
else
|
403
|
+
pb2_metadata_analyis(filename)
|
370
404
|
end
|
405
|
+
|
406
|
+
else
|
407
|
+
@logger.warn("Not a ruby file: " + filename)
|
371
408
|
end
|
372
409
|
end
|
373
410
|
|