logstash-filter-grok 3.1.2 → 3.2.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 +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.
|