logstash-filter-kv 2.0.2 → 2.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 67fc628e91472f712e8195aaff0bea1cce4e1538
4
- data.tar.gz: 84912044f62dc43af4529cac310638b72ca8a26f
3
+ metadata.gz: 7823f827ecc2a7359e187f76eeef30ace026483d
4
+ data.tar.gz: 15efba89f825cfe3431ad6fc66f1fa8eb06ff1d9
5
5
  SHA512:
6
- metadata.gz: 3c4deb2402e12be72d4660fa567e01d29802e1024b0571dd7c3ce42f08237e0c7dcbc85b02cc2c9c68dc64070be2e6bb93ab5839b52114410ac746abd9bf6a03
7
- data.tar.gz: 7626be289be507fa32ebee6669d39f29805be40b5d1947d052da14837652d2c1b54be5f5fe5fe93784362418a4d554e569207281709fe4896efe935019b5f450
6
+ metadata.gz: b5c3322e32a0c28e133ec2a7b7a347019e208c70d7ec84afca3ec086c1d0389f3b20effce8392a8646b333e378dfa4a917d00f7b92731da3c2953ce6291d8604
7
+ data.tar.gz: f0fd946fba942d8e8c5d0c678e85619b522b32e12ba6b2bce49e2bf6166cc6744b5ed9838e94ebc267565b1336304778b8551d431e01978d3b11336b041d63a4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
+ ## 2.0.3
2
+ - fixed fixed short circuit expressions, some optimizations, added specs, PR #20
3
+ - fixed event field assignment, PR #21
4
+
1
5
  ## 2.0.0
2
- - Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
6
+ - Plugins were updated to follow the new shutdown semantic, this mainly allows Logstash to instruct input plugins to terminate gracefully,
3
7
  instead of using Thread.raise on the plugins' threads. Ref: https://github.com/elastic/logstash/pull/3895
4
8
  - Dependency on logstash-core update to 2.0
5
9
 
data/README.md CHANGED
@@ -1,5 +1,8 @@
1
1
  # Logstash Plugin
2
2
 
