logstash-filter-kv 2.0.2 → 2.0.3

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 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: []