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.
@@ -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