3
+ [![Build
4
+ Status](http://build-eu-00.elastic.co/view/LS%20Plugins/view/LS%20Filters/job/logstash-plugin-filter-kv-unit/badge/icon)](http://build-eu-00.elastic.co/view/LS%20Plugins/view/LS%20Filters/job/logstash-plugin-filter-kv-unit/)
5
+
3
6
  This is a plugin for [Logstash](https://github.com/elastic/logstash).
4
7
 
5
8
  It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+
2
3
  require "logstash/filters/base"
3
4
  require "logstash/namespace"
4
5
 
@@ -124,7 +125,7 @@ class LogStash::Filters::KV < LogStash::Filters::Base
124
125
  # An array specifying the parsed keys which should be added to the event.
125
126
  # By default all keys will be added.
126
127
  #
127
- # For example, consider a source like `Hey, from=<abc>, to=def foo=bar`.
128
+ # For example, consider a source like `Hey, from=<abc>, to=def foo=bar`.
128
129
  # To include `from` and `to`, but exclude the `foo` key, you could use this configuration:
129
130
  # [source,ruby]
130
131
  # filter {
@@ -137,7 +138,7 @@ class LogStash::Filters::KV < LogStash::Filters::Base
137
138
  # An array specifying the parsed keys which should not be added to the event.
138
139
  # By default no keys will be excluded.
139
140
  #
140
- # For example, consider a source like `Hey, from=<abc>, to=def foo=bar`.
141
+ # For example, consider a source like `Hey, from=<abc>, to=def foo=bar`.
141
142
  # To exclude `from` and `to`, but retain the `foo` key, you could use this configuration:
142
143
  # [source,ruby]
143
144
  # filter {
@@ -158,10 +159,10 @@ class LogStash::Filters::KV < LogStash::Filters::Base
158
159
  # }
159
160
  config :default_keys, :validate => :hash, :default => {}
160
161
 
161
- # A bool option for removing duplicate key/value pairs. When set to false, only
162
+ # A bool option for removing duplicate key/value pairs. When set to false, only
162
163
  # one unique key/value pair will be preserved.
163
164
  #
164
- # For example, consider a source like `from=me from=me`. `[from]` will map to
165
+ # For example, consider a source like `from=me from=me`. `[from]` will map to
165
166
  # an Array with two elements: `["me", "me"]`. to only keep unique key/value pairs,
166
167
  # you could use this configuration:
167
168
  # [source,ruby]
@@ -193,7 +194,7 @@ class LogStash::Filters::KV < LogStash::Filters::Base
193
194
  # * bracketstwo: [hello
194
195
  config :include_brackets, :validate => :boolean, :default => true
195
196
 
196
- # A boolean specifying whether to drill down into values
197
+ # A boolean specifying whether to drill down into values
197
198
  # and recursively get more key-value pairs from it.
198
199
  # The extra key-value pairs will be stored as subkeys of the root key.
199
200
  #
@@ -208,90 +209,86 @@ class LogStash::Filters::KV < LogStash::Filters::Base
208
209
  config :recursive, :validate => :boolean, :default => false
209
210
 
210
211
  def register
211
- @trim_re = Regexp.new("[#{@trim}]") if !@trim.nil?
212
- @trimkey_re = Regexp.new("[#{@trimkey}]") if !@trimkey.nil?
212
+ @trim_re = Regexp.new("[#{@trim}]") if @trim
213
+ @trimkey_re = Regexp.new("[#{@trimkey}]") if @trimkey
213
214
 
214
215
  valueRxString = "(?:\"([^\"]+)\"|'([^']+)'"
215
216
  valueRxString += "|\\(([^\\)]+)\\)|\\[([^\\]]+)\\]" if @include_brackets
216
- valueRxString += "|((?:\\\\ |[^"+@field_split+"])+))"
217
- @scan_re = Regexp.new("((?:\\\\ |[^"+@field_split+@value_split+"])+)\\s*["+@value_split+"]\\s*"+valueRxString)
218
- end # def register
217
+ valueRxString += "|((?:\\\\ |[^" + @field_split + "])+))"
218
+ @scan_re = Regexp.new("((?:\\\\ |[^" + @field_split + @value_split + "])+)\\s*[" + @value_split + "]\\s*" + valueRxString)
219
219
 
220
- def filter(event)
221
-
220
+ @value_split_re = /[#{@value_split}]/
221
+ end
222
222
 
223
+ def filter(event)
223
224
  kv = Hash.new
224
-
225
225
  value = event[@source]
226
226
 
227
227
  case value
228
- when nil; # Nothing to do
229
- when String; kv = parse(value, event, kv)
230
- when Array; value.each { |v| kv = parse(v, event, kv) }
231
- else
232
- @logger.warn("kv filter has no support for this type of data",
233
- :type => value.class, :value => value)
234
- end # case value
228
+ when nil
229
+ # Nothing to do
230
+ when String
231
+ kv = parse(value, event, kv)
232
+ when Array
233
+ value.each { |v| kv = parse(v, event, kv) }
234
+ else
235
+ @logger.warn("kv filter has no support for this type of data", :type => value.class, :value => value)
236
+ end
235
237
 
236
238
  # Add default key-values for missing keys
237
239
  kv = @default_keys.merge(kv)
238
240
 
239
- # If we have any keys, create/append the hash
240
- if kv.length > 0
241
- if @target.nil?
242
- # Default is to write to the root of the event.
243
- dest = event.to_hash
244
- else
245
- if !event[@target].is_a?(Hash)
246
- @logger.debug("Overwriting existing target field", :target => @target)
247
- dest = event[@target] = {}
248
- else
249
- dest = event[@target]
250
- end
251
- end
241
+ return if kv.empty?
252
242
 
253
- dest.merge!(kv)
254
- filter_matched(event)
243
+ if @target
244
+ @logger.debug? && @logger.debug("Overwriting existing target field", :target => @target)
245
+ event[@target] = kv
246
+ else
247
+ kv.each{|k, v| event[k] = v}
255
248
  end
256
- end # def filter
249
+
250
+ filter_matched(event)
251
+ end
257
252
 
258
253
  private
254
+
255
+ def has_value_splitter?(s)
256
+ s =~ @value_split_re
257
+ end
258
+
259
259
  def parse(text, event, kv_keys)
260
- if !event =~ /[@value_split]/
261
- return kv_keys
262
- end
263
-
260
+ # short circuit parsing if the text does not contain the @value_split
261
+ return kv_keys unless has_value_splitter?(text)
262
+
264
263
  # Interpret dynamic keys for @include_keys and @exclude_keys
265
264
  include_keys = @include_keys.map{|key| event.sprintf(key)}
266
265
  exclude_keys = @exclude_keys.map{|key| event.sprintf(key)}
267
-
266
+
268
267
  text.scan(@scan_re) do |key, v1, v2, v3, v4, v5|
269
268
  value = v1 || v2 || v3 || v4 || v5
270
- key = @trimkey.nil? ? key : key.gsub(@trimkey_re, "")
271
-
269
+ key = @trimkey ? key.gsub(@trimkey_re, "") : key
270
+
272
271
  # Bail out as per the values of include_keys and exclude_keys
273
272
  next if not include_keys.empty? and not include_keys.include?(key)
273
+ # next unless include_keys.include?(key)
274
274
  next if exclude_keys.include?(key)
275
275
 
276
276
  key = event.sprintf(@prefix) + key
277
277
 
278
- value = @trim.nil? ? value : value.gsub(@trim_re, "")
278
+ value = @trim ? value.gsub(@trim_re, "") : value
279
279
 
280
- # Bail out if inserting duplicate value in key mapping when unique_values
280
+ # Bail out if inserting duplicate value in key mapping when unique_values
281
281
  # option is set to true.
282
282
  next if not @allow_duplicate_values and kv_keys.has_key?(key) and kv_keys[key].include?(value)
283
283
 
284
284
  # recursively get more kv pairs from the value
285
- if @recursive and value =~ /[@value_split]/
286
- innerKv = Hash.new
287
- innerKv = parse(value, event, innerKv)
288
- if innerKv.length > 0
289
- value = innerKv
290
- end
285
+ if @recursive
286
+ innerKv = parse(value, event, {})
287
+ value = innerKv unless innerKv.empty?
291
288
  end
292
289
 
293
290
  if kv_keys.has_key?(key)
294
- if kv_keys[key].is_a? Array
291
+ if kv_keys[key].is_a?(Array)
295
292
  kv_keys[key].push(value)
296
293
  else
297
294
  kv_keys[key] = [kv_keys[key], value]
@@ -300,6 +297,7 @@ class LogStash::Filters::KV < LogStash::Filters::Base
300
297
  kv_keys[key] = value
301
298
  end
302
299
  end
300
+
303
301
  return kv_keys
304
302
  end
305
- end # class LogStash::Filters::KV
303
+ end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-filter-kv'
4
- s.version = '2.0.2'
4
+ s.version = '2.0.3'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "This filter helps automatically parse messages (or specific event fields) which are of the 'foo=bar' variety."
7
7
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require "logstash/devutils/rspec/spec_helper"
2
4
  require "logstash/filters/kv"
3
5
 
@@ -54,21 +56,108 @@ describe LogStash::Filters::KV do
54
56
  end
55
57
 
56
58
  describe "test value_split" do
57
- config <<-CONFIG
58
- filter {
59
- kv { value_split => ':' }
60
- }
61
- CONFIG
59
+ context "using an alternate splitter" do
60
+ config <<-CONFIG
61
+ filter {
62
+ kv { value_split => ':' }
63
+ }
64
+ CONFIG
62
65
 
63
- sample "hello:=world foo:bar baz=:fizz doublequoted:\"hello world\" singlequoted:'hello world' brackets:(hello world)" do
64
- insist { subject["hello"] } == "=world"
65
- insist { subject["foo"] } == "bar"
66
- insist { subject["baz="] } == "fizz"
67
- insist { subject["doublequoted"] } == "hello world"
68
- insist { subject["singlequoted"] } == "hello world"
69
- insist { subject["brackets"] } == "hello world"
66
+ sample "hello:=world foo:bar baz=:fizz doublequoted:\"hello world\" singlequoted:'hello world' brackets:(hello world)" do
67
+ insist { subject["hello"] } == "=world"
68
+ insist { subject["foo"] } == "bar"
69
+ insist { subject["baz="] } == "fizz"
70
+ insist { subject["doublequoted"] } == "hello world"
71
+ insist { subject["singlequoted"] } == "hello world"
72
+ insist { subject["brackets"] } == "hello world"
73
+ end
74
+ end
75
+ end
76
+
77
+ # these specs are quite implementation specific by testing on the private method
78
+ # has_value_splitter? - this is what I figured would help fixing the short circuit
79
+ # broken code that was previously in place
80
+ describe "short circuit" do
81
+ subject do
82
+ plugin = LogStash::Filters::KV.new(options)
83
+ plugin.register
84
+ plugin
85
+ end
86
+ let(:data) { {"message" => message} }
87
+ let(:event) { LogStash::Event.new(data) }
88
+
89
+ context "plain message" do
90
+ let(:options) { {} }
91
+
92
+ context "without splitter" do
93
+ let(:message) { "foo:bar" }
94
+ it "should short circuit" do
95
+ expect(subject.send(:has_value_splitter?, message)).to be_falsey
96
+ expect(subject).to receive(:has_value_splitter?).with(message).once.and_return(false)
97
+ subject.filter(event)
98
+ end
99
+ end
100
+
101
+ context "with splitter" do
102
+ let(:message) { "foo=bar" }
103
+ it "should not short circuit" do
104
+ expect(subject.send(:has_value_splitter?, message)).to be_truthy
105
+ expect(subject).to receive(:has_value_splitter?).with(message).once.and_return(true)
106
+ subject.filter(event)
107
+ end
108
+ end
70
109
  end
71
110
 
111
+ context "recursive message" do
112
+ context "without inner splitter" do
113
+ let(:inner) { "bar" }
114
+ let(:message) { "foo=#{inner}" }
115
+ let(:options) { {"recursive" => "true"} }
116
+
117
+ it "should extract kv" do
118
+ subject.filter(event)
119
+ expect(event["foo"]).to eq(inner)
120
+ end
121
+
122
+ it "should short circuit" do
123
+ expect(subject.send(:has_value_splitter?, message)).to be_truthy
124
+ expect(subject.send(:has_value_splitter?, inner)).to be_falsey
125
+ expect(subject).to receive(:has_value_splitter?).with(message).once.and_return(true)
126
+ expect(subject).to receive(:has_value_splitter?).with(inner).once.and_return(false)
127
+ subject.filter(event)
128
+ end
129
+ end
130
+
131
+ context "with inner splitter" do
132
+ let(:foo_val) { "1" }
133
+ let(:baz_val) { "2" }
134
+ let(:inner) { "baz=#{baz_val}" }
135
+ let(:message) { "foo=#{foo_val} bar=(#{inner})" } # foo=1 bar=(baz=2)
136
+ let(:options) { {"recursive" => "true"} }
137
+
138
+ it "should extract kv" do
139
+ subject.filter(event)
140
+ expect(event["foo"]).to eq(foo_val)
141
+ expect(event["[bar][baz]"]).to eq(baz_val)
142
+ end
143
+
144
+ it "should short circuit" do
145
+ expect(subject.send(:has_value_splitter?, message)).to be_truthy
146
+ expect(subject.send(:has_value_splitter?, foo_val)).to be_falsey
147
+
148
+ expect(subject.send(:has_value_splitter?, inner)).to be_truthy
149
+ expect(subject.send(:has_value_splitter?, baz_val)).to be_falsey
150
+
151
+ expect(subject).to receive(:has_value_splitter?).with(message).once.and_return(true)
152
+ expect(subject).to receive(:has_value_splitter?).with(foo_val).once.and_return(false)
153
+
154
+ expect(subject).to receive(:has_value_splitter?).with(inner).once.and_return(true)
155
+ expect(subject).to receive(:has_value_splitter?).with(baz_val).once.and_return(false)
156
+
157
+ subject.filter(event)
158
+ end
159
+ end
160
+ end
72
161
  end
73
162
 
74
163
  describe "test field_split" do
@@ -104,12 +193,12 @@ describe LogStash::Filters::KV do
104
193
  describe "test recursive" do
105
194
  config <<-CONFIG
106
195
  filter {
107
- kv {
196
+ kv {
108
197
  recursive => 'true'
109
198
  }
110
199
  }
111
200
  CONFIG
112
-
201
+
113
202
  sample 'IKE="Quick Mode completion" IKE\ IDs = (subnet= x.x.x.x mask= 255.255.255.254 and host=y.y.y.y)' do
114
203
  insist { subject["IKE"] } == 'Quick Mode completion'
115
204
  insist { subject['IKE\ IDs']['subnet'] } == 'x.x.x.x'
@@ -393,7 +482,7 @@ describe LogStash::Filters::KV do
393
482
  insist { subject["__doublequoted"] } == "hello world"
394
483
  end
395
484
  end
396
-
485
+
397
486
  describe "test include_keys with dynamic key" do
398
487
  config <<-CONFIG
399
488
  filter {
@@ -403,13 +492,13 @@ describe LogStash::Filters::KV do
403
492
  }
404
493
  }
405
494
  CONFIG
406
-
495
+
407
496
  sample({"data" => "foo=bar baz=fizz", "key" => "foo"}) do
408
497
  insist { subject["foo"] } == "bar"
409
498
  insist { subject["baz"] } == nil
410
499
  end
411
500
  end
412
-
501
+
413
502
  describe "test exclude_keys with dynamic key" do
414
503
  config <<-CONFIG
415
504
  filter {
@@ -419,7 +508,7 @@ describe LogStash::Filters::KV do
419
508
  }
420
509
  }
421
510
  CONFIG
422
-
511
+
423
512
  sample({"data" => "foo=bar baz=fizz", "key" => "foo"}) do
424
513
  insist { subject["foo"] } == nil
425
514
  insist { subject["baz"] } == "fizz"
metadata CHANGED
@@ -1,17 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-kv
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-14 00:00:00.000000000 Z
11
+ date: 2015-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- requirement: !ruby/object:Gem::Requirement
14
+ name: logstash-core
15
+ version_requirements: !ruby/object:Gem::Requirement
15
16
  requirements:
16
17
  - - '>='
17
18
  - !ruby/object:Gem::Version
@@ -19,10 +20,7 @@ dependencies:
19
20
  - - <
20
21
  - !ruby/object:Gem::Version
21
22
  version: 3.0.0
22
- name: logstash-core
23
- prerelease: false
24
- type: :runtime
25
- version_requirements: !ruby/object:Gem::Requirement
23
+ requirement: !ruby/object:Gem::Requirement
26
24
  requirements:
27
25
  - - '>='
28
26
  - !ruby/object:Gem::Version
@@ -30,20 +28,22 @@ dependencies:
30
28
  - - <
31
29
  - !ruby/object:Gem::Version
32
30
  version: 3.0.0
31
+ prerelease: false
32
+ type: :runtime
33
33
  - !ruby/object:Gem::Dependency
34
- requirement: !ruby/object:Gem::Requirement
34
+ name: logstash-devutils
35
+ version_requirements: !ruby/object:Gem::Requirement
35
36
  requirements:
36
37
  - - '>='
37
38
  - !ruby/object:Gem::Version
38
39
  version: '0'
39
- name: logstash-devutils
40
- prerelease: false
41
- type: :development
42
- version_requirements: !ruby/object:Gem::Requirement
40
+ requirement: !ruby/object:Gem::Requirement
43
41
  requirements:
44
42
  - - '>='
45
43
  - !ruby/object:Gem::Version
46
44
  version: '0'
45
+ prerelease: false
46
+ type: :development
47
47
  description: This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program
48
48
  email: info@elastic.co
49
49
  executables: []