logstash-filter-grok 3.1.2 → 3.2.0

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: caf1f9a43c9403b8c1dfaa59d356e1600b5f1881
4
- data.tar.gz: 2404df9cf8936c1bb6f62867733e53838e87c1c9
3
+ metadata.gz: a34c685833867a04238f8544429c5938cd58a696
4
+ data.tar.gz: 096eb04af33607c064d210c9e007ffbfccb3a9fa
5
5
  SHA512:
6
- metadata.gz: a655b0603e43911b7ab64b2f59936224f656cb5de4064a28a06c378bdc9cba77c1716b3fee14531721abfb2ff09c7d8d53747035d0bc7b269c8e88d7911d1515
7
- data.tar.gz: 306b4b5a7345991750b63f49b497b3fe59773a045680b6e934791a5a9a76280d615c74b7825ca45fd533727fefe485e109f23a42f5af051ea731e0831b5bee97
6
+ metadata.gz: b7aacf53f2a102c96c65597710b529ef0acef10730358ae4c5d4fd32c9dd053e5f226a2696535921fccc75504f205c8580762287ead404d413359bac8e9758f1
7
+ data.tar.gz: 61bfb20dbb5452b957e08e82b5ad54d8b1f89d50dcdd2d0ba6bd37f7e06b2f630e2d92247ddd9e81de7fcefba720f12a3a32b5a21edb99eafb9d6a57331b7b54
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 3.2.0
2
+ - Add new timeout options to cancel grok execution if a threshold time is exceeded
3
+
1
4
  ## 3.1.2
2
5
  - Relax constraint on logstash-core-plugin-api to >= 1.60 <= 2.99
3
6
 
@@ -0,0 +1,77 @@
1
+ class LogStash::Filters::Grok::TimeoutEnforcer
2
+ def initialize(logger, timeout_nanos)
3
+ @logger = logger
4
+ @running = true
5
+ @timeout_nanos = timeout_nanos
6
+
7
+ # Stores running matches with their start time, this is used to cancel long running matches
8
+ # Is a map of Thread => start_time
9
+ @timer_mutex = Mutex.new
10
+ @threads_to_start_time = {}
11
+ end
12
+
13
+ def grok_till_timeout(event, grok, field, value)
14
+ begin
15
+ thread = Thread.current
16
+ start_thread_groking(thread)
17
+ yield
18
+ rescue ::LogStash::Filters::Grok::TimeoutException => e
19
+ # These fields aren't present at the time the exception was raised
20
+ # so we add them here.
21
+ # We could store this metadata in the @threads_to_start_time hash
22
+ # but that'd come at a perf cost and this works just as well.
23
+ e.grok = grok
24
+ e.field = field
25
+ e.value = value
26
+ raise e
27
+ ensure
28
+ stop_thread_groking(thread)
29
+ end
30
+ end
31
+
32
+ def start_thread_groking(thread)
33
+ @timer_mutex.synchronize do
34
+ @threads_to_start_time[thread] = java.lang.System.nanoTime()
35
+ end
36
+ end
37
+
38
+ def stop_thread_groking(thread)
39
+ @timer_mutex.synchronize do
40
+ @threads_to_start_time.delete(thread)
41
+ end
42
+ end
43
+
44
+ def cancel_timed_out!
45
+ @threads_to_start_time.each do |thread,start_time|
46
+ now = java.lang.System.nanoTime # save ourselves some nanotime calls
47
+ elapsed = java.lang.System.nanoTime - start_time
48
+ if elapsed > @timeout_nanos
49
+ elapsed_millis = elapsed / 1000
50
+ thread.raise(::LogStash::Filters::Grok::TimeoutException.new(elapsed_millis))
51
+ end
52
+ end
53
+ end
54
+
55
+ def start!
56
+ @timer_thread = Thread.new do
57
+ while @running
58
+ begin
59
+ cancel_timed_out!
60
+ rescue Exception => e
61
+ @logger.error("Error while attempting to check/cancel excessively long grok patterns",
62
+ :message => e.message,
63
+ :class => e.class.name,
64
+ :backtrace => e.backtrace
65
+ )
66
+ end
67
+ sleep 0.25
68
+ end
69
+ end
70
+ end
71
+
72
+ def stop!
73
+ @running = false
74
+ # Check for the thread mostly for a fast start/shutdown scenario
75
+ @timer_thread.join if @timer_thread
76
+ end
77
+ end
@@ -0,0 +1,23 @@
1
+ class LogStash::Filters::Grok::TimeoutException < Exception
2
+ attr_accessor :elapsed_millis, :grok, :field, :value
3
+
4
+ def initialize(elapsed_millis, grok=nil, field=nil, value=nil)
5
+ @elapsed_millis = elapsed_millis
6
+ @field = field
7
+ @value = value
8
+ @grok = grok
9
+ end
10
+
11
+ def message
12
+ "Timeout executing grok '#{@grok.pattern}' against field '#{field}' with value '#{trunc_value}'!"
13
+ end
14
+
15
+ def trunc_value
16
+ if value.size <= 255 # If no more than 255 chars
17
+ value
18
+ else
19
+ "Value too large to output (#{value.bytesize} bytes)! First 255 chars are: #{value[0..255]}"
20
+ end
21
+ end
22
+ end
23
+
@@ -138,6 +138,8 @@
138
138
  # `SYSLOGBASE` pattern which itself is defined by other patterns.
