logstash-filter-grok 4.0.4 → 4.4.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 +17 -0
- data/Gemfile +6 -0
- data/LICENSE +199 -10
- data/README.md +1 -1
- data/docs/index.asciidoc +61 -0
- data/lib/logstash/filters/grok.rb +186 -55
- data/logstash-filter-grok.gemspec +4 -4
- data/spec/filters/grok_performance_spec.rb +144 -0
- data/spec/filters/grok_spec.rb +607 -630
- data/spec/spec_helper.rb +19 -0
- metadata +32 -10
- data/lib/logstash/filters/grok/timeout_enforcer.rb +0 -72
- data/lib/logstash/filters/grok/timeout_exception.rb +0 -21
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
|
-
|
|
3
2
|
s.name = 'logstash-filter-grok'
|
|
4
|
-
s.version = '4.0
|
|
3
|
+
s.version = '4.4.0'
|
|
5
4
|
s.licenses = ['Apache License (2.0)']
|
|
6
5
|
s.summary = "Parses unstructured event data into fields"
|
|
7
6
|
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,10 +21,11 @@ Gem::Specification.new do |s|
|
|
|
22
21
|
# Gem dependencies
|
|
23
22
|
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
|
24
23
|
s.add_runtime_dependency "logstash-core", ">= 5.6.0"
|
|
24
|
+
s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~> 1.0'
|
|
25
25
|
|
|
26
26
|
s.add_runtime_dependency 'jls-grok', '~> 0.11.3'
|
|
27
27
|
s.add_runtime_dependency 'stud', '~> 0.0.22'
|
|
28
|
-
s.add_runtime_dependency 'logstash-patterns-core'
|
|
28
|
+
s.add_runtime_dependency 'logstash-patterns-core', '>= 4.3.0', '< 5'
|
|
29
29
|
|
|
30
|
-
s.add_development_dependency 'logstash-devutils'
|
|
30
|
+
s.add_development_dependency 'logstash-devutils'
|
|
31
31
|
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require_relative "../spec_helper"
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require "rspec-benchmark"
|
|
6
|
+
rescue LoadError # due testing against LS 5.x
|
|
7
|
+
end
|
|
8
|
+
RSpec.configure do |config|
|
|
9
|
+
config.include RSpec::Benchmark::Matchers if defined? RSpec::Benchmark::Matchers
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
require "logstash/filters/grok"
|
|
13
|
+
|
|
14
|
+
describe LogStash::Filters::Grok do
|
|
15
|
+
|
|
16
|
+
subject do
|
|
17
|
+
described_class.new(config).tap { |filter| filter.register }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
EVENT_COUNT = 300_000
|
|
21
|
+
|
|
22
|
+
describe "base-line performance", :performance => true do
|
|
23
|
+
|
|
24
|
+
EXPECTED_MIN_RATE = 30_000 # per second - based on Travis CI (docker) numbers
|
|
25
|
+
|
|
26
|
+
let(:config) do
|
|
27
|
+
{ 'match' => { "message" => "%{SYSLOGLINE}" }, 'overwrite' => [ "message" ] }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "matches at least #{EXPECTED_MIN_RATE} events/second" do
|
|
31
|
+
max_duration = EVENT_COUNT / EXPECTED_MIN_RATE
|
|
32
|
+
message = "Mar 16 00:01:25 evita postfix/smtpd[1713]: connect from camomile.cloud9.net[168.100.1.3]"
|
|
33
|
+
expect do
|
|
34
|
+
duration = measure do
|
|
35
|
+
EVENT_COUNT.times { subject.filter(LogStash::Event.new("message" => message)) }
|
|
36
|
+
end
|
|
37
|
+
puts "filters/grok parse rate: #{"%02.0f/sec" % (EVENT_COUNT / duration)}, elapsed: #{duration}s"
|
|
38
|
+
end.to perform_under(max_duration).warmup(1).sample(2).times
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
describe "timeout", :performance => true do
|
|
44
|
+
|
|
45
|
+
ACCEPTED_TIMEOUT_DEGRADATION = 100 # in % (compared to timeout-less run)
|
|
46
|
+
# TODO: with more real-world (pipeline) setup this usually gets bellow 10% on average
|
|
47
|
+
|
|
48
|
+
MATCH_PATTERNS = {
|
|
49
|
+
"message" => [
|
|
50
|
+
"foo0: %{NUMBER:bar}", "foo1: %{NUMBER:bar}", "foo2: %{NUMBER:bar}", "foo3: %{NUMBER:bar}", "foo4: %{NUMBER:bar}",
|
|
51
|
+
"foo5: %{NUMBER:bar}", "foo6: %{NUMBER:bar}", "foo7: %{NUMBER:bar}", "foo8: %{NUMBER:bar}", "foo9: %{NUMBER:bar}",
|
|
52
|
+
"%{SYSLOGLINE}"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
SAMPLE_MESSAGE = "Mar 16 00:01:25 evita postfix/smtpd[1713]: connect from aaaaaaaa.aaaaaa.net[111.111.11.1]".freeze
|
|
57
|
+
|
|
58
|
+
TIMEOUT_MILLIS = 5_000
|
|
59
|
+
|
|
60
|
+
let(:config_wout_timeout) do
|
|
61
|
+
{
|
|
62
|
+
'match' => MATCH_PATTERNS,
|
|
63
|
+
'timeout_scope' => "event",
|
|
64
|
+
'timeout_millis' => 0 # 0 - disabled timeout
|
|
65
|
+
}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
let(:config_with_timeout) do
|
|
69
|
+
{
|
|
70
|
+
'match' => MATCH_PATTERNS,
|
|
71
|
+
'timeout_scope' => "event",
|
|
72
|
+
'timeout_millis' => TIMEOUT_MILLIS
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
SAMPLE_COUNT = 2
|
|
77
|
+
|
|
78
|
+
it "has less than #{ACCEPTED_TIMEOUT_DEGRADATION}% overhead" do
|
|
79
|
+
filter_wout_timeout = LogStash::Filters::Grok.new(config_wout_timeout).tap(&:register)
|
|
80
|
+
wout_timeout_duration = do_sample_filter(filter_wout_timeout) # warmup
|
|
81
|
+
puts "filters/grok(timeout => 0) warmed up in #{wout_timeout_duration}"
|
|
82
|
+
before_sample!
|
|
83
|
+
no_timeout_durations = Array.new(SAMPLE_COUNT).map do
|
|
84
|
+
do_sample_filter(filter_wout_timeout)
|
|
85
|
+
end
|
|
86
|
+
puts "filters/grok(timeout => 0) took #{no_timeout_durations}"
|
|
87
|
+
|
|
88
|
+
expected_duration = avg(no_timeout_durations)
|
|
89
|
+
expected_duration += (expected_duration / 100) * ACCEPTED_TIMEOUT_DEGRADATION
|
|
90
|
+
puts "expected_duration #{expected_duration}"
|
|
91
|
+
|
|
92
|
+
filter_with_timeout = LogStash::Filters::Grok.new(config_with_timeout).tap(&:register)
|
|
93
|
+
with_timeout_duration = do_sample_filter(filter_with_timeout) # warmup
|
|
94
|
+
puts "filters/grok(timeout_scope => event) warmed up in #{with_timeout_duration}"
|
|
95
|
+
|
|
96
|
+
try(3) do
|
|
97
|
+
before_sample!
|
|
98
|
+
durations = []
|
|
99
|
+
begin
|
|
100
|
+
expect do
|
|
101
|
+
do_sample_filter(filter_with_timeout).tap { |duration| durations << duration }
|
|
102
|
+
end.to perform_under(expected_duration).sample(SAMPLE_COUNT).times
|
|
103
|
+
ensure
|
|
104
|
+
puts "filters/grok(timeout_scope => event) took #{durations}"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
@private
|
|
110
|
+
|
|
111
|
+
def do_sample_filter(filter)
|
|
112
|
+
sample_event = { "message" => SAMPLE_MESSAGE }
|
|
113
|
+
measure do
|
|
114
|
+
for _ in (1..EVENT_COUNT) do # EVENT_COUNT.times without the block cost
|
|
115
|
+
filter.filter(LogStash::Event.new(sample_event))
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
@private
|
|
123
|
+
|
|
124
|
+
def measure
|
|
125
|
+
start = Time.now
|
|
126
|
+
yield
|
|
127
|
+
Time.now - start
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def avg(ary)
|
|
131
|
+
ary.inject(0) { |m, i| m + i } / ary.size.to_f
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def before_sample!
|
|
135
|
+
2.times { JRuby.gc }
|
|
136
|
+
sleep TIMEOUT_MILLIS / 1000
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def sleep(seconds)
|
|
140
|
+
puts "sleeping for #{seconds} seconds (redundant - potential timeout propagation)"
|
|
141
|
+
Kernel.sleep(seconds)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
data/spec/filters/grok_spec.rb
CHANGED
|
@@ -1,392 +1,367 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
-
|
|
3
|
-
require "stud/temporary"
|
|
2
|
+
require_relative "../spec_helper"
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
require "logstash/filters/grok"
|
|
5
|
+
|
|
6
|
+
describe LogStash::Filters::Grok do
|
|
7
|
+
subject { described_class.new(config) }
|
|
8
|
+
let(:config) { {} }
|
|
9
|
+
let(:event) { LogStash::Event.new(data) }
|
|
10
|
+
let(:data) { { "message" => message } }
|
|
11
|
+
|
|
12
|
+
before(:each) do
|
|
13
|
+
subject.register
|
|
14
|
+
subject.filter(event)
|
|
11
15
|
end
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
def self.sample(message, &block)
|
|
18
|
+
# mod = RSpec::Core::MemoizedHelpers.module_for(self)
|
|
19
|
+
# mod.attr_reader :message
|
|
20
|
+
# # mod.__send__(:define_method, :message) { message }
|
|
21
|
+
# it("matches: #{message}") { @message = message; block.call }
|
|
22
|
+
describe message do
|
|
23
|
+
let(:message) { message }
|
|
24
|
+
it("groks", &block)
|
|
17
25
|
end
|
|
18
26
|
end
|
|
19
|
-
end
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
describe "simple syslog line" do
|
|
29
|
+
let(:config) { { "match" => { "message" => "%{SYSLOGLINE}" }, "overwrite" => [ "message" ] } }
|
|
30
|
+
let(:message) { 'Mar 16 00:01:25 evita postfix/smtpd[1713]: connect from camomile.cloud9.net[168.100.1.3]' }
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
it "matches pattern" do
|
|
33
|
+
expect( event.get("tags") ).to be nil
|
|
34
|
+
expect( event.get("logsource") ).to eql "evita"
|
|
35
|
+
expect( event.get("timestamp") ).to eql "Mar 16 00:01:25"
|
|
36
|
+
expect( event.get("message") ).to eql "connect from camomile.cloud9.net[168.100.1.3]"
|
|
37
|
+
expect( event.get("program") ).to eql "postfix/smtpd"
|
|
38
|
+
expect( event.get("pid") ).to eql "1713"
|
|
39
|
+
end
|
|
24
40
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# At this time, only filters are supported.
|
|
28
|
-
config <<-CONFIG
|
|
29
|
-
filter {
|
|
30
|
-
grok {
|
|
31
|
-
match => { "message" => "%{SYSLOGLINE}" }
|
|
32
|
-
overwrite => [ "message" ]
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
CONFIG
|
|
41
|
+
context 'in ecs mode' do
|
|
42
|
+
let(:config) { super.merge('ecs_compatibility' => 'v1') }
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
it "matches pattern" do
|
|
45
|
+
expect( event.get("host") ).to eql "hostname"=>"evita"
|
|
46
|
+
expect( event.get("process") ).to eql "name"=>"postfix/smtpd", "pid"=>1713
|
|
47
|
+
expect( event.get("message") ).to eql "connect from camomile.cloud9.net[168.100.1.3]"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
context 'with target' do
|
|
52
|
+
let(:config) { { "match" => { "message" => "%{SYSLOGLINE}" }, "target" => "grok" } }
|
|
53
|
+
|
|
54
|
+
it "matches pattern" do
|
|
55
|
+
expect( event.get("message") ).to eql message
|
|
56
|
+
expect( event.get("tags") ).to be nil
|
|
57
|
+
expect( event.get("grok") ).to_not be nil
|
|
58
|
+
expect( event.get("[grok][timestamp]") ).to eql "Mar 16 00:01:25"
|
|
59
|
+
expect( event.get("[grok][message]") ).to eql "connect from camomile.cloud9.net[168.100.1.3]"
|
|
60
|
+
expect( event.get("[grok][pid]") ).to eql "1713"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'with [deep] target' do
|
|
65
|
+
let(:config) { { "match" => { "message" => "%{SYSLOGLINE}" }, "target" => "[@metadata][grok]" } }
|
|
66
|
+
|
|
67
|
+
it "matches pattern" do
|
|
68
|
+
expect( event.get("message") ).to eql message
|
|
69
|
+
expect( event.get("tags") ).to be nil
|
|
70
|
+
expect( event.get("grok") ).to be nil
|
|
71
|
+
expect( event.get("[@metadata][grok][logsource]") ).to eql "evita"
|
|
72
|
+
expect( event.get("[@metadata][grok][message]") ).to eql "connect from camomile.cloud9.net[168.100.1.3]"
|
|
73
|
+
end
|
|
44
74
|
end
|
|
45
75
|
end
|
|
46
76
|
|
|
47
77
|
describe "ietf 5424 syslog line" do
|
|
48
|
-
|
|
49
|
-
# At this time, only filters are supported.
|
|
50
|
-
config <<-CONFIG
|
|
51
|
-
filter {
|
|
52
|
-
grok {
|
|
53
|
-
match => { "message" => "%{SYSLOG5424LINE}" }
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
CONFIG
|
|
78
|
+
let(:config) { { "match" => { "message" => "%{SYSLOG5424LINE}" } } }
|
|
57
79
|
|
|
58
80
|
sample "<191>1 2009-06-30T18:30:00+02:00 paxton.local grokdebug 4123 - [id1 foo=\"bar\"][id2 baz=\"something\"] Hello, syslog." do
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
81
|
+
expect( event.get("tags") ).to be nil
|
|
82
|
+
expect( event.get("syslog5424_pri") ).to eql "191"
|
|
83
|
+
expect( event.get("syslog5424_ver") ).to eql "1"
|
|
84
|
+
expect( event.get("syslog5424_ts") ).to eql "2009-06-30T18:30:00+02:00"
|
|
85
|
+
expect( event.get("syslog5424_host") ).to eql "paxton.local"
|
|
86
|
+
expect( event.get("syslog5424_app") ).to eql "grokdebug"
|
|
87
|
+
expect( event.get("syslog5424_proc") ).to eql "4123"
|
|
88
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
89
|
+
expect( event.get("syslog5424_sd") ).to eql "[id1 foo=\"bar\"][id2 baz=\"something\"]"
|
|
90
|
+
expect( event.get("syslog5424_msg") ).to eql "Hello, syslog."
|
|
69
91
|
end
|
|
70
92
|
|
|
71
93
|
sample "<191>1 2009-06-30T18:30:00+02:00 paxton.local grokdebug - - [id1 foo=\"bar\"] No process ID." do
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
expect( event.get("tags") ).to be nil
|
|
95
|
+
expect( event.get("syslog5424_pri") ).to eql "191"
|
|
96
|
+
expect( event.get("syslog5424_ver") ).to eql "1"
|
|
97
|
+
expect( event.get("syslog5424_ts") ).to eql "2009-06-30T18:30:00+02:00"
|
|
98
|
+
expect( event.get("syslog5424_host") ).to eql "paxton.local"
|
|
99
|
+
expect( event.get("syslog5424_app") ).to eql "grokdebug"
|
|
100
|
+
expect( event.get("syslog5424_proc") ).to be nil
|
|
101
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
102
|
+
expect( event.get("syslog5424_sd") ).to eql "[id1 foo=\"bar\"]"
|
|
103
|
+
expect( event.get("syslog5424_msg") ).to eql "No process ID."
|
|
82
104
|
end
|
|
83
105
|
|
|
84
106
|
sample "<191>1 2009-06-30T18:30:00+02:00 paxton.local grokdebug 4123 - - No structured data." do
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
107
|
+
expect( event.get("tags") ).to be nil
|
|
108
|
+
expect( event.get("syslog5424_pri") ).to eql "191"
|
|
109
|
+
expect( event.get("syslog5424_ver") ).to eql "1"
|
|
110
|
+
expect( event.get("syslog5424_ts") ).to eql "2009-06-30T18:30:00+02:00"
|
|
111
|
+
expect( event.get("syslog5424_host") ).to eql "paxton.local"
|
|
112
|
+
expect( event.get("syslog5424_app") ).to eql "grokdebug"
|
|
113
|
+
expect( event.get("syslog5424_proc") ).to eql '4123'
|
|
114
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
115
|
+
expect( event.get("syslog5424_sd") ).to be nil
|
|
116
|
+
expect( event.get("syslog5424_msg") ).to eql "No structured data."
|
|
95
117
|
end
|
|
96
118
|
|
|
97
119
|
sample "<191>1 2009-06-30T18:30:00+02:00 paxton.local grokdebug - - - No PID or SD." do
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
expect( event.get("tags") ).to be nil
|
|
121
|
+
expect( event.get("syslog5424_pri") ).to eql "191"
|
|
122
|
+
expect( event.get("syslog5424_ver") ).to eql "1"
|
|
123
|
+
expect( event.get("syslog5424_ts") ).to eql "2009-06-30T18:30:00+02:00"
|
|
124
|
+
expect( event.get("syslog5424_host") ).to eql "paxton.local"
|
|
125
|
+
expect( event.get("syslog5424_app") ).to eql "grokdebug"
|
|
126
|
+
expect( event.get("syslog5424_proc") ).to be nil
|
|
127
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
128
|
+
expect( event.get("syslog5424_sd") ).to be nil
|
|
129
|
+
expect( event.get("syslog5424_msg") ).to eql "No PID or SD."
|
|
108
130
|
end
|
|
109
131
|
|
|
110
132
|
sample "<191>1 2009-06-30T18:30:00+02:00 paxton.local grokdebug 4123 - Missing structured data." do
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
insist { subject.get("syslog5424_proc") } == "4123"
|
|
118
|
-
insist { subject.get("syslog5424_msgid") } == nil
|
|
119
|
-
insist { subject.get("syslog5424_sd") } == nil
|
|
120
|
-
insist { subject.get("syslog5424_msg") } == "Missing structured data."
|
|
133
|
+
expect( event.get("tags") ).to be nil
|
|
134
|
+
|
|
135
|
+
expect( event.get("syslog5424_proc") ).to eql '4123'
|
|
136
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
137
|
+
expect( event.get("syslog5424_sd") ).to be nil
|
|
138
|
+
expect( event.get("syslog5424_msg") ).to eql "Missing structured data."
|
|
121
139
|
end
|
|
122
140
|
|
|
123
141
|
sample "<191>1 2009-06-30T18:30:00+02:00 paxton.local grokdebug 4123 - - Additional spaces." do
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
insist { subject.get("syslog5424_msgid") } == nil
|
|
132
|
-
insist { subject.get("syslog5424_sd") } == nil
|
|
133
|
-
insist { subject.get("syslog5424_msg") } == "Additional spaces."
|
|
142
|
+
expect( event.get("tags") ).to be nil
|
|
143
|
+
|
|
144
|
+
expect( event.get("syslog5424_app") ).to eql "grokdebug"
|
|
145
|
+
expect( event.get("syslog5424_proc") ).to eql '4123'
|
|
146
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
147
|
+
expect( event.get("syslog5424_sd") ).to be nil
|
|
148
|
+
expect( event.get("syslog5424_msg") ).to eql "Additional spaces."
|
|
134
149
|
end
|
|
135
150
|
|
|
136
151
|
sample "<191>1 2009-06-30T18:30:00+02:00 paxton.local grokdebug 4123 - Additional spaces and missing SD." do
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
insist { subject.get("syslog5424_msgid") } == nil
|
|
145
|
-
insist { subject.get("syslog5424_sd") } == nil
|
|
146
|
-
insist { subject.get("syslog5424_msg") } == "Additional spaces and missing SD."
|
|
152
|
+
expect( event.get("tags") ).to be nil
|
|
153
|
+
|
|
154
|
+
expect( event.get("syslog5424_app") ).to eql "grokdebug"
|
|
155
|
+
expect( event.get("syslog5424_proc") ).to eql '4123'
|
|
156
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
157
|
+
expect( event.get("syslog5424_sd") ).to be nil
|
|
158
|
+
expect( event.get("syslog5424_msg") ).to eql "Additional spaces and missing SD."
|
|
147
159
|
end
|
|
148
160
|
|
|
149
161
|
sample "<30>1 2014-04-04T16:44:07+02:00 osctrl01 dnsmasq-dhcp 8048 - - Appname contains a dash" do
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
162
|
+
expect( event.get("tags") ).to be nil
|
|
163
|
+
expect( event.get("syslog5424_pri") ).to eql "30"
|
|
164
|
+
expect( event.get("syslog5424_ver") ).to eql "1"
|
|
165
|
+
expect( event.get("syslog5424_ts") ).to eql "2014-04-04T16:44:07+02:00"
|
|
166
|
+
expect( event.get("syslog5424_host") ).to eql "osctrl01"
|
|
167
|
+
expect( event.get("syslog5424_app") ).to eql "dnsmasq-dhcp"
|
|
168
|
+
expect( event.get("syslog5424_proc") ).to eql "8048"
|
|
169
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
170
|
+
expect( event.get("syslog5424_sd") ).to be nil
|
|
171
|
+
expect( event.get("syslog5424_msg") ).to eql "Appname contains a dash"
|
|
160
172
|
end
|
|
161
173
|
|
|
162
174
|
sample "<30>1 2014-04-04T16:44:07+02:00 osctrl01 - 8048 - - Appname is nil" do
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
expect( event.get("tags") ).to be nil
|
|
176
|
+
expect( event.get("syslog5424_pri") ).to eql "30"
|
|
177
|
+
expect( event.get("syslog5424_ver") ).to eql "1"
|
|
178
|
+
expect( event.get("syslog5424_ts") ).to eql "2014-04-04T16:44:07+02:00"
|
|
179
|
+
expect( event.get("syslog5424_host") ).to eql "osctrl01"
|
|
180
|
+
expect( event.get("syslog5424_app") ).to be nil
|
|
181
|
+
expect( event.get("syslog5424_proc") ).to eql "8048"
|
|
182
|
+
expect( event.get("syslog5424_msgid") ).to be nil
|
|
183
|
+
expect( event.get("syslog5424_sd") ).to be nil
|
|
184
|
+
expect( event.get("syslog5424_msg") ).to eql "Appname is nil"
|
|
173
185
|
end
|
|
174
186
|
end
|
|
175
187
|
|
|
176
|
-
describe "parsing an event with multiple messages (array of strings)", :
|
|
177
|
-
config
|
|
178
|
-
|
|
179
|
-
grok {
|
|
180
|
-
match => { "message" => "(?:hello|world) %{NUMBER}" }
|
|
181
|
-
named_captures_only => false
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
CONFIG
|
|
188
|
+
describe "parsing an event with multiple messages (array of strings)", if: false do
|
|
189
|
+
let(:config) { { "message" => "(?:hello|world) %{NUMBER}" } }
|
|
190
|
+
let(:message) { [ "hello 12345", "world 23456" ] }
|
|
185
191
|
|
|
186
|
-
|
|
187
|
-
|
|
192
|
+
it "matches them all" do
|
|
193
|
+
expect( event.get("NUMBER") ).to eql [ "12345", "23456" ]
|
|
188
194
|
end
|
|
189
195
|
end
|
|
190
196
|
|
|
191
197
|
describe "coercing matched values" do
|
|
192
|
-
config
|
|
193
|
-
|
|
194
|
-
grok {
|
|
195
|
-
match => { "message" => "%{NUMBER:foo:int} %{NUMBER:bar:float}" }
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
CONFIG
|
|
198
|
+
let(:config) { { "match" => { "message" => "%{NUMBER:foo:int} %{NUMBER:bar:float}" } } }
|
|
199
|
+
let(:message) { '400 454.33' }
|
|
199
200
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
it "coerces matched values" do
|
|
202
|
+
expect( event.get("foo") ).to be_a Integer
|
|
203
|
+
expect( event.get("foo") ).to eql 400
|
|
204
|
+
expect( event.get("bar") ).to be_a Float
|
|
205
|
+
expect( event.get("bar") ).to eql 454.33
|
|
205
206
|
end
|
|
206
207
|
end
|
|
207
208
|
|
|
208
209
|
describe "in-line pattern definitions" do
|
|
209
|
-
config
|
|
210
|
-
filter {
|
|
211
|
-
grok {
|
|
212
|
-
match => { "message" => "%{FIZZLE=\\d+}" }
|
|
213
|
-
named_captures_only => false
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
CONFIG
|
|
210
|
+
let(:config) { { "match" => { "message" => "%{FIZZLE=\\d+}" }, "named_captures_only" => false } }
|
|
217
211
|
|
|
218
212
|
sample "hello 1234" do
|
|
219
|
-
|
|
213
|
+
expect( event.get("FIZZLE") ).to eql '1234'
|
|
220
214
|
end
|
|
221
215
|
end
|
|
222
216
|
|
|
223
217
|
describe "processing selected fields" do
|
|
224
|
-
config
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
match => { "examplefield" => "%{NUMBER:num}" }
|
|
229
|
-
break_on_match => false
|
|
230
|
-
}
|
|
218
|
+
let(:config) {
|
|
219
|
+
{
|
|
220
|
+
'match' => { "message" => "%{WORD:word}", "examplefield" => "%{NUMBER:num}" },
|
|
221
|
+
'break_on_match' => false
|
|
231
222
|
}
|
|
232
|
-
|
|
223
|
+
}
|
|
224
|
+
let(:data) { { "message" => "hello world", "examplefield" => "12345" } }
|
|
233
225
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
226
|
+
it "processes declared matches" do
|
|
227
|
+
expect( event.get("word") ).to eql 'hello'
|
|
228
|
+
expect( event.get("examplefield") ).to eql '12345'
|
|
237
229
|
end
|
|
238
230
|
end
|
|
239
231
|
|
|
240
232
|
describe "adding fields on match" do
|
|
241
|
-
config
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
add_field => [ "new_field", "%{fancy}" ]
|
|
246
|
-
}
|
|
233
|
+
let(:config) {
|
|
234
|
+
{
|
|
235
|
+
'match' => { "message" => "matchme %{NUMBER:fancy}" },
|
|
236
|
+
'add_field' => [ "new_field", "%{fancy}" ]
|
|
247
237
|
}
|
|
248
|
-
|
|
238
|
+
}
|
|
249
239
|
|
|
250
240
|
sample "matchme 1234" do
|
|
251
|
-
|
|
252
|
-
|
|
241
|
+
expect( event.get("tags") ).to be nil
|
|
242
|
+
expect( event.get("new_field") ).to eql "1234"
|
|
253
243
|
end
|
|
254
244
|
|
|
255
245
|
sample "this will not be matched" do
|
|
256
|
-
|
|
257
|
-
|
|
246
|
+
expect( event.get("tags") ).to include("_grokparsefailure")
|
|
247
|
+
expect( event ).not_to include 'new_field'
|
|
258
248
|
end
|
|
259
249
|
end
|
|
260
250
|
|
|
261
251
|
context "empty fields" do
|
|
262
252
|
describe "drop by default" do
|
|
263
|
-
config
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
match => { "message" => "1=%{WORD:foo1} *(2=%{WORD:foo2})?" }
|
|
267
|
-
}
|
|
253
|
+
let(:config) {
|
|
254
|
+
{
|
|
255
|
+
'match' => { "message" => "1=%{WORD:foo1} *(2=%{WORD:foo2})?" }
|
|
268
256
|
}
|
|
269
|
-
|
|
257
|
+
}
|
|
270
258
|
|
|
271
259
|
sample "1=test" do
|
|
272
|
-
|
|
273
|
-
|
|
260
|
+
expect( event.get("tags") ).to be nil
|
|
261
|
+
expect( event ).to include 'foo1'
|
|
274
262
|
|
|
275
263
|
# Since 'foo2' was not captured, it must not be present in the event.
|
|
276
|
-
|
|
264
|
+
expect( event ).not_to include 'foo2'
|
|
277
265
|
end
|
|
278
266
|
end
|
|
279
267
|
|
|
280
268
|
describe "keep if keep_empty_captures is true" do
|
|
281
|
-
config
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
keep_empty_captures => true
|
|
286
|
-
}
|
|
269
|
+
let(:config) {
|
|
270
|
+
{
|
|
271
|
+
'match' => { "message" => "1=%{WORD:foo1} *(2=%{WORD:foo2})?" },
|
|
272
|
+
'keep_empty_captures' => true
|
|
287
273
|
}
|
|
288
|
-
|
|
274
|
+
}
|
|
289
275
|
|
|
290
276
|
sample "1=test" do
|
|
291
|
-
|
|
277
|
+
expect( event.get("tags") ).to be nil
|
|
292
278
|
# use .to_hash for this test, for now, because right now
|
|
293
279
|
# the Event.include? returns false for missing fields as well
|
|
294
280
|
# as for fields with nil values.
|
|
295
|
-
|
|
296
|
-
|
|
281
|
+
expect( event.to_hash ).to include 'foo1'
|
|
282
|
+
expect( event.to_hash ).to include 'foo2'
|
|
297
283
|
end
|
|
298
284
|
end
|
|
299
285
|
end
|
|
300
286
|
|
|
301
287
|
describe "when named_captures_only == false" do
|
|
302
|
-
config
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
named_captures_only => false
|
|
307
|
-
}
|
|
288
|
+
let(:config) {
|
|
289
|
+
{
|
|
290
|
+
'match' => { "message" => "Hello %{WORD}. %{WORD:foo}" },
|
|
291
|
+
'named_captures_only' => false
|
|
308
292
|
}
|
|
309
|
-
|
|
293
|
+
}
|
|
310
294
|
|
|
311
295
|
sample "Hello World, yo!" do
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
296
|
+
expect( event ).to include 'WORD'
|
|
297
|
+
expect( event.get("WORD") ).to eql "World"
|
|
298
|
+
expect( event ).to include 'foo'
|
|
299
|
+
expect( event.get("foo") ).to eql "yo"
|
|
316
300
|
end
|
|
317
301
|
end
|
|
318
302
|
|
|
319
303
|
describe "using oniguruma named captures (?<name>regex)" do
|
|
320
304
|
context "plain regexp" do
|
|
321
|
-
config
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
match => { "message" => "(?<foo>\w+)" }
|
|
325
|
-
}
|
|
305
|
+
let(:config) {
|
|
306
|
+
{
|
|
307
|
+
'match' => { "message" => "(?<foo>\\w+)" }
|
|
326
308
|
}
|
|
327
|
-
|
|
309
|
+
}
|
|
310
|
+
|
|
328
311
|
sample "hello world" do
|
|
329
|
-
|
|
330
|
-
|
|
312
|
+
expect( event.get("tags") ).to be nil
|
|
313
|
+
expect( event.get("foo") ).to eql "hello"
|
|
331
314
|
end
|
|
332
315
|
end
|
|
333
316
|
|
|
334
317
|
context "grok patterns" do
|
|
335
|
-
config
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
match => { "message" => "(?<timestamp>%{DATE_EU} %{TIME})" }
|
|
339
|
-
}
|
|
318
|
+
let(:config) {
|
|
319
|
+
{
|
|
320
|
+
'match' => { "message" => "(?<timestamp>%{DATE_EU} %{TIME})" }
|
|
340
321
|
}
|
|
341
|
-
|
|
322
|
+
}
|
|
342
323
|
|
|
343
324
|
sample "fancy 12-12-12 12:12:12" do
|
|
344
|
-
|
|
345
|
-
|
|
325
|
+
expect( event.get("tags") ).to be nil
|
|
326
|
+
expect( event.get("timestamp") ).to eql "12-12-12 12:12:12"
|
|
346
327
|
end
|
|
347
328
|
end
|
|
348
329
|
end
|
|
349
330
|
|
|
350
331
|
describe "grok on integer types" do
|
|
351
|
-
config
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
match => { "status" => "^403$" }
|
|
355
|
-
add_tag => "four_oh_three"
|
|
356
|
-
}
|
|
332
|
+
let(:config) {
|
|
333
|
+
{
|
|
334
|
+
'match' => { "status" => "^403$" }, 'add_tag' => "four_oh_three"
|
|
357
335
|
}
|
|
358
|
-
|
|
336
|
+
}
|
|
337
|
+
let(:data) { Hash({ "status" => 403 }) }
|
|
359
338
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
339
|
+
it "parses" do
|
|
340
|
+
expect( event.get("tags") ).not_to include "_grokparsefailure"
|
|
341
|
+
expect( event.get("tags") ).to include "four_oh_three"
|
|
363
342
|
end
|
|
364
343
|
end
|
|
365
344
|
|
|
366
345
|
describe "grok on float types" do
|
|
367
|
-
config
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
match => { "version" => "^1.0$" }
|
|
371
|
-
add_tag => "one_point_oh"
|
|
372
|
-
}
|
|
346
|
+
let(:config) {
|
|
347
|
+
{
|
|
348
|
+
'match' => { "version" => "^1.0$" }, 'add_tag' => "one_point_oh"
|
|
373
349
|
}
|
|
374
|
-
|
|
350
|
+
}
|
|
351
|
+
let(:data) { Hash({ "version" => 1.0 }) }
|
|
375
352
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
353
|
+
it "parses" do
|
|
354
|
+
expect( event.get("tags") ).not_to include "_grokparsefailure"
|
|
355
|
+
expect( event.get("tags") ).to include "one_point_oh"
|
|
379
356
|
end
|
|
380
357
|
end
|
|
381
358
|
|
|
382
359
|
describe "grok on %{LOGLEVEL}" do
|
|
383
|
-
config
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
match => { "message" => "%{LOGLEVEL:level}: error!" }
|
|
387
|
-
}
|
|
360
|
+
let(:config) {
|
|
361
|
+
{
|
|
362
|
+
'match' => { "message" => "%{LOGLEVEL:level}: error!" }
|
|
388
363
|
}
|
|
389
|
-
|
|
364
|
+
}
|
|
390
365
|
|
|
391
366
|
log_level_names = %w(
|
|
392
367
|
trace Trace TRACE
|
|
@@ -402,531 +377,533 @@ describe LogStash::Filters::Grok do
|
|
|
402
377
|
)
|
|
403
378
|
log_level_names.each do |level_name|
|
|
404
379
|
sample "#{level_name}: error!" do
|
|
405
|
-
|
|
380
|
+
expect( event.get("level") ).to eql level_name
|
|
406
381
|
end
|
|
407
382
|
end
|
|
408
383
|
end
|
|
409
384
|
|
|
410
385
|
describe "timeout on failure" do
|
|
411
|
-
config
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
message => "(.*a){30}"
|
|
416
|
-
}
|
|
417
|
-
timeout_millis => 100
|
|
418
|
-
}
|
|
386
|
+
let(:config) {
|
|
387
|
+
{
|
|
388
|
+
'match' => { "message" => "(.*a){30}" },
|
|
389
|
+
'timeout_millis' => 100
|
|
419
390
|
}
|
|
420
|
-
|
|
391
|
+
}
|
|
421
392
|
|
|
422
393
|
sample "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" do
|
|
423
|
-
expect(
|
|
424
|
-
expect(
|
|
394
|
+
expect( event.get("tags") ).to include("_groktimeout")
|
|
395
|
+
expect( event.get("tags") ).not_to include("_grokparsefailure")
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
describe "no timeout on failure with multiple patterns (when timeout not grouped)" do
|
|
400
|
+
let(:config) {
|
|
401
|
+
{
|
|
402
|
+
'match' => {
|
|
403
|
+
"message" => [
|
|
404
|
+
"(.*f){20}", "(.*e){20}", "(.*d){20}", "(.*c){20}", "(.*b){20}",
|
|
405
|
+
"(.*a){25}", "(.*a){24}", "(.*a){23}", "(.*a){22}", "(.*a){21}",
|
|
406
|
+
"(.*a){25}", "(.*a){24}", "(.*a){23}", "(.*a){22}", "(.*a){21}",
|
|
407
|
+
"(.*a){25}", "(.*a){24}", "(.*a){23}", "(.*a){22}", "(.*a){21}",
|
|
408
|
+
"(.*a){20}"
|
|
409
|
+
]
|
|
410
|
+
},
|
|
411
|
+
'timeout_millis' => 750,
|
|
412
|
+
'timeout_scope' => 'pattern'
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
sample( 'b' * 15 + 'c' * 15 + 'd' * 15 + 'e' * 15 + ' ' + 'a' * 20 ) do
|
|
417
|
+
expect( event.get("tags") ).to be nil
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
describe "timeout on grouped (multi-pattern) failure" do
|
|
422
|
+
let(:config) {
|
|
423
|
+
{
|
|
424
|
+
'match' => {
|
|
425
|
+
"message" => [
|
|
426
|
+
"(.*f){20}", "(.*e){20}", "(.*d){20}", "(.*c){20}", "(.*b){20}",
|
|
427
|
+
"(.*a){25}", "(.*a){24}", "(.*a){23}", "(.*a){22}", "(.*a){21}",
|
|
428
|
+
"(.*a){25}", "(.*a){24}", "(.*a){23}", "(.*a){22}", "(.*a){21}",
|
|
429
|
+
"(.*a){25}", "(.*a){24}", "(.*a){23}", "(.*a){22}", "(.*a){21}",
|
|
430
|
+
"(.*a){20}"
|
|
431
|
+
]
|
|
432
|
+
},
|
|
433
|
+
'timeout_millis' => 750,
|
|
434
|
+
'timeout_scope' => 'event'
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
sample( 'b' * 15 + 'c' * 15 + 'd' * 15 + 'e' * 15 + ' ' + 'a' * 20 ) do
|
|
439
|
+
expect( event.get("tags") ).to include("_groktimeout")
|
|
440
|
+
expect( event.get("tags") ).not_to include("_grokparsefailure")
|
|
425
441
|
end
|
|
426
442
|
end
|
|
427
443
|
|
|
428
444
|
describe "tagging on failure" do
|
|
429
|
-
config
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
tag_on_failure => not_a_match
|
|
434
|
-
}
|
|
445
|
+
let(:config) {
|
|
446
|
+
{
|
|
447
|
+
'match' => { "message" => "matchme %{NUMBER:fancy}" },
|
|
448
|
+
'tag_on_failure' => 'not_a_match'
|
|
435
449
|
}
|
|
436
|
-
|
|
450
|
+
}
|
|
437
451
|
|
|
438
452
|
sample "matchme 1234" do
|
|
439
|
-
|
|
453
|
+
expect( event.get("tags") ).to be nil
|
|
440
454
|
end
|
|
441
455
|
|
|
442
456
|
sample "this will not be matched" do
|
|
443
|
-
|
|
457
|
+
expect( event.get("tags") ).to include("not_a_match")
|
|
444
458
|
end
|
|
445
459
|
end
|
|
446
460
|
|
|
447
461
|
describe "captures named fields even if the whole text matches" do
|
|
448
|
-
config
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
match => { "message" => "%{DATE_EU:stimestamp}" }
|
|
452
|
-
}
|
|
462
|
+
let(:config) {
|
|
463
|
+
{
|
|
464
|
+
'match' => { "message" => "%{DATE_EU:stimestamp}" }
|
|
453
465
|
}
|
|
454
|
-
|
|
466
|
+
}
|
|
455
467
|
|
|
456
468
|
sample "11/01/01" do
|
|
457
|
-
|
|
469
|
+
expect( event.get("stimestamp") ).to eql "11/01/01"
|
|
458
470
|
end
|
|
459
471
|
end
|
|
460
472
|
|
|
461
473
|
describe "allow dashes in capture names" do
|
|
462
|
-
config
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
match => { "message" => "%{WORD:foo-bar}" }
|
|
466
|
-
}
|
|
474
|
+
let(:config) {
|
|
475
|
+
{
|
|
476
|
+
'match' => { "message" => "%{WORD:foo-bar}" }
|
|
467
477
|
}
|
|
468
|
-
|
|
478
|
+
}
|
|
469
479
|
|
|
470
480
|
sample "hello world" do
|
|
471
|
-
|
|
472
|
-
end
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
describe "performance test", :performance => true do
|
|
476
|
-
event_count = 100000
|
|
477
|
-
min_rate = 2000
|
|
478
|
-
|
|
479
|
-
max_duration = event_count / min_rate
|
|
480
|
-
input = "Nov 24 01:29:01 -0800"
|
|
481
|
-
config <<-CONFIG
|
|
482
|
-
input {
|
|
483
|
-
generator {
|
|
484
|
-
count => #{event_count}
|
|
485
|
-
message => "Mar 16 00:01:25 evita postfix/smtpd[1713]: connect from camomile.cloud9.net[168.100.1.3]"
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
filter {
|
|
489
|
-
grok {
|
|
490
|
-
match => { "message" => "%{SYSLOGLINE}" }
|
|
491
|
-
overwrite => [ "message" ]
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
output { null { } }
|
|
495
|
-
CONFIG
|
|
496
|
-
|
|
497
|
-
2.times do
|
|
498
|
-
start = Time.now
|
|
499
|
-
agent do
|
|
500
|
-
duration = (Time.now - start)
|
|
501
|
-
puts "filters/grok parse rate: #{"%02.0f/sec" % (event_count / duration)}, elapsed: #{duration}s"
|
|
502
|
-
insist { duration } < max_duration
|
|
503
|
-
end
|
|
481
|
+
expect( event.get("foo-bar") ).to eql "hello"
|
|
504
482
|
end
|
|
505
483
|
end
|
|
506
484
|
|
|
507
485
|
describe "single value match with duplicate-named fields in pattern" do
|
|
508
|
-
config
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
match => { "message" => "%{INT:foo}|%{WORD:foo}" }
|
|
512
|
-
}
|
|
486
|
+
let(:config) {
|
|
487
|
+
{
|
|
488
|
+
'match' => { "message" => "%{INT:foo}|%{WORD:foo}" }
|
|
513
489
|
}
|
|
514
|
-
|
|
490
|
+
}
|
|
515
491
|
|
|
516
492
|
sample "hello world" do
|
|
517
|
-
|
|
493
|
+
expect( event.get("foo") ).to be_a(String)
|
|
518
494
|
end
|
|
519
495
|
|
|
520
496
|
sample "123 world" do
|
|
521
|
-
|
|
497
|
+
expect( event.get("foo") ).to be_a(String)
|
|
522
498
|
end
|
|
523
499
|
end
|
|
524
500
|
|
|
525
|
-
describe "break_on_match default should be true and first match should exit filter" do
|
|
526
|
-
config <<-CONFIG
|
|
527
|
-
filter {
|
|
528
|
-
grok {
|
|
529
|
-
match => { "message" => "%{INT:foo}"
|
|
530
|
-
"somefield" => "%{INT:bar}"}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
CONFIG
|
|
534
501
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
end
|
|
540
|
-
|
|
541
|
-
describe "break_on_match when set to false should try all patterns" do
|
|
542
|
-
config <<-CONFIG
|
|
543
|
-
filter {
|
|
544
|
-
grok {
|
|
545
|
-
match => { "message" => "%{INT:foo}"
|
|
546
|
-
"somefield" => "%{INT:bar}"}
|
|
547
|
-
break_on_match => false
|
|
548
|
-
}
|
|
502
|
+
describe "break_on_match default should be true" do
|
|
503
|
+
let(:config) {
|
|
504
|
+
{
|
|
505
|
+
'match' => { "message" => "%{INT:foo}", "somefield" => "%{INT:bar}" }
|
|
549
506
|
}
|
|
550
|
-
|
|
507
|
+
}
|
|
508
|
+
let(:data) { Hash("message" => "hello world 123", "somefield" => "testme abc 999") }
|
|
551
509
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
510
|
+
it 'exits filter after first match' do
|
|
511
|
+
expect( event.get("foo") ).to eql '123'
|
|
512
|
+
expect( event.get("bar") ).to be nil
|
|
555
513
|
end
|
|
556
514
|
end
|
|
557
515
|
|
|
558
|
-
describe "
|
|
559
|
-
config
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
break_on_match => false
|
|
564
|
-
}
|
|
516
|
+
describe "break_on_match when set to false" do
|
|
517
|
+
let(:config) {
|
|
518
|
+
{
|
|
519
|
+
'match' => { "message" => "%{INT:foo}", "somefield" => "%{INT:bar}" },
|
|
520
|
+
'break_on_match' => false
|
|
565
521
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
sample "treebranch" do
|
|
569
|
-
insist { subject.get("name2") } == "branch"
|
|
570
|
-
end
|
|
522
|
+
}
|
|
523
|
+
let(:data) { Hash("message" => "hello world 123", "somefield" => "testme abc 999") }
|
|
571
524
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
sample "treebeard" do
|
|
577
|
-
insist { subject.get("name1") } == "tree"
|
|
578
|
-
insist { subject.get("name2") } == "beard"
|
|
525
|
+
it 'should try all patterns' do
|
|
526
|
+
expect( event.get("foo") ).to eql '123'
|
|
527
|
+
expect( event.get("bar") ).to eql '999'
|
|
579
528
|
end
|
|
580
529
|
end
|
|
581
530
|
|
|
582
|
-
|
|
583
|
-
config
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
531
|
+
context "break_on_match default for array input with single grok pattern" do
|
|
532
|
+
let(:config) {
|
|
533
|
+
{
|
|
534
|
+
'match' => { "message" => "%{INT:foo}" },
|
|
535
|
+
'break_on_match' => false
|
|
588
536
|
}
|
|
589
|
-
|
|
537
|
+
}
|
|
590
538
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
539
|
+
describe 'fully matching input' do
|
|
540
|
+
let(:data) { Hash("message" => ["hello world 123", "line 23"]) } # array input --
|
|
541
|
+
it 'matches' do
|
|
542
|
+
expect( event.get("foo") ).to eql ["123", "23"]
|
|
543
|
+
expect( event.get("tags") ).to be nil
|
|
544
|
+
end
|
|
595
545
|
end
|
|
596
546
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
547
|
+
describe 'partially matching input' do
|
|
548
|
+
let(:data) { Hash("message" => ["hello world 123", "abc"]) } # array input, one of them matches
|
|
549
|
+
it 'matches' do
|
|
550
|
+
expect( event.get("foo") ).to eql "123"
|
|
551
|
+
expect( event.get("tags") ).to be nil
|
|
552
|
+
end
|
|
601
553
|
end
|
|
602
554
|
end
|
|
603
555
|
|
|
604
556
|
describe "break_on_match = true (default) for array input with multiple grok pattern" do
|
|
605
|
-
config
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
match => { "message" => ["%{INT:foo}", "%{WORD:bar}"] }
|
|
609
|
-
}
|
|
557
|
+
let(:config) {
|
|
558
|
+
{
|
|
559
|
+
'match' => { "message" => ["%{INT:foo}", "%{WORD:bar}"] }
|
|
610
560
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
describe 'matching input' do
|
|
564
|
+
let(:data) { Hash("message" => ["hello world 123", "line 23"]) } # array input --
|
|
565
|
+
it 'matches' do
|
|
566
|
+
expect( event.get("foo") ).to eql ["123", "23"]
|
|
567
|
+
expect( event.get("bar") ).to be nil
|
|
568
|
+
expect( event.get("tags") ).to be nil
|
|
569
|
+
end
|
|
618
570
|
end
|
|
619
571
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
572
|
+
describe 'partially matching input' do
|
|
573
|
+
let(:data) { Hash("message" => ["hello world", "line 23"]) } # array input, one of them matches
|
|
574
|
+
it 'matches' do
|
|
575
|
+
expect( event.get("bar") ).to eql 'hello'
|
|
576
|
+
expect( event.get("foo") ).to eql "23"
|
|
577
|
+
expect( event.get("tags") ).to be nil
|
|
578
|
+
end
|
|
625
579
|
end
|
|
626
580
|
end
|
|
627
581
|
|
|
628
582
|
describe "break_on_match = false for array input with multiple grok pattern" do
|
|
629
|
-
config
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
break_on_match => false
|
|
634
|
-
}
|
|
583
|
+
let(:config) {
|
|
584
|
+
{
|
|
585
|
+
'match' => { "message" => ["%{INT:foo}", "%{WORD:bar}"] },
|
|
586
|
+
'break_on_match' => false
|
|
635
587
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
describe 'fully matching input' do
|
|
591
|
+
let(:data) { Hash("message" => ["hello world 123", "line 23"]) } # array input --
|
|
592
|
+
it 'matches' do
|
|
593
|
+
expect( event.get("foo") ).to eql ["123", "23"]
|
|
594
|
+
expect( event.get("bar") ).to eql ["hello", "line"]
|
|
595
|
+
expect( event.get("tags") ).to be nil
|
|
596
|
+
end
|
|
643
597
|
end
|
|
644
598
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
599
|
+
describe 'partially matching input' do
|
|
600
|
+
let(:data) { Hash("message" => ["hello world", "line 23"]) } # array input, one of them matches
|
|
601
|
+
it 'matches' do
|
|
602
|
+
expect( event.get("bar") ).to eql ["hello", "line"]
|
|
603
|
+
expect( event.get("foo") ).to eql "23"
|
|
604
|
+
expect( event.get("tags") ).to be nil
|
|
605
|
+
end
|
|
650
606
|
end
|
|
651
607
|
end
|
|
652
608
|
|
|
653
609
|
describe "grok with unicode" do
|
|
654
|
-
config
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
match => { "message" => "<%{POSINT:syslog_pri}>%{SPACE}%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{PROG:syslog_program}(:?)(?:\\[%{GREEDYDATA:syslog_pid}\\])?(:?) %{GREEDYDATA:syslog_message}" }
|
|
659
|
-
}
|
|
610
|
+
let(:config) {
|
|
611
|
+
{
|
|
612
|
+
#'match' => { "message" => "<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{PROG:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" }
|
|
613
|
+
'match' => { "message" => "<%{POSINT:syslog_pri}>%{SPACE}%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{PROG:syslog_program}(:?)(?:\\[%{GREEDYDATA:syslog_pid}\\])?(:?) %{GREEDYDATA:syslog_message}" }
|
|
660
614
|
}
|
|
661
|
-
|
|
615
|
+
}
|
|
662
616
|
|
|
663
617
|
sample "<22>Jan 4 07:50:46 mailmaster postfix/policy-spf[9454]: : SPF permerror (Junk encountered in record 'v=spf1 mx a:mail.domain.no ip4:192.168.0.4 �all'): Envelope-from: email@domain.no" do
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
618
|
+
expect( event.get("tags") ).to be nil
|
|
619
|
+
expect( event.get("syslog_pri") ).to eql "22"
|
|
620
|
+
expect( event.get("syslog_program") ).to eql "postfix/policy-spf"
|
|
667
621
|
end
|
|
668
622
|
end
|
|
669
623
|
|
|
670
|
-
describe
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
624
|
+
describe "grok with nil coerced value" do
|
|
625
|
+
let(:config) {
|
|
626
|
+
{
|
|
627
|
+
'match' => { "message" => "test (N/A|%{BASE10NUM:duration:float}ms)" }
|
|
628
|
+
}
|
|
629
|
+
}
|
|
674
630
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
@file.write('WORD \b[2-5]\b')
|
|
679
|
-
@file.close
|
|
631
|
+
sample "test 28.4ms" do
|
|
632
|
+
expect( event.get("duration") ).to eql 28.4
|
|
633
|
+
expect( event.get("tags") ).to be nil
|
|
680
634
|
end
|
|
681
635
|
|
|
682
|
-
|
|
683
|
-
|
|
636
|
+
sample "test N/A" do
|
|
637
|
+
expect( event.to_hash ).not_to include("duration")
|
|
638
|
+
expect( event.get("tags") ).to be nil
|
|
684
639
|
end
|
|
685
640
|
|
|
686
|
-
sample
|
|
687
|
-
|
|
641
|
+
sample "test abc" do
|
|
642
|
+
expect( event.get("duration") ).to be nil
|
|
643
|
+
expect( event.get("tags") ).to eql ["_grokparsefailure"]
|
|
688
644
|
end
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
describe "grok with nil coerced value and keep_empty_captures" do
|
|
648
|
+
let(:config) {
|
|
649
|
+
{
|
|
650
|
+
'match' => { "message" => "test (N/A|%{BASE10NUM:duration:float}ms)" },
|
|
651
|
+
'keep_empty_captures' => true
|
|
652
|
+
}
|
|
653
|
+
}
|
|
689
654
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
655
|
+
sample "test N/A" do
|
|
656
|
+
expect( event.to_hash ).to include("duration")
|
|
657
|
+
expect( event.get("tags") ).to be nil
|
|
693
658
|
end
|
|
694
659
|
end
|
|
695
660
|
|
|
696
|
-
describe
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
661
|
+
describe "grok with no coercion" do
|
|
662
|
+
let(:config) {
|
|
663
|
+
{
|
|
664
|
+
'match' => { "message" => "test (N/A|%{BASE10NUM:duration}ms)" },
|
|
665
|
+
}
|
|
666
|
+
}
|
|
701
667
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
@file1.write('WORD \b[2-5]\b')
|
|
706
|
-
@file1.close
|
|
707
|
-
@file2 = File.new(File.join(tmpdir, 'grok.pattern'), 'w+')
|
|
708
|
-
@file2.write('WORD \b[0-1]\b')
|
|
709
|
-
@file2.close
|
|
668
|
+
sample "test 28.4ms" do
|
|
669
|
+
expect( event.get("duration") ).to eql '28.4'
|
|
670
|
+
expect( event.get("tags") ).to be nil
|
|
710
671
|
end
|
|
711
672
|
|
|
712
|
-
|
|
713
|
-
|
|
673
|
+
sample "test N/A" do
|
|
674
|
+
expect( event.get("duration") ).to be nil
|
|
675
|
+
expect( event.get("tags") ).to be nil
|
|
714
676
|
end
|
|
677
|
+
end
|
|
715
678
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
679
|
+
describe "opening/closing" do
|
|
680
|
+
let(:config) { { "match" => {"message" => "A"} } }
|
|
681
|
+
let(:message) { 'AAA' }
|
|
719
682
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
File.unlink @file2
|
|
723
|
-
FileUtils.remove_entry tmpdir
|
|
724
|
-
FileUtils.rm_rf(pattern_dir) unless has_pattern_dir?
|
|
683
|
+
it "should close cleanly" do
|
|
684
|
+
expect { subject.do_close }.not_to raise_error
|
|
725
685
|
end
|
|
726
686
|
end
|
|
727
687
|
|
|
728
|
-
describe "
|
|
688
|
+
describe "after grok when the event is JSON serialised the field values are unchanged" do
|
|
689
|
+
let(:config) {
|
|
690
|
+
{
|
|
691
|
+
'match' => ["message", "Failed password for (invalid user |)%{USERNAME:username} from %{IP:src_ip} port %{BASE10NUM:port}"],
|
|
692
|
+
'remove_field' => ["message","severity"],
|
|
693
|
+
'add_tag' => ["ssh_failure"]
|
|
694
|
+
}
|
|
695
|
+
}
|
|
729
696
|
|
|
730
|
-
|
|
697
|
+
sample('{"facility":"auth","message":"Failed password for testuser from 1.1.1.1 port 22"}') do
|
|
698
|
+
expect( event.get("username") ).to eql "testuser"
|
|
699
|
+
expect( event.get("port") ).to eql "22"
|
|
700
|
+
expect( event.get("src_ip") ).to eql "1.1.1.1"
|
|
701
|
+
expect( LogStash::Json.dump(event.get('username')) ).to eql "\"testuser\""
|
|
731
702
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
@
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
end
|
|
740
|
-
|
|
741
|
-
let(:config) do
|
|
742
|
-
"filter { grok { patterns_dir => \"#{tmpdir}\" patterns_files_glob => \"*.pattern\" match => { \"message\" => \"%{WORD:word}\" } } }"
|
|
703
|
+
expect( event.to_json ).to match %r|"src_ip":"1.1.1.1"|
|
|
704
|
+
expect( event.to_json ).to match %r|"@timestamp":"20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ"|
|
|
705
|
+
expect( event.to_json ).to match %r|"port":"22"|
|
|
706
|
+
expect( event.to_json ).to match %r|"@version":"1"|
|
|
707
|
+
expect( event.to_json ).to match %r|"username"|i
|
|
708
|
+
expect( event.to_json ).to match %r|"testuser"|
|
|
709
|
+
expect( event.to_json ).to match %r|"tags":\["ssh_failure"\]|
|
|
743
710
|
end
|
|
711
|
+
end
|
|
744
712
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
713
|
+
describe "grok with inline pattern definition successfully extracts fields" do
|
|
714
|
+
let(:config) {
|
|
715
|
+
{
|
|
716
|
+
'match' => { "message" => "%{APACHE_TIME:timestamp} %{LOGLEVEL:level} %{MY_PATTERN:hindsight}" },
|
|
717
|
+
'pattern_definitions' => {
|
|
718
|
+
"APACHE_TIME" => "%{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{YEAR}",
|
|
719
|
+
"MY_PATTERN" => "%{YEAR}"
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
748
723
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
724
|
+
sample "Mon Dec 26 16:22:08 2016 error 2020" do
|
|
725
|
+
expect( event.get("timestamp") ).to eql "Mon Dec 26 16:22:08 2016"
|
|
726
|
+
expect( event.get("level") ).to eql "error"
|
|
727
|
+
expect( event.get("hindsight") ).to eql "2020"
|
|
753
728
|
end
|
|
754
729
|
end
|
|
755
730
|
|
|
756
|
-
describe
|
|
757
|
-
|
|
758
|
-
|
|
731
|
+
describe "grok with inline pattern definition overwrites existing pattern definition" do
|
|
732
|
+
let(:config) {
|
|
733
|
+
{
|
|
734
|
+
'match' => { "message" => "%{APACHE_TIME:timestamp} %{LOGLEVEL:level}" },
|
|
735
|
+
# loglevel was previously ([Aa]lert|ALERT|[Tt]...
|
|
736
|
+
'pattern_definitions' => {
|
|
737
|
+
"APACHE_TIME" => "%{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{YEAR}",
|
|
738
|
+
"LOGLEVEL" => "%{NUMBER}"
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
759
742
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
@file3.close
|
|
764
|
-
Dir.mkdir(File.join(tmpdir, "subdir"))
|
|
743
|
+
sample "Mon Dec 26 16:22:08 2016 9999" do
|
|
744
|
+
expect( event.get("timestamp") ).to eql "Mon Dec 26 16:22:08 2016"
|
|
745
|
+
expect( event.get("level") ).to eql "9999"
|
|
765
746
|
end
|
|
747
|
+
end
|
|
766
748
|
|
|
749
|
+
context 'when timeouts are explicitly disabled' do
|
|
767
750
|
let(:config) do
|
|
768
|
-
|
|
751
|
+
{
|
|
752
|
+
"timeout_millis" => 0
|
|
753
|
+
}
|
|
769
754
|
end
|
|
770
755
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
756
|
+
context 'when given a pathological input', slow: true do
|
|
757
|
+
let(:message) { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
|
|
758
|
+
let(:config) { super().merge("match" => { "message" => "(.*a){30}" }) }
|
|
774
759
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
760
|
+
it 'blocks for at least 3 seconds' do
|
|
761
|
+
blocking_exception_class = Class.new(::Exception) # avoid RuntimeError
|
|
762
|
+
expect do
|
|
763
|
+
Timeout.timeout(3, blocking_exception_class) do
|
|
764
|
+
subject.filter(event)
|
|
765
|
+
end
|
|
766
|
+
end.to raise_exception(blocking_exception_class)
|
|
767
|
+
end
|
|
778
768
|
end
|
|
779
769
|
end
|
|
770
|
+
end
|
|
780
771
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
grok {
|
|
785
|
-
match => { "message" => "test (N/A|%{BASE10NUM:duration:float}ms)" }
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
CONFIG
|
|
772
|
+
describe LogStash::Filters::Grok do
|
|
773
|
+
describe "(LEGACY)" do
|
|
774
|
+
describe "patterns in the 'patterns/' dir override core patterns" do
|
|
789
775
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
insist { subject.get("tags") }.nil?
|
|
793
|
-
end
|
|
776
|
+
let(:pattern_dir) { File.join(LogStash::Environment::LOGSTASH_HOME, "patterns") }
|
|
777
|
+
let(:has_pattern_dir?) { Dir.exist?(pattern_dir) }
|
|
794
778
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
779
|
+
before do
|
|
780
|
+
FileUtils.mkdir(pattern_dir) unless has_pattern_dir?
|
|
781
|
+
@file = File.new(File.join(pattern_dir, 'grok.pattern'), 'w+')
|
|
782
|
+
@file.write('WORD \b[2-5]\b')
|
|
783
|
+
@file.close
|
|
784
|
+
end
|
|
799
785
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
end
|
|
804
|
-
end
|
|
786
|
+
let(:config) do
|
|
787
|
+
'filter { grok { match => { "message" => "%{WORD:word}" } } }'
|
|
788
|
+
end
|
|
805
789
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
grok {
|
|
810
|
-
match => { "message" => "test (N/A|%{BASE10NUM:duration:float}ms)" }
|
|
811
|
-
keep_empty_captures => true
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
CONFIG
|
|
790
|
+
sample("message" => 'hello') do
|
|
791
|
+
expect(subject.get("tags")).to eql ["_grokparsefailure"]
|
|
792
|
+
end
|
|
815
793
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
794
|
+
after do
|
|
795
|
+
File.unlink @file
|
|
796
|
+
FileUtils.rm_rf(pattern_dir) if has_pattern_dir?
|
|
797
|
+
end
|
|
819
798
|
end
|
|
820
799
|
|
|
821
|
-
|
|
800
|
+
describe "patterns in custom dir override those in 'patterns/' dir" do
|
|
822
801
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
grok {
|
|
827
|
-
match => { "message" => "test (N/A|%{BASE10NUM:duration}ms)" }
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
CONFIG
|
|
802
|
+
let(:tmpdir) { Stud::Temporary.directory }
|
|
803
|
+
let(:pattern_dir) { File.join(LogStash::Environment::LOGSTASH_HOME, "patterns") }
|
|
804
|
+
let(:has_pattern_dir?) { Dir.exist?(pattern_dir) }
|
|
831
805
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
806
|
+
before do
|
|
807
|
+
FileUtils.mkdir(pattern_dir) unless has_pattern_dir?
|
|
808
|
+
@file1 = File.new(File.join(pattern_dir, 'grok.pattern'), 'w+')
|
|
809
|
+
@file1.write('WORD \b[2-5]\b')
|
|
810
|
+
@file1.close
|
|
811
|
+
@file2 = File.new(File.join(tmpdir, 'grok.pattern'), 'w+')
|
|
812
|
+
@file2.write('WORD \b[0-1]\b')
|
|
813
|
+
@file2.close
|
|
814
|
+
end
|
|
836
815
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
end
|
|
841
|
-
end
|
|
816
|
+
let(:config) do
|
|
817
|
+
"filter { grok { patterns_dir => \"#{tmpdir}\" match => { \"message\" => \"%{WORD:word}\" } } }"
|
|
818
|
+
end
|
|
842
819
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
::LogStash::Filters::Grok.new(config)
|
|
847
|
-
end
|
|
820
|
+
sample("message" => '0') do
|
|
821
|
+
expect(subject.get("tags")).to be nil
|
|
822
|
+
end
|
|
848
823
|
|
|
849
|
-
|
|
850
|
-
|
|
824
|
+
after do
|
|
825
|
+
File.unlink @file1
|
|
826
|
+
File.unlink @file2
|
|
827
|
+
FileUtils.remove_entry tmpdir
|
|
828
|
+
FileUtils.rm_rf(pattern_dir) unless has_pattern_dir?
|
|
829
|
+
end
|
|
851
830
|
end
|
|
852
831
|
|
|
853
|
-
|
|
854
|
-
expect(plugin.timeout_enforcer.running).to be true
|
|
855
|
-
end
|
|
832
|
+
describe "patterns with file glob" do
|
|
856
833
|
|
|
857
|
-
|
|
858
|
-
let(:config) { super.merge("timeout_millis" => 0) }
|
|
834
|
+
let(:tmpdir) { Stud::Temporary.directory }
|
|
859
835
|
|
|
860
|
-
|
|
861
|
-
|
|
836
|
+
before do
|
|
837
|
+
@file3 = File.new(File.join(tmpdir, 'grok.pattern'), 'w+')
|
|
838
|
+
@file3.write('WORD \b[0-1]\b')
|
|
839
|
+
@file3.close
|
|
840
|
+
@file4 = File.new(File.join(tmpdir, 'grok.pattern.old'), 'w+')
|
|
841
|
+
@file4.write('WORD \b[2-5]\b')
|
|
842
|
+
@file4.close
|
|
862
843
|
end
|
|
863
|
-
end
|
|
864
844
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
845
|
+
let(:config) do
|
|
846
|
+
"filter { grok { patterns_dir => \"#{tmpdir}\" patterns_files_glob => \"*.pattern\" match => { \"message\" => \"%{WORD:word}\" } } }"
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
sample("message" => '0') do
|
|
850
|
+
expect(subject.get("tags")).to be nil
|
|
851
|
+
end
|
|
868
852
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
853
|
+
after do
|
|
854
|
+
File.unlink @file3
|
|
855
|
+
File.unlink @file4
|
|
856
|
+
FileUtils.remove_entry tmpdir
|
|
857
|
+
end
|
|
872
858
|
end
|
|
873
|
-
end
|
|
874
859
|
|
|
875
|
-
|
|
876
|
-
config <<-CONFIG
|
|
877
|
-
filter {grok {match => ["message", "Failed password for (invalid user |)%{USERNAME:username} from %{IP:src_ip} port %{BASE10NUM:port}"] remove_field => ["message","severity"] add_tag => ["ssh_failure"]}}
|
|
878
|
-
CONFIG
|
|
860
|
+
describe "patterns with file glob on directory that contains subdirectories" do
|
|
879
861
|
|
|
880
|
-
|
|
881
|
-
insist { subject.get("username") } == "testuser"
|
|
882
|
-
insist { subject.get("port") } == "22"
|
|
883
|
-
insist { subject.get("src_ip") } == "1.1.1.1"
|
|
884
|
-
insist { LogStash::Json.dump(subject.get('username')) } == "\"testuser\""
|
|
862
|
+
let(:tmpdir) { Stud::Temporary.directory }
|
|
885
863
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
insist { subject.to_json } =~ %r|"tags":\["ssh_failure"\]|
|
|
893
|
-
end
|
|
894
|
-
end
|
|
864
|
+
before do
|
|
865
|
+
@file3 = File.new(File.join(tmpdir, 'grok.pattern'), 'w+')
|
|
866
|
+
@file3.write('WORD \b[0-1]\b')
|
|
867
|
+
@file3.close
|
|
868
|
+
Dir.mkdir(File.join(tmpdir, "subdir"))
|
|
869
|
+
end
|
|
895
870
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
grok {
|
|
900
|
-
match => { "message" => "%{APACHE_TIME:timestamp} %{LOGLEVEL:level} %{MY_PATTERN:hindsight}" }
|
|
901
|
-
pattern_definitions => { "APACHE_TIME" => "%{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{YEAR}"
|
|
902
|
-
"MY_PATTERN" => "%{YEAR}"}
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
CONFIG
|
|
871
|
+
let(:config) do
|
|
872
|
+
"filter { grok { patterns_dir => \"#{tmpdir}\" patterns_files_glob => \"*\" match => { \"message\" => \"%{WORD:word}\" } } }"
|
|
873
|
+
end
|
|
906
874
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
875
|
+
sample("message" => '0') do
|
|
876
|
+
expect(subject.get("tags")).to be nil
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
after do
|
|
880
|
+
File.unlink @file3
|
|
881
|
+
FileUtils.remove_entry tmpdir
|
|
882
|
+
end
|
|
911
883
|
end
|
|
912
|
-
end
|
|
913
884
|
|
|
914
|
-
|
|
915
|
-
|
|
885
|
+
describe "LOGSTASH-1547 - break_on_match should work on fields with multiple patterns" do
|
|
886
|
+
config <<-CONFIG
|
|
916
887
|
filter {
|
|
917
888
|
grok {
|
|
918
|
-
match => { "message" => "%{
|
|
919
|
-
|
|
920
|
-
pattern_definitions => { "APACHE_TIME" => "%{DAY} %{MONTH} %{MONTHDAY} %{TIME} %{YEAR}"
|
|
921
|
-
"LOGLEVEL" => "%{NUMBER}"}
|
|
889
|
+
match => { "message" => ["%{GREEDYDATA:name1}beard", "tree%{GREEDYDATA:name2}"] }
|
|
890
|
+
break_on_match => false
|
|
922
891
|
}
|
|
923
892
|
}
|
|
924
|
-
|
|
893
|
+
CONFIG
|
|
925
894
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
895
|
+
sample "treebranch" do
|
|
896
|
+
expect(subject.get("name2")).to eql "branch"
|
|
897
|
+
end
|
|
898
|
+
|
|
899
|
+
sample "bushbeard" do
|
|
900
|
+
expect(subject.get("name1")).to eql "bush"
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
sample "treebeard" do
|
|
904
|
+
expect(subject.get("name1")).to eql "tree"
|
|
905
|
+
expect(subject.get("name2")).to eql "beard"
|
|
906
|
+
end
|
|
929
907
|
end
|
|
930
908
|
end
|
|
931
|
-
|
|
932
909
|
end
|