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 +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/logstash/filters/grok/timeout_enforcer.rb +77 -0
- data/lib/logstash/filters/grok/timeout_exception.rb +23 -0
- data/lib/logstash/filters/grok.rb +43 -14
- data/logstash-filter-grok.gemspec +2 -2
- data/spec/filters/grok_spec.rb +20 -2
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a34c685833867a04238f8544429c5938cd58a696
|
4
|
+
data.tar.gz: 096eb04af33607c064d210c9e007ffbfccb3a9fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7aacf53f2a102c96c65597710b529ef0acef10730358ae4c5d4fd32c9dd053e5f226a2696535921fccc75504f205c8580762287ead404d413359bac8e9758f1
|
7
|
+
data.tar.gz: 61bfb20dbb5452b957e08e82b5ad54d8b1f89d50dcdd2d0ba6bd37f7e06b2f630e2d92247ddd9e81de7fcefba720f12a3a32b5a21edb99eafb9d6a57331b7b54
|
data/CHANGELOG.md
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
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
|
-
|
308
|
-
|
309
|
-
|
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
|
-
|
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.
|
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.
|
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'
|
data/spec/filters/grok_spec.rb
CHANGED
@@ -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 =>
|
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?("
|
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.
|
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-
|
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.
|
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.
|
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.
|
114
|
+
rubygems_version: 2.4.8
|
113
115
|
signing_key:
|
114
116
|
specification_version: 4
|
115
117
|
summary: Parse arbitrary text and structure it.
|