logstash-filter-grok 4.0.4 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
-
3
2
  s.name = 'logstash-filter-grok'
4
- s.version = '4.0.4'
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', '= 1.3.6'
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
@@ -1,392 +1,367 @@
1
1
  # encoding: utf-8
2
- require "logstash/devutils/rspec/spec_helper"
3
- require "stud/temporary"
2
+ require_relative "../spec_helper"
4
3
 
5
- module LogStash::Environment
6
- # running the grok code outside a logstash package means
7
- # LOGSTASH_HOME will not be defined, so let's set it here
8
- # before requiring the grok filter
9
- unless self.const_defined?(:LOGSTASH_HOME)
10
- LOGSTASH_HOME = File.expand_path("../../../", __FILE__)
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
- # also :pattern_path method must exist so we define it too
14
- unless self.method_defined?(:pattern_path)
15
- def pattern_path(path)
16
- ::File.join(LOGSTASH_HOME, "patterns", path)
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
- require "logstash/filters/grok"
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
- describe LogStash::Filters::Grok do
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
- describe "simple syslog line" do
26
- # The logstash config goes here.
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
- sample "Mar 16 00:01:25 evita postfix/smtpd[1713]: connect from camomile.cloud9.net[168.100.1.3]" do
38
- insist { subject.get("tags") }.nil?
39
- insist { subject.get("logsource") } == "evita"
40
- insist { subject.get("timestamp") } == "Mar 16 00:01:25"
41
- insist { subject.get("message") } == "connect from camomile.cloud9.net[168.100.1.3]"
42
- insist { subject.get("program") } == "postfix/smtpd"
43
- insist { subject.get("pid") } == "1713"
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
- # The logstash config goes here.
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
- insist { subject.get("tags") }.nil?
60
- insist { subject.get("syslog5424_pri") } == "191"
61
- insist { subject.get("syslog5424_ver") } == "1"
62
- insist { subject.get("syslog5424_ts") } == "2009-06-30T18:30:00+02:00"
63
- insist { subject.get("syslog5424_host") } == "paxton.local"
64
- insist { subject.get("syslog5424_app") } == "grokdebug"
65
- insist { subject.get("syslog5424_proc") } == "4123"
66
- insist { subject.get("syslog5424_msgid") } == nil
67
- insist { subject.get("syslog5424_sd") } == "[id1 foo=\"bar\"][id2 baz=\"something\"]"
68
- insist { subject.get("syslog5424_msg") } == "Hello, syslog."
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
- insist { subject.get("tags") }.nil?
73
- insist { subject.get("syslog5424_pri") } == "191"
74
- insist { subject.get("syslog5424_ver") } == "1"
75
- insist { subject.get("syslog5424_ts") } == "2009-06-30T18:30:00+02:00"
76
- insist { subject.get("syslog5424_host") } == "paxton.local"
77
- insist { subject.get("syslog5424_app") } == "grokdebug"
78
- insist { subject.get("syslog5424_proc") } == nil
79
- insist { subject.get("syslog5424_msgid") } == nil
80
- insist { subject.get("syslog5424_sd") } == "[id1 foo=\"bar\"]"
81
- insist { subject.get("syslog5424_msg") } == "No process ID."
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
- insist { subject.get("tags") }.nil?
86
- insist { subject.get("syslog5424_pri") } == "191"
87
- insist { subject.get("syslog5424_ver") } == "1"
88
- insist { subject.get("syslog5424_ts") } == "2009-06-30T18:30:00+02:00"
89
- insist { subject.get("syslog5424_host") } == "paxton.local"
90
- insist { subject.get("syslog5424_app") } == "grokdebug"
91
- insist { subject.get("syslog5424_proc") } == "4123"
92
- insist { subject.get("syslog5424_msgid") } == nil
93
- insist { subject.get("syslog5424_sd") } == nil
94
- insist { subject.get("syslog5424_msg") } == "No structured data."
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
- insist { subject.get("tags") }.nil?
99
- insist { subject.get("syslog5424_pri") } == "191"
100
- insist { subject.get("syslog5424_ver") } == "1"
101
- insist { subject.get("syslog5424_ts") } == "2009-06-30T18:30:00+02:00"
102
- insist { subject.get("syslog5424_host") } == "paxton.local"
103
- insist { subject.get("syslog5424_app") } == "grokdebug"
104
- insist { subject.get("syslog5424_proc") } == nil
105
- insist { subject.get("syslog5424_msgid") } == nil
106
- insist { subject.get("syslog5424_sd") } == nil
107
- insist { subject.get("syslog5424_msg") } == "No PID or SD."
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
- insist { subject.get("tags") }.nil?
112
- insist { subject.get("syslog5424_pri") } == "191"
113
- insist { subject.get("syslog5424_ver") } == "1"
114
- insist { subject.get("syslog5424_ts") } == "2009-06-30T18:30:00+02:00"
115
- insist { subject.get("syslog5424_host") } == "paxton.local"
116
- insist { subject.get("syslog5424_app") } == "grokdebug"
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
- insist { subject.get("tags") }.nil?
125
- insist { subject.get("syslog5424_pri") } == "191"
126
- insist { subject.get("syslog5424_ver") } == "1"
127
- insist { subject.get("syslog5424_ts") } == "2009-06-30T18:30:00+02:00"
128
- insist { subject.get("syslog5424_host") } == "paxton.local"
129
- insist { subject.get("syslog5424_app") } == "grokdebug"
130
- insist { subject.get("syslog5424_proc") } == "4123"
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
- insist { subject.get("tags") }.nil?
138
- insist { subject.get("syslog5424_pri") } == "191"
139
- insist { subject.get("syslog5424_ver") } == "1"
140
- insist { subject.get("syslog5424_ts") } == "2009-06-30T18:30:00+02:00"
141
- insist { subject.get("syslog5424_host") } == "paxton.local"
142
- insist { subject.get("syslog5424_app") } == "grokdebug"
143
- insist { subject.get("syslog5424_proc") } == "4123"
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
- insist { subject.get("tags") }.nil?
151
- insist { subject.get("syslog5424_pri") } == "30"
152
- insist { subject.get("syslog5424_ver") } == "1"
153
- insist { subject.get("syslog5424_ts") } == "2014-04-04T16:44:07+02:00"
154
- insist { subject.get("syslog5424_host") } == "osctrl01"
155
- insist { subject.get("syslog5424_app") } == "dnsmasq-dhcp"
156
- insist { subject.get("syslog5424_proc") } == "8048"
157
- insist { subject.get("syslog5424_msgid") } == nil
158
- insist { subject.get("syslog5424_sd") } == nil
159
- insist { subject.get("syslog5424_msg") } == "Appname contains a dash"
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
- insist { subject.get("tags") }.nil?
164
- insist { subject.get("syslog5424_pri") } == "30"
165
- insist { subject.get("syslog5424_ver") } == "1"
166
- insist { subject.get("syslog5424_ts") } == "2014-04-04T16:44:07+02:00"
167
- insist { subject.get("syslog5424_host") } == "osctrl01"
168
- insist { subject.get("syslog5424_app") } == nil
169
- insist { subject.get("syslog5424_proc") } == "8048"
170
- insist { subject.get("syslog5424_msgid") } == nil
171
- insist { subject.get("syslog5424_sd") } == nil
172
- insist { subject.get("syslog5424_msg") } == "Appname is nil"
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)", :if => false do
177
- config <<-CONFIG
178
- filter {
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
- sample("message" => [ "hello 12345", "world 23456" ]) do
187
- insist { subject.get("NUMBER") } == [ "12345", "23456" ]
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 <<-CONFIG
193
- filter {
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
- sample "400 454.33" do
201
- insist { subject.get("foo") } == 400
202
- insist { subject.get("foo") }.is_a?(Fixnum)
203
- insist { subject.get("bar") } == 454.33
204
- insist { subject.get("bar") }.is_a?(Float)
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 <<-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
- insist { subject.get("FIZZLE") } == "1234"
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 <<-CONFIG
225
- filter {
226
- grok {
227
- match => { "message" => "%{WORD:word}" }
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
- CONFIG
223
+ }
224
+ let(:data) { { "message" => "hello world", "examplefield" => "12345" } }
233
225
 
234
- sample("message" => "hello world", "examplefield" => "12345") do
235
- insist { subject.get("examplefield") } == "12345"
236
- insist { subject.get("word") } == "hello"
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 <<-CONFIG
242
- filter {
243
- grok {
244
- match => { "message" => "matchme %{NUMBER:fancy}" }
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
- CONFIG
238
+ }
249
239
 
250
240
  sample "matchme 1234" do
251
- insist { subject.get("tags") }.nil?
252
- insist { subject.get("new_field") } == "1234"
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
- insist { subject.get("tags") }.include?("_grokparsefailure")
257
- reject { subject }.include?("new_field")
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 <<-CONFIG
264
- filter {
265
- grok {
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
- CONFIG
257
+ }
270
258
 
271
259
  sample "1=test" do
272
- insist { subject.get("tags") }.nil?
273
- insist { subject }.include?("foo1")
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
- reject { subject }.include?("foo2")
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 <<-CONFIG
282
- filter {
283
- grok {
284
- match => { "message" => "1=%{WORD:foo1} *(2=%{WORD:foo2})?" }
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
- CONFIG
274
+ }
289
275
 
290
276
  sample "1=test" do
291
- insist { subject.get("tags") }.nil?
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
- insist { subject.to_hash }.include?("foo2")
296
- insist { subject.to_hash }.include?("foo2")
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 <<-CONFIG
303
- filter {
304
- grok {
305
- match => { "message" => "Hello %{WORD}. %{WORD:foo}" }
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
- CONFIG
293
+ }
310
294
 
311
295
  sample "Hello World, yo!" do
312
- insist { subject }.include?("WORD")
313
- insist { subject.get("WORD") } == "World"
314
- insist { subject }.include?("foo")
315
- insist { subject.get("foo") } == "yo"
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 <<-'CONFIG'
322
- filter {
323
- grok {
324
- match => { "message" => "(?<foo>\w+)" }
325
- }
305
+ let(:config) {
306
+ {
307
+ 'match' => { "message" => "(?<foo>\\w+)" }
326
308
  }
327
- CONFIG
309
+ }
310
+
328
311
  sample "hello world" do
329
- insist { subject.get("tags") }.nil?
330
- insist { subject.get("foo") } == "hello"
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 <<-'CONFIG'
336
- filter {
337
- grok {
338
- match => { "message" => "(?<timestamp>%{DATE_EU} %{TIME})" }
339
- }
318
+ let(:config) {
319
+ {
320
+ 'match' => { "message" => "(?<timestamp>%{DATE_EU} %{TIME})" }
340
321
  }
341
- CONFIG
322
+ }
342
323
 
343
324
  sample "fancy 12-12-12 12:12:12" do
344
- insist { subject.get("tags") }.nil?
345
- insist { subject.get("timestamp") } == "12-12-12 12:12:12"
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 <<-'CONFIG'
352
- filter {
353
- grok {
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
- CONFIG
336
+ }
337
+ let(:data) { Hash({ "status" => 403 }) }
359
338
 
360
- sample("status" => 403) do
361
- reject { subject.get("tags") }.include?("_grokparsefailure")
362
- insist { subject.get("tags") }.include?("four_oh_three")
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 <<-'CONFIG'
368
- filter {
369
- grok {
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
- CONFIG
350
+ }
351
+ let(:data) { Hash({ "version" => 1.0 }) }
375
352
 
376
- sample("version" => 1.0) do
377
- insist { subject.get("tags") }.include?("one_point_oh")
378
- insist { subject.get("tags") }.include?("one_point_oh")
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 <<-'CONFIG'
384
- filter {
385
- grok {
386
- match => { "message" => "%{LOGLEVEL:level}: error!" }
387
- }
360
+ let(:config) {
361
+ {
362
+ 'match' => { "message" => "%{LOGLEVEL:level}: error!" }
388
363
  }
389
- CONFIG
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
- insist { subject.get("level") } == level_name
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 <<-CONFIG
412
- filter {
413
- grok {
414
- match => {
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
- CONFIG
391
+ }
421
392
 
422
393
  sample "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" do
423
- expect(subject.get("tags")).to include("_groktimeout")
424
- expect(subject.get("tags")).not_to include("_grokparsefailure")
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 <<-CONFIG
430
- filter {
431
- grok {
432
- match => { "message" => "matchme %{NUMBER:fancy}" }
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
- CONFIG
450
+ }
437
451
 
438
452
  sample "matchme 1234" do
439
- insist { subject.get("tags") }.nil?
453
+ expect( event.get("tags") ).to be nil
440
454
  end
441
455
 
442
456
  sample "this will not be matched" do
443
- insist { subject.get("tags") }.include?("not_a_match")
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 <<-CONFIG
449
- filter {
450
- grok {
451
- match => { "message" => "%{DATE_EU:stimestamp}" }
452
- }
462
+ let(:config) {
463
+ {
464
+ 'match' => { "message" => "%{DATE_EU:stimestamp}" }
453
465
  }
454
- CONFIG
466
+ }
455
467
 
456
468
  sample "11/01/01" do
457
- insist { subject.get("stimestamp") } == "11/01/01"
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 <<-CONFIG
463
- filter {
464
- grok {
465
- match => { "message" => "%{WORD:foo-bar}" }
466
- }
474
+ let(:config) {
475
+ {
476
+ 'match' => { "message" => "%{WORD:foo-bar}" }
467
477
  }
468
- CONFIG
478
+ }
469
479
 
470
480
  sample "hello world" do
471
- insist { subject.get("foo-bar") } == "hello"
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 <<-CONFIG
509
- filter {
510
- grok {
511
- match => { "message" => "%{INT:foo}|%{WORD:foo}" }
512
- }
486
+ let(:config) {
487
+ {
488
+ 'match' => { "message" => "%{INT:foo}|%{WORD:foo}" }
513
489
  }
514
- CONFIG
490
+ }
515
491
 
516
492
  sample "hello world" do
517
- insist { subject.get("foo") }.is_a?(String)
493
+ expect( event.get("foo") ).to be_a(String)
518
494
  end
519
495
 
520
496
  sample "123 world" do
521
- insist { subject.get("foo") }.is_a?(String)
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
- sample("message" => "hello world 123", "somefield" => "testme abc 999") do
536
- insist { subject.get("foo") } == "123"
537
- insist { subject.get("bar") }.nil?
538
- end
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
- CONFIG
507
+ }
508
+ let(:data) { Hash("message" => "hello world 123", "somefield" => "testme abc 999") }
551
509
 
552
- sample("message" => "hello world 123", "somefield" => "testme abc 999") do
553
- insist { subject.get("foo") } == "123"
554
- insist { subject.get("bar") } == "999"
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 "LOGSTASH-1547 - break_on_match should work on fields with multiple patterns" do
559
- config <<-CONFIG
560
- filter {
561
- grok {
562
- match => { "message" => ["%{GREEDYDATA:name1}beard", "tree%{GREEDYDATA:name2}"] }
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
- CONFIG
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
- sample "bushbeard" do
573
- insist { subject.get("name1") } == "bush"
574
- end
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
- describe "break_on_match default for array input with single grok pattern" do
583
- config <<-CONFIG
584
- filter {
585
- grok {
586
- match => { "message" => "%{INT:foo}"}
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
- CONFIG
537
+ }
590
538
 
591
- # array input --
592
- sample("message" => ["hello world 123", "line 23"]) do
593
- insist { subject.get("foo") } == ["123", "23"]
594
- insist { subject.get("tags") }.nil?
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
- # array input, one of them matches
598
- sample("message" => ["hello world 123", "abc"]) do
599
- insist { subject.get("foo") } == "123"
600
- insist { subject.get("tags") }.nil?
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 <<-CONFIG
606
- filter {
607
- grok {
608
- match => { "message" => ["%{INT:foo}", "%{WORD:bar}"] }
609
- }
557
+ let(:config) {
558
+ {
559
+ 'match' => { "message" => ["%{INT:foo}", "%{WORD:bar}"] }
610
560
  }
611
- CONFIG
612
-
613
- # array input --
614
- sample("message" => ["hello world 123", "line 23"]) do
615
- insist { subject.get("foo") } == ["123", "23"]
616
- insist { subject.get("bar") }.nil?
617
- insist { subject.get("tags") }.nil?
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
- # array input, one of them matches
621
- sample("message" => ["hello world", "line 23"]) do
622
- insist { subject.get("bar") } == "hello"
623
- insist { subject.get("foo") } == "23"
624
- insist { subject.get("tags") }.nil?
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 <<-CONFIG
630
- filter {
631
- grok {
632
- match => { "message" => ["%{INT:foo}", "%{WORD:bar}"] }
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
- CONFIG
637
-
638
- # array input --
639
- sample("message" => ["hello world 123", "line 23"]) do
640
- insist { subject.get("foo") } == ["123", "23"]
641
- insist { subject.get("bar") } == ["hello", "line"]
642
- insist { subject.get("tags") }.nil?
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
- # array input, one of them matches
646
- sample("message" => ["hello world", "line 23"]) do
647
- insist { subject.get("bar") } == ["hello", "line"]
648
- insist { subject.get("foo") } == "23"
649
- insist { subject.get("tags") }.nil?
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 <<-CONFIG
655
- filter {
656
- grok {
657
- #match => { "message" => "<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{PROG:syslog_program}(?:\[%{POSINT:syslog_pid}\])?: %{GREEDYDATA:syslog_message}" }
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
- CONFIG
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
- insist { subject.get("tags") }.nil?
665
- insist { subject.get("syslog_pri") } == "22"
666
- insist { subject.get("syslog_program") } == "postfix/policy-spf"
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 "patterns in the 'patterns/' dir override core patterns" do
671
-
672
- let(:pattern_dir) { File.join(LogStash::Environment::LOGSTASH_HOME, "patterns") }
673
- let(:has_pattern_dir?) { Dir.exist?(pattern_dir) }
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
- before do
676
- FileUtils.mkdir(pattern_dir) unless has_pattern_dir?
677
- @file = File.new(File.join(pattern_dir, 'grok.pattern'), 'w+')
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
- let(:config) do
683
- 'filter { grok { match => { "message" => "%{WORD:word}" } } }'
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("message" => 'hello') do
687
- insist { subject.get("tags") } == ["_grokparsefailure"]
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
- after do
691
- File.unlink @file
692
- FileUtils.rm_rf(pattern_dir) if has_pattern_dir?
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 "patterns in custom dir override those in 'patterns/' dir" do
697
-
698
- let(:tmpdir) { Stud::Temporary.directory }
699
- let(:pattern_dir) { File.join(LogStash::Environment::LOGSTASH_HOME, "patterns") }
700
- let(:has_pattern_dir?) { Dir.exist?(pattern_dir) }
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
- before do
703
- FileUtils.mkdir(pattern_dir) unless has_pattern_dir?
704
- @file1 = File.new(File.join(pattern_dir, 'grok.pattern'), 'w+')
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
- let(:config) do
713
- "filter { grok { patterns_dir => \"#{tmpdir}\" match => { \"message\" => \"%{WORD:word}\" } } }"
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
- sample("message" => '0') do
717
- insist { subject.get("tags") } == nil
718
- end
679
+ describe "opening/closing" do
680
+ let(:config) { { "match" => {"message" => "A"} } }
681
+ let(:message) { 'AAA' }
719
682
 
720
- after do
721
- File.unlink @file1
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 "patterns with file glob" do
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
- let(:tmpdir) { Stud::Temporary.directory }
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
- before do
733
- @file3 = File.new(File.join(tmpdir, 'grok.pattern'), 'w+')
734
- @file3.write('WORD \b[0-1]\b')
735
- @file3.close
736
- @file4 = File.new(File.join(tmpdir, 'grok.pattern.old'), 'w+')
737
- @file4.write('WORD \b[2-5]\b')
738
- @file4.close
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
- sample("message" => '0') do
746
- insist { subject.get("tags") } == nil
747
- end
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
- after do
750
- File.unlink @file3
751
- File.unlink @file4
752
- FileUtils.remove_entry tmpdir
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 "patterns with file glob on directory that contains subdirectories" do
757
-
758
- let(:tmpdir) { Stud::Temporary.directory }
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
- before do
761
- @file3 = File.new(File.join(tmpdir, 'grok.pattern'), 'w+')
762
- @file3.write('WORD \b[0-1]\b')
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
- "filter { grok { patterns_dir => \"#{tmpdir}\" patterns_files_glob => \"*\" match => { \"message\" => \"%{WORD:word}\" } } }"
751
+ {
752
+ "timeout_millis" => 0
753
+ }
769
754
  end
770
755
 
771
- sample("message" => '0') do
772
- insist { subject.get("tags") } == nil
773
- end
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
- after do
776
- File.unlink @file3
777
- FileUtils.remove_entry tmpdir
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
- describe "grok with nil coerced value" do
782
- config <<-CONFIG
783
- filter {
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
- sample "test 28.4ms" do
791
- insist { subject.get("duration") } == 28.4
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
- sample "test N/A" do
796
- insist { insist { subject.to_hash }.include?("duration") }.fails
797
- insist { subject.get("tags") }.nil?
798
- end
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
- sample "test abc" do
801
- insist { subject.get("duration") }.nil?
802
- insist { subject.get("tags") } == ["_grokparsefailure"]
803
- end
804
- end
786
+ let(:config) do
787
+ 'filter { grok { match => { "message" => "%{WORD:word}" } } }'
788
+ end
805
789
 
806
- describe "grok with nil coerced value and keep_empty_captures" do
807
- config <<-CONFIG
808
- filter {
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
- sample "test N/A" do
817
- insist { subject.to_hash }.include?("duration")
818
- insist { subject.get("tags") }.nil?
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
- end
800
+ describe "patterns in custom dir override those in 'patterns/' dir" do
822
801
 
823
- describe "grok with no coercion" do
824
- config <<-CONFIG
825
- filter {
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
- sample "test 28.4ms" do
833
- insist { subject.get("duration") } == "28.4"
834
- insist { subject.get("tags") }.nil?
835
- end
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
- sample "test N/A" do
838
- insist { subject.get("duration") }.nil?
839
- insist { subject.get("tags") }.nil?
840
- end
841
- end
816
+ let(:config) do
817
+ "filter { grok { patterns_dir => \"#{tmpdir}\" match => { \"message\" => \"%{WORD:word}\" } } }"
818
+ end
842
819
 
843
- describe "opening/closing" do
844
- let(:config) { {"match" => {"message" => "A"}} }
845
- subject(:plugin) do
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
- before do
850
- plugin.register
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
- it "should start the timeout enforcer" do
854
- expect(plugin.timeout_enforcer.running).to be true
855
- end
832
+ describe "patterns with file glob" do
856
833
 
857
- context "with the timeout enforcer disabled" do
858
- let(:config) { super.merge("timeout_millis" => 0) }
834
+ let(:tmpdir) { Stud::Temporary.directory }
859
835
 
860
- it "should not start the timeout enforcer" do
861
- expect(plugin.timeout_enforcer.running).to be false
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
- it "should close cleanly" do
866
- expect { plugin.do_close }.not_to raise_error
867
- end
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
- it "should stop the timeout enforcer" do
870
- plugin.do_close
871
- expect(plugin.timeout_enforcer.running).to be false
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
- describe "after grok when the event is JSON serialised the field values are unchanged" do
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
- sample('{"facility":"auth","message":"Failed password for testuser from 1.1.1.1 port 22"}') do
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
- insist { subject.to_json } =~ %r|"src_ip":"1.1.1.1"|
887
- insist { subject.to_json } =~ %r|"@timestamp":"20\d\d-\d\d-\d\dT\d\d:\d\d:\d\d\.\d\d\dZ"|
888
- insist { subject.to_json } =~ %r|"port":"22"|
889
- insist { subject.to_json } =~ %r|"@version":"1"|
890
- insist { subject.to_json } =~ %r|"username"|i
891
- insist { subject.to_json } =~ %r|"testuser"|
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
- describe "grok with inline pattern definition successfully extracts fields" do
897
- config <<-CONFIG
898
- filter {
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
- sample "Mon Dec 26 16:22:08 2016 error 2020" do
908
- insist { subject.get("timestamp") } == "Mon Dec 26 16:22:08 2016"
909
- insist { subject.get("level") } == "error"
910
- insist { subject.get("hindsight") } == "2020"
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
- describe "grok with inline pattern definition overwrites existing pattern definition" do
915
- config <<-CONFIG
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" => "%{APACHE_TIME:timestamp} %{LOGLEVEL:level}" }
919
- # loglevel was previously ([Aa]lert|ALERT|[Tt]...
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
- CONFIG
893
+ CONFIG
925
894
 
926
- sample "Mon Dec 26 16:22:08 2016 9999" do
927
- insist { subject.get("timestamp") } == "Mon Dec 26 16:22:08 2016"
928
- insist { subject.get("level") } == "9999"
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