139
139
  class LogStash::Filters::Grok < LogStash::Filters::Base
140
140
  config_name "grok"
141
+ require "logstash/filters/grok/timeout_enforcer"
142
+ require "logstash/filters/grok/timeout_exception"
141
143
 
142
144
  # A hash of matches of field => value
143
145
  #
@@ -159,9 +161,9 @@
159
161
  #
160
162
  # Logstash ships by default with a bunch of patterns, so you don't
161
163
  # necessarily need to define this yourself unless you are adding additional
162
- # patterns. You can point to multiple pattern directories using this setting
164
+ # patterns. You can point to multiple pattern directories using this setting.
163
165
  # Note that Grok will read all files in the directory matching the patterns_files_glob
164
- # and assume its a pattern file (including any tilde backup files)
166
+ # and assume it's a pattern file (including any tilde backup files)
165
167
  # [source,ruby]
166
168
  # patterns_dir => ["/opt/logstash/patterns", "/opt/logstash/extra_patterns"]
167
169
  #
@@ -193,6 +195,15 @@
193
195
  # successful match
194
196
  config :tag_on_failure, :validate => :array, :default => ["_grokparsefailure"]
195
197
 
198
+ # Attempt to terminate regexps after this amount of time.
199
+ # This applies per pattern if multiple patterns are applied
200
+ # This will never timeout early, but may take a little longer to timeout.
201
+ # Actual timeout is approximate based on a 250ms quantization.
202
+ config :timeout_millis, :validate => :number, :default => 2000
203
+
204
+ # Tag to apply if a grok regexp times out.
205
+ config :tag_on_timeout, :validate => :string, :default => '_groktimeout'
206
+
196
207
  # The fields to overwrite.
197
208
  #
198
209
  # This allows you to overwrite a value in a field that already exists.
@@ -223,6 +234,9 @@
223
234
  super(params)
224
235
  # a cache of capture name handler methods.
225
236
  @handlers = {}
237
+
238
+ @timeout_enforcer = TimeoutEnforcer.new(@logger, @timeout_millis * 1000000)
239
+ @timeout_enforcer.start!
226
240
  end
227
241
 
228
242
  public
@@ -264,8 +278,11 @@
264
278
  done = false
265
279
 
266
280
  @logger.debug? and @logger.debug("Running grok filter", :event => event);
281
+
267
282
  @patterns.each do |field, groks|
268
- if match(groks, field, event)
283
+ success = match(groks, field, event)
284
+
285
+ if success
269
286
  matched = true
270
287
  break if @break_on_match
271
288
  end
@@ -277,10 +294,14 @@
277
294
  filter_matched(event)
278
295
  else
279
296
  metric.increment(:failures)
280
- @tag_on_failure.each{|tag| event.tag(tag)}
297
+ @tag_on_failure.each {|tag| event.tag(tag)}
281
298
  end
282
299
 
283
300
  @logger.debug? and @logger.debug("Event now: ", :event => event)
301
+ rescue ::LogStash::Filters::Grok::TimeoutException => e
302
+ @logger.warn(e.message)
303
+ metric.increment(:timeouts)
304
+ event.tag(@tag_on_timeout)
284
305
  end # def filter
285
306
 
286
307
  private
@@ -289,28 +310,32 @@
289
310
  if input.is_a?(Array)
290
311
  success = false
291
312
  input.each do |input|
292
- success |= match_against_groks(groks, input, event)
313
+ success |= match_against_groks(groks, field, input, event)
293
314
  end
294
315
  return success
295
316
  else
296
- return match_against_groks(groks, input, event)
317
+ match_against_groks(groks, field, input, event)
297
318
  end
