logstash-input-beats 3.1.0.beta1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +131 -0
  3. data/CONTRIBUTORS +17 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +14 -0
  6. data/NOTICE.TXT +5 -0
  7. data/PROTOCOL.md +127 -0
  8. data/README.md +98 -0
  9. data/VERSION +1 -0
  10. data/lib/logstash-input-beats_jars.rb +17 -0
  11. data/lib/logstash/inputs/beats.rb +184 -0
  12. data/lib/logstash/inputs/beats/codec_callback_listener.rb +26 -0
  13. data/lib/logstash/inputs/beats/decoded_event_transform.rb +34 -0
  14. data/lib/logstash/inputs/beats/event_transform_common.rb +48 -0
  15. data/lib/logstash/inputs/beats/message_listener.rb +96 -0
  16. data/lib/logstash/inputs/beats/raw_event_transform.rb +18 -0
  17. data/lib/logstash/inputs/beats/tls.rb +40 -0
  18. data/lib/tasks/build.rake +15 -0
  19. data/lib/tasks/test.rake +65 -0
  20. data/logstash-input-beats.gemspec +41 -0
  21. data/spec/inputs/beats/codec_callback_listener_spec.rb +33 -0
  22. data/spec/inputs/beats/decoded_event_transform_spec.rb +74 -0
  23. data/spec/inputs/beats/event_transform_common_spec.rb +11 -0
  24. data/spec/inputs/beats/message_listener_spec.rb +108 -0
  25. data/spec/inputs/beats/raw_event_transform_spec.rb +26 -0
  26. data/spec/inputs/beats/tls_spec.rb +39 -0
  27. data/spec/inputs/beats_spec.rb +99 -0
  28. data/spec/integration/filebeat_spec.rb +234 -0
  29. data/spec/integration/logstash_forwarder_spec.rb +104 -0
  30. data/spec/spec_helper.rb +14 -0
  31. data/spec/support/client_process_helpers.rb +28 -0
  32. data/spec/support/file_helpers.rb +61 -0
  33. data/spec/support/flores_extensions.rb +82 -0
  34. data/spec/support/integration_shared_context.rb +73 -0
  35. data/spec/support/logstash_test.rb +66 -0
  36. data/spec/support/shared_examples.rb +56 -0
  37. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-annotations/2.7.5/jackson-annotations-2.7.5.jar +0 -0
  38. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-core/2.7.5/jackson-core-2.7.5.jar +0 -0
  39. data/vendor/jar-dependencies/com/fasterxml/jackson/core/jackson-databind/2.7.5/jackson-databind-2.7.5.jar +0 -0
  40. data/vendor/jar-dependencies/com/fasterxml/jackson/module/jackson-module-afterburner/2.7.5/jackson-module-afterburner-2.7.5.jar +0 -0
  41. data/vendor/jar-dependencies/io/netty/netty-all/4.1.1.Final/netty-all-4.1.1.Final.jar +0 -0
  42. data/vendor/jar-dependencies/io/netty/netty-tcnative-boringssl-static/1.1.33.Fork17/netty-tcnative-boringssl-static-1.1.33.Fork17.jar +0 -0
  43. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-1.2-api/2.6.1/log4j-1.2-api-2.6.1.jar +0 -0
  44. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-api/2.6.1/log4j-api-2.6.1.jar +0 -0
  45. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-core/2.6.1/log4j-core-2.6.1.jar +0 -0
  46. data/vendor/jar-dependencies/org/apache/logging/log4j/log4j-slf4j-impl/2.6.1/log4j-slf4j-impl-2.6.1.jar +0 -0
  47. data/vendor/jar-dependencies/org/bouncycastle/bcpkix-jdk15on/1.54/bcpkix-jdk15on-1.54.jar +0 -0
  48. data/vendor/jar-dependencies/org/bouncycastle/bcprov-jdk15on/1.54/bcprov-jdk15on-1.54.jar +0 -0
  49. data/vendor/jar-dependencies/org/javassist/javassist/3.20.0-GA/javassist-3.20.0-GA.jar +0 -0
  50. data/vendor/jar-dependencies/org/logstash/beats/logstash-input-beats/3.1.0.beta1/logstash-input-beats-3.1.0.beta1.jar +0 -0
  51. metadata +313 -0
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+ require "logstash/event"
3
+ require "logstash/inputs/beats"
4
+ require_relative "../../support/shared_examples"
5
+ require "spec_helper"
6
+
7
+ describe LogStash::Inputs::Beats::EventTransformCommon do
8
+ subject { described_class.new(input).transform(event) }
9
+
10
+ include_examples "Common Event Transformation"
11
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/beats"
3
+ require "logstash/event"
4
+ require "logstash/inputs/beats/message_listener"
5
+ require "thread"
6
+
7
+ class MockMessage
8
+ def initialize(identity_stream, data = {})
9
+ @identity_stream = identity_stream
10
+ @data = data
11
+ end
12
+
13
+ def getData
14
+ @data
15
+ end
16
+
17
+ def getIdentityStream
18
+ @identity_stream
19
+ end
20
+ end
21
+
22
+ class DummyCodec < LogStash::Codecs::Base
23
+ DUMMY_EVENT = LogStash::Event.new
24
+
25
+ def decode(message, &block)
26
+ block.call(LogStash::Event.new({"message" => message}))
27
+ end
28
+
29
+ def flush(&block)
30
+ block.call(DUMMY_EVENT)
31
+ end
32
+ end
33
+
34
+ describe LogStash::Inputs::Beats::MessageListener do
35
+ let(:queue) { Queue.new }
36
+ let(:codec) { DummyCodec.new }
37
+ let(:input) { LogStash::Inputs::Beats.new({ "port" => 5555, "codec" => codec }) }
38
+ let(:ctx) { double("ChannelHandlerContext") }
39
+ let(:message) { MockMessage.new("abc", { "message" => "hello world"}) }
40
+
41
+ subject { described_class.new(queue, input) }
42
+
43
+ before do
44
+ subject.onNewConnection(ctx)
45
+ end
46
+
47
+ context "onNewConnection" do
48
+ it "register the connection to the connection list" do
49
+ expect { subject.onNewConnection(double("ChannelHandlerContext")) }.to change { subject.connections_list.count }.by(1)
50
+ end
51
+ end
52
+
53
+ context "onNewMessage" do
54
+ context "when the message is from filebeat" do
55
+ let(:message) { MockMessage.new("abc", { "message" => "hello world" } )}
56
+
57
+ it "extract the event" do
58
+ subject.onNewMessage(ctx, message)
59
+ event = queue.pop
60
+ expect(event.get("message")).to eq("hello world")
61
+ end
62
+ end
63
+
64
+ context "when the message is from LSF" do
65
+ let(:message) { MockMessage.new("abc", { "line" => "hello world" } )}
66
+
67
+ it "extract the event" do
68
+ subject.onNewMessage(ctx, message)
69
+ event = queue.pop
70
+ expect(event.get("message")).to eq("hello world")
71
+ end
72
+ end
73
+
74
+ context "when the message is from any libbeat" do
75
+ let(:message) { MockMessage.new("abc", { "metric" => 1, "name" => "super-stats"} )}
76
+
77
+ it "extract the event" do
78
+ subject.onNewMessage(ctx, message)
79
+ event = queue.pop
80
+ expect(event.get("message")).to be_nil
81
+ expect(event.get("metric")).to eq(1)
82
+ expect(event.get("name")).to eq("super-stats")
83
+ end
84
+ end
85
+ end
86
+
87
+ context "onException" do
88
+ it "remove the connection to the connection list" do
89
+ expect { subject.onException(ctx) }.to change { subject.connections_list.count }.by(-1)
90
+ end
91
+
92
+ it "calls flush on codec" do
93
+ subject.onConnectionClose(ctx)
94
+ expect(queue).not_to be_empty
95
+ end
96
+ end
97
+
98
+ context "onConnectionClose" do
99
+ it "remove the connection to the connection list" do
100
+ expect { subject.onConnectionClose(ctx) }.to change { subject.connections_list.count }.by(-1)
101
+ end
102
+
103
+ it "calls flush on codec" do
104
+ subject.onConnectionClose(ctx)
105
+ expect(queue).not_to be_empty
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ require "logstash/event"
3
+ require "logstash/inputs/beats"
4
+ require_relative "../../support/shared_examples"
5
+ require "spec_helper"
6
+
7
+ describe LogStash::Inputs::Beats::RawEventTransform do
8
+ let(:config) do
9
+ {
10
+ "port" => 0,
11
+ "type" => "example",
12
+ "tags" => "beats"
13
+ }
14
+ end
15
+
16
+ let(:input) { LogStash::Inputs::Beats.new(config) }
17
+ let(:event) { LogStash::Event.new }
18
+
19
+ subject { described_class.new(input).transform(event) }
20
+
21
+ include_examples "Common Event Transformation"
22
+
23
+ it "tags the event" do
24
+ expect(subject.get("tags")).to include("beats_input_raw_event")
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/beats/tls"
3
+
4
+ describe LogStash::Inputs::Beats::TLS do
5
+ subject { described_class }
6
+
7
+ it "returns the minimum supported tls" do
8
+ expect(subject.min.version).to eq(1)
9
+ expect(subject.min.name).to eq("TLSv1")
10
+ end
11
+
12
+ it "returns the maximum supported tls" do
13
+ expect(subject.max.version).to eq(1.2)
14
+ expect(subject.max.name).to eq("TLSv1.2")
15
+ end
16
+
17
+ describe ".get_supported" do
18
+ context "when a range is given" do
19
+ it "returns the list of compatible TLS from a range" do
20
+ expect(subject.get_supported((1.1)..(1.2)).map(&:version)).to match([1.1, 1.2])
21
+ end
22
+
23
+ it "it return an empty array when nothing match" do
24
+ expect(subject.get_supported((3.1)..(8.2))).to be_empty
25
+ end
26
+ end
27
+
28
+ context "when a scalar is given" do
29
+ it "when a scalar is given we return the compatible value" do
30
+ expect(subject.get_supported(1.1).map(&:version)).to match([1.1])
31
+ end
32
+
33
+
34
+ it "it return an empty array when nothing match" do
35
+ expect(subject.get_supported(9)).to be_empty
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,99 @@
1
+ # encoding: utf-8
2
+ require_relative "../spec_helper"
3
+ require "stud/temporary"
4
+ require "logstash/inputs/beats"
5
+ require "logstash/codecs/plain"
6
+ require "logstash/codecs/json"
7
+ require "logstash/codecs/multiline"
8
+ require "logstash/event"
9
+
10
+ describe LogStash::Inputs::Beats do
11
+ let(:connection) { double("connection") }
12
+ let(:certificate) { BeatsInputTest.certificate }
13
+ let(:port) { BeatsInputTest.random_port }
14
+ let(:queue) { Queue.new }
15
+ let(:config) { { "port" => 0, "ssl_certificate" => certificate.ssl_cert, "ssl_key" => certificate.ssl_key, "type" => "example", "tags" => "beats"} }
16
+
17
+ context "#register" do
18
+ context "identity map" do
19
+ subject(:plugin) { LogStash::Inputs::Beats.new(config) }
20
+ before { plugin.register }
21
+
22
+ context "when using the multiline codec" do
23
+ let(:codec) { LogStash::Codecs::Multiline.new("pattern" => '^2015',
24
+ "what" => "previous",
25
+ "negate" => true) }
26
+ let(:config) { super.merge({ "codec" => codec }) }
27
+
28
+ it "wraps the codec with the identity_map" do
29
+ expect(plugin.codec).to be_kind_of(LogStash::Codecs::IdentityMapCodec)
30
+ end
31
+ end
32
+
33
+ context "when using non buffered codecs" do
34
+ let(:config) { super.merge({ "codec" => "json" }) }
35
+
36
+ it "doesnt wrap the codec with the identity map" do
37
+ expect(plugin.codec).to be_kind_of(LogStash::Codecs::JSON)
38
+ end
39
+ end
40
+ end
41
+
42
+ it "raise no exception" do
43
+ plugin = LogStash::Inputs::Beats.new(config)
44
+ expect { plugin.register }.not_to raise_error
45
+ end
46
+
47
+ context "with ssl enabled" do
48
+ context "without certificate configuration" do
49
+ let(:config) {{ "port" => 0, "ssl" => true, "ssl_key" => certificate.ssl_key, "type" => "example", "tags" => "beats" }}
50
+
51
+ it "should fail to register the plugin with ConfigurationError" do
52
+ plugin = LogStash::Inputs::Beats.new(config)
53
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError)
54
+ end
55
+ end
56
+
57
+ context "without key configuration" do
58
+ let(:config) { { "port" => 0, "ssl" => true, "ssl_certificate" => certificate.ssl_cert, "type" => "example", "tags" => "Beats"} }
59
+ it "should fail to register the plugin with ConfigurationError" do
60
+ plugin = LogStash::Inputs::Beats.new(config)
61
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError)
62
+ end
63
+ end
64
+ end
65
+
66
+ context "with ssl disabled" do
67
+ context "and certificate configuration" do
68
+ let(:config) { { "port" => 0, "ssl" => false, "ssl_certificate" => certificate.ssl_cert, "type" => "example", "tags" => "Beats" } }
69
+
70
+ it "should not fail" do
71
+ plugin = LogStash::Inputs::Beats.new(config)
72
+ expect {plugin.register}.not_to raise_error
73
+ end
74
+ end
75
+
76
+ context "and certificate key configuration" do
77
+ let(:config) {{ "port" => 0, "ssl" => false, "ssl_key" => certificate.ssl_key, "type" => "example", "tags" => "beats" }}
78
+
79
+ it "should not fail" do
80
+ plugin = LogStash::Inputs::Beats.new(config)
81
+ expect {plugin.register}.not_to raise_error
82
+ end
83
+ end
84
+
85
+ context "and no certificate or key configured" do
86
+ let(:config) {{ "ssl" => false, "port" => 0, "type" => "example", "tags" => "beats" }}
87
+
88
+ it "should work just fine" do
89
+ plugin = LogStash::Inputs::Beats.new(config)
90
+ expect {plugin.register}.not_to raise_error
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ context "when interrupting the plugin" do
97
+ it_behaves_like "an interruptible input plugin"
98
+ end
99
+ end
@@ -0,0 +1,234 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/beats"
3
+ require "stud/temporary"
4
+ require "flores/pki"
5
+ require "fileutils"
6
+ require "thread"
7
+ require "spec_helper"
8
+ require "yaml"
9
+ require "fileutils"
10
+ require "flores/pki"
11
+ require_relative "../support/flores_extensions"
12
+ require_relative "../support/file_helpers"
13
+ require_relative "../support/integration_shared_context"
14
+ require_relative "../support/client_process_helpers"
15
+
16
+ FILEBEAT_BINARY = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "vendor", "filebeat", "filebeat"))
17
+
18
+ describe "Filebeat", :integration => true do
19
+ include ClientProcessHelpers
20
+ include FileHelpers
21
+
22
+ before :all do
23
+ unless File.exist?(FILEBEAT_BINARY)
24
+ raise "Cannot find `Filebeat` binary in `vendor/filebeat`. Did you run `bundle exec rake test:integration:setup` before running the integration suite?"
25
+ end
26
+ end
27
+
28
+ include_context "beats configuration"
29
+
30
+ # Filebeat related variables
31
+ let(:cmd) { [filebeat_exec, "-c", filebeat_config_path, "-e", "-v"] }
32
+
33
+ let(:filebeat_exec) { FILEBEAT_BINARY }
34
+
35
+ let_empty_tmp_file(:registry_file)
36
+ let(:filebeat_config) do
37
+ {
38
+ "filebeat" => {
39
+ "prospectors" => [{ "paths" => [log_file], "input_type" => "log" }],
40
+ "registry_file" => registry_file,
41
+ "scan_frequency" => "1s"
42
+ },
43
+ "output" => {
44
+ "logstash" => { "hosts" => ["#{host}:#{port}"] },
45
+ "logging" => { "level" => "debug" }
46
+ }
47
+ }
48
+ end
49
+
50
+ let_tmp_file(:filebeat_config_path) { YAML.dump(filebeat_config) }
51
+ before :each do
52
+ start_client
53
+ sleep(2) # give some time to FB to send something
54
+ stop_client
55
+ end
56
+
57
+ ###########################################################
58
+ shared_context "Root CA" do
59
+ let(:root_ca) { Flores::PKI.generate("CN=root.localhost") }
60
+ let(:root_ca_certificate) { root_ca.first }
61
+ let(:root_ca_key) { root_ca.last }
62
+ let_tmp_file(:root_ca_certificate_file) { root_ca_certificate }
63
+ end
64
+
65
+ shared_context "Intermediate CA" do
66
+ let(:intermediate_ca) { Flores::PKI.create_intermediate_certificate("CN=intermediate.localhost", root_ca_certificate, root_ca_key) }
67
+ let(:intermediate_ca_certificate) { intermediate_ca.first }
68
+ let(:intermediate_ca_key) { intermediate_ca.last }
69
+ let_tmp_file(:intermediate_ca_certificate_file) { intermediate_ca_certificate }
70
+ let_tmp_file(:certificate_authorities_chain) { Flores::PKI.chain_certificates(root_ca_certificate, intermediate_ca_certificate) }
71
+ end
72
+
73
+ ############################################################
74
+ # Actuals tests
75
+ context "Plain TCP" do
76
+ include_examples "send events"
77
+ end
78
+
79
+ context "TLS" do
80
+ context "Server verification" do
81
+ let(:filebeat_config) do
82
+ super.merge({
83
+ "output" => {
84
+ "logstash" => {
85
+ "hosts" => ["#{host}:#{port}"],
86
+ "tls" => { "certificate_authorities" => certificate_authorities }
87
+ },
88
+ "logging" => { "level" => "debug" }
89
+ }})
90
+ end
91
+
92
+ let(:input_config) do
93
+ super.merge({
94
+ "ssl" => true,
95
+ "ssl_certificate" => certificate_file,
96
+ "ssl_key" => certificate_key_file
97
+ })
98
+ end
99
+
100
+ let(:certificate_authorities) { [certificate_file] }
101
+ let(:certificate_data) { Flores::PKI.generate }
102
+ let_tmp_file(:certificate_key_file) { certificate_data.last }
103
+ let_tmp_file(:certificate_file) { certificate_data.first }
104
+
105
+ context "self signed certificate" do
106
+ include_examples "send events"
107
+ end
108
+
109
+ context "CA root" do
110
+ include_context "Root CA"
111
+
112
+ context "directly signed client certificate" do
113
+ let(:certificate_authorities) { [root_ca_certificate_file] }
114
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
115
+
116
+ include_examples "send events"
117
+ end
118
+
119
+ context "intermediate CA signs client certificate" do
120
+ include_context "Intermediate CA"
121
+
122
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
123
+ let(:certificate_authorities) { [certificate_authorities_chain] }
124
+
125
+ include_examples "send events"
126
+ end
127
+ end
128
+
129
+ context "Client verification / Mutual validation" do
130
+ let(:filebeat_config) do
131
+ super.merge({
132
+ "output" => {
133
+ "logstash" => {
134
+ "hosts" => ["#{host}:#{port}"],
135
+ "tls" => {
136
+ "certificate_authorities" => certificate_authorities,
137
+ "certificate" => certificate_file,
138
+ "certificate_key" => certificate_key_file
139
+ }
140
+ },
141
+ "logging" => { "level" => "debug" }
142
+ }})
143
+ end
144
+
145
+ let(:input_config) do
146
+ super.merge({
147
+ "ssl" => true,
148
+ "ssl_certificate_authorities" => certificate_authorities,
149
+ "ssl_certificate" => server_certificate_file,
150
+ "ssl_key" => server_certificate_key_file,
151
+ "ssl_verify_mode" => "force_peer"
152
+ })
153
+ end
154
+
155
+ context "with a self signed certificate" do
156
+ let(:certificate_authorities) { [certificate_file] }
157
+ let(:certificate_data) { Flores::PKI.generate }
158
+ let_tmp_file(:certificate_key_file) { certificate_data.last }
159
+ let_tmp_file(:certificate_file) { certificate_data.first }
160
+ let_tmp_file(:server_certificate_file) { certificate_data.first }
161
+ let_tmp_file(:server_certificate_key_file) { certificate_data.last }
162
+
163
+ include_examples "send events"
164
+ end
165
+
166
+ context "CA root" do
167
+ include_context "Root CA"
168
+
169
+ let_tmp_file(:server_certificate_file) { server_certificate_data.first }
170
+ let_tmp_file(:server_certificate_key_file) { server_certificate_data.last }
171
+
172
+ context "directly signed client certificate" do
173
+ let(:certificate_authorities) { [root_ca_certificate_file] }
174
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
175
+ let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
176
+
177
+ include_examples "send events"
178
+ end
179
+
180
+ ### DOESNT WORK
181
+ context "intermediate create server and client certificate" do
182
+ include_context "Intermediate CA"
183
+
184
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
185
+ let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
186
+ let(:certificate_authorities) { [intermediate_ca_certificate_file] }
187
+
188
+ include_examples "send events"
189
+ end
190
+
191
+ context "and Secondary CA multiples clients" do
192
+ context "with CA in different files" do
193
+ let(:secondary_ca) { Flores::PKI.generate }
194
+ let(:secondary_ca_key) { secondary_ca.last }
195
+ let(:secondary_ca_certificate) { secondary_ca.first }
196
+ let_tmp_file(:secondary_ca_certificate_file) { secondary_ca.first }
197
+
198
+ let(:secondary_client_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", secondary_ca_certificate, secondary_ca_key) }
199
+ let_tmp_file(:secondary_client_certificate_file) { secondary_client_certificate_data.first }
200
+ let_tmp_file(:secondary_client_certificate_key_file) { secondary_client_certificate_data.last }
201
+ let(:certificate_authorities) { [root_ca_certificate_file, secondary_ca_certificate_file] }
202
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
203
+
204
+ let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
205
+
206
+ context "client from primary CA" do
207
+ include_examples "send events"
208
+ end
209
+
210
+ context "client from secondary CA" do
211
+ let(:filebeat_config) do
212
+ super.merge({
213
+ "output" => {
214
+ "logstash" => {
215
+ "hosts" => ["#{host}:#{port}"],
216
+ "tls" => {
217
+ "certificate_authorities" => certificate_authorities,
218
+ "certificate" => secondary_client_certificate_file,
219
+ "certificate_key" => secondary_client_certificate_key_file
220
+ }
221
+ },
222
+ "logging" => { "level" => "debug" }
223
+ }})
224
+ end
225
+
226
+ include_examples "send events"
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end