298
319
  rescue StandardError => e
299
- @logger.warn("Grok regexp threw exception", :exception => e.message)
320
+ @logger.warn("Grok regexp threw exception", :exception => e.message, :backtrace => e.backtrace, :class => e.class.name)
321
+ return false
300
322
  end
301
-
323
+
302
324
  private
303
- def match_against_groks(groks, input, event)
325
+ def match_against_groks(groks, field, input, event)
326
+ input = input.to_s
304
327
  matched = false
305
328
  groks.each do |grok|
306
329
  # Convert anything else to string (number, hash, etc)
307
- matched = grok.match_and_capture(input.to_s) do |field, value|
308
- matched = true
309
- handle(field, value, event)
330
+
331
+ matched = @timeout_enforcer.grok_till_timeout(event, grok, field, input) { grok.execute(input) }
332
+ if matched
333
+ grok.capture(matched) {|field, value| handle(field, value, event)}
334
+ break if @break_on_match
310
335
  end
311
- break if matched and @break_on_match
312
336
  end
313
- return matched
337
+
338
+ matched
314
339
  end
315
340
 
316
341
  private
@@ -364,4 +389,8 @@
364
389
  end
365
390
  end # def add_patterns_from_files
366
391
 
392
+ def close
393
+ @timeout_handler.stop!
394
+ end
395
+
367
396
  end # class LogStash::Filters::Grok
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-filter-grok'
4
- s.version = '3.1.2'
4
+ s.version = '3.2.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Parse arbitrary text and structure it."
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/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
  # Gem dependencies
23
23
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
24
 
25
- s.add_runtime_dependency 'jls-grok', '~> 0.11.1'
25
+ s.add_runtime_dependency 'jls-grok', '~> 0.11.3'
26
26
  s.add_runtime_dependency 'logstash-patterns-core'
27
27
 
28
28
  s.add_development_dependency 'logstash-devutils'
@@ -407,12 +407,30 @@ describe LogStash::Filters::Grok do
407
407
  end
408
408
  end
409
409
 
410
+ describe "timeout on failure" do
411
+ config <<-CONFIG
412
+ filter {
413
+ grok {
414
+ match => {
415
+ message => "(.*a){30}"
416
+ }
417
+ timeout_millis => 100
418
+ }
419
+ }
420
+ CONFIG
421
+
422
+ sample "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" do
423
+ expect(subject.get("tags")).to include("_groktimeout")
424
+ expect(subject.get("tags")).not_to include("_grokparsefailure")
425
+ end
426
+ end
427
+
410
428
  describe "tagging on failure" do
411
429
  config <<-CONFIG
412
430
  filter {
413
431
  grok {
414
432
  match => { "message" => "matchme %{NUMBER:fancy}" }
415
- tag_on_failure => false
433
+ tag_on_failure => not_a_match
416
434
  }
417
435
  }
418
436
  CONFIG
@@ -422,7 +440,7 @@ describe LogStash::Filters::Grok do
422
440
  end
423
441
 
424
442
  sample "this will not be matched" do
425
- insist { subject.get("tags") }.include?("false")
443
+ insist { subject.get("tags") }.include?("not_a_match")
426
444
  end
427
445
  end
428
446
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-filter-grok
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.2
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-14 00:00:00.000000000 Z
11
+ date: 2016-08-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -35,7 +35,7 @@ dependencies:
35
35
  requirements:
36
36
  - - "~>"
37
37
  - !ruby/object:Gem::Version
38
- version: 0.11.1
38
+ version: 0.11.3
39
39
  name: jls-grok
40
40
  prerelease: false
41
41
  type: :runtime
@@ -43,7 +43,7 @@ dependencies:
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 0.11.1
46
+ version: 0.11.3
47
47
  - !ruby/object:Gem::Dependency
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  requirements:
@@ -85,6 +85,8 @@ files:
85
85
  - NOTICE.TXT
86
86
  - README.md
87
87
  - lib/logstash/filters/grok.rb
88
+ - lib/logstash/filters/grok/timeout_enforcer.rb
89
+ - lib/logstash/filters/grok/timeout_exception.rb
88
90
  - logstash-filter-grok.gemspec
89
91
  - spec/filters/grok_spec.rb
90
92
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
@@ -109,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
111
  version: '0'
110
112
  requirements: []
111
113
  rubyforge_project:
112
- rubygems_version: 2.6.3
114
+ rubygems_version: 2.4.8
113
115
  signing_key:
114
116
  specification_version: 4
115
117
  summary: Parse arbitrary text and structure it.