logstash-integration-kafka 10.7.4-java → 10.7.5-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/logstash/inputs/kafka.rb +90 -49
- data/logstash-integration-kafka.gemspec +1 -1
- data/spec/check_docs_spec.rb +36 -0
- data/spec/integration/inputs/kafka_spec.rb +2 -1
- data/spec/integration/outputs/kafka_spec.rb +1 -1
- data/spec/unit/inputs/kafka_spec.rb +162 -22
- data/spec/unit/outputs/kafka_spec.rb +28 -36
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e76697fe666ea555f7256a8a42865a749ee00e05f9e7294d89c481b1ba6a7c8
|
4
|
+
data.tar.gz: 5cfffe44f6e36776efb87a77e3a30ac03cdc802ae769aa423abdae4e0a36a6d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9052d2ae8570e274840882751383a0cfb3c76e6858ce0519ccfdf2be8ca773b0becc9c27e1ec0f52319004b5ee21e4fdd32d60affed719f5b54b4772b4329541
|
7
|
+
data.tar.gz: ff4924b87505befa7f19ddfc2699c109fbdcd53c313b4b7cb5775b7fe6de2853cc98480fae1c9267463e88eac3dbf8c2484178af803505fb820aa0b26af4e5e9
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## 10.7.5
|
2
|
+
- Improved error handling in the input plugin to avoid errors 'escaping' from the plugin, and crashing the logstash
|
3
|
+
process [#87](https://github.com/logstash-plugins/logstash-integration-kafka/pull/87)
|
4
|
+
|
1
5
|
## 10.7.4
|
2
6
|
- Docs: make sure Kafka clients version is updated in docs [#83](https://github.com/logstash-plugins/logstash-integration-kafka/pull/83)
|
3
7
|
Since **10.6.0** Kafka client was updated to **2.5.1**
|
@@ -253,6 +253,7 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
253
253
|
def register
|
254
254
|
@runner_threads = []
|
255
255
|
@metadata_mode = extract_metadata_level(@decorate_events)
|
256
|
+
@pattern ||= java.util.regex.Pattern.compile(@topics_pattern) unless @topics_pattern.nil?
|
256
257
|
check_schema_registry_parameters
|
257
258
|
end
|
258
259
|
|
@@ -280,9 +281,11 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
280
281
|
|
281
282
|
public
|
282
283
|
def run(logstash_queue)
|
283
|
-
@runner_consumers = consumer_threads.times.map { |i| create_consumer("#{client_id}-#{i}") }
|
284
|
-
@runner_threads = @runner_consumers.map { |consumer| thread_runner(logstash_queue, consumer
|
285
|
-
|
284
|
+
@runner_consumers = consumer_threads.times.map { |i| subscribe(create_consumer("#{client_id}-#{i}")) }
|
285
|
+
@runner_threads = @runner_consumers.map.with_index { |consumer, i| thread_runner(logstash_queue, consumer,
|
286
|
+
"kafka-input-worker-#{client_id}-#{i}") }
|
287
|
+
@runner_threads.each(&:start)
|
288
|
+
@runner_threads.each(&:join)
|
286
289
|
end # def run
|
287
290
|
|
288
291
|
public
|
@@ -296,62 +299,100 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
296
299
|
@runner_consumers
|
297
300
|
end
|
298
301
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
+
def subscribe(consumer)
|
303
|
+
@pattern.nil? ? consumer.subscribe(topics) : consumer.subscribe(@pattern)
|
304
|
+
consumer
|
305
|
+
end
|
306
|
+
|
307
|
+
def thread_runner(logstash_queue, consumer, name)
|
308
|
+
java.lang.Thread.new do
|
309
|
+
LogStash::Util::set_thread_name(name)
|
302
310
|
begin
|
303
|
-
unless @topics_pattern.nil?
|
304
|
-
nooplistener = org.apache.kafka.clients.consumer.internals.NoOpConsumerRebalanceListener.new
|
305
|
-
pattern = java.util.regex.Pattern.compile(@topics_pattern)
|
306
|
-
consumer.subscribe(pattern, nooplistener)
|
307
|
-
else
|
308
|
-
consumer.subscribe(topics);
|
309
|
-
end
|
310
311
|
codec_instance = @codec.clone
|
311
|
-
|
312
|
-
records = consumer
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
decorate(event)
|
317
|
-
if schema_registry_url
|
318
|
-
json = LogStash::Json.load(record.value.to_s)
|
319
|
-
json.each do |k, v|
|
320
|
-
event.set(k, v)
|
321
|
-
end
|
322
|
-
event.remove("message")
|
323
|
-
end
|
324
|
-
if @metadata_mode.include?(:record_props)
|
325
|
-
event.set("[@metadata][kafka][topic]", record.topic)
|
326
|
-
event.set("[@metadata][kafka][consumer_group]", @group_id)
|
327
|
-
event.set("[@metadata][kafka][partition]", record.partition)
|
328
|
-
event.set("[@metadata][kafka][offset]", record.offset)
|
329
|
-
event.set("[@metadata][kafka][key]", record.key)
|
330
|
-
event.set("[@metadata][kafka][timestamp]", record.timestamp)
|
331
|
-
end
|
332
|
-
if @metadata_mode.include?(:headers)
|
333
|
-
record.headers.each do |header|
|
334
|
-
s = String.from_java_bytes(header.value)
|
335
|
-
s.force_encoding(Encoding::UTF_8)
|
336
|
-
if s.valid_encoding?
|
337
|
-
event.set("[@metadata][kafka][headers]["+header.key+"]", s)
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
341
|
-
logstash_queue << event
|
342
|
-
end
|
312
|
+
until stop?
|
313
|
+
records = do_poll(consumer)
|
314
|
+
unless records.empty?
|
315
|
+
records.each { |record| handle_record(record, codec_instance, logstash_queue) }
|
316
|
+
maybe_commit_offset(consumer)
|
343
317
|
end
|
344
|
-
# Manual offset commit
|
345
|
-
consumer.commitSync if @enable_auto_commit.eql?(false)
|
346
318
|
end
|
347
|
-
rescue org.apache.kafka.common.errors.WakeupException => e
|
348
|
-
raise e if !stop?
|
349
319
|
ensure
|
350
320
|
consumer.close
|
351
321
|
end
|
352
322
|
end
|
353
323
|
end
|
354
324
|
|
325
|
+
def do_poll(consumer)
|
326
|
+
records = []
|
327
|
+
begin
|
328
|
+
records = consumer.poll(poll_timeout_ms)
|
329
|
+
rescue org.apache.kafka.common.errors.WakeupException => e
|
330
|
+
logger.debug("Wake up from poll", :kafka_error_message => e)
|
331
|
+
raise e unless stop?
|
332
|
+
rescue => e
|
333
|
+
logger.error("Unable to poll Kafka consumer",
|
334
|
+
:kafka_error_message => e,
|
335
|
+
:cause => e.respond_to?(:getCause) ? e.getCause : nil)
|
336
|
+
Stud.stoppable_sleep(1) { stop? }
|
337
|
+
end
|
338
|
+
records
|
339
|
+
end
|
340
|
+
|
341
|
+
def handle_record(record, codec_instance, queue)
|
342
|
+
codec_instance.decode(record.value.to_s) do |event|
|
343
|
+
decorate(event)
|
344
|
+
maybe_apply_schema(event, record)
|
345
|
+
maybe_set_metadata(event, record)
|
346
|
+
queue << event
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def maybe_apply_schema(event, record)
|
351
|
+
if schema_registry_url
|
352
|
+
json = LogStash::Json.load(record.value.to_s)
|
353
|
+
json.each do |k, v|
|
354
|
+
event.set(k, v)
|
355
|
+
end
|
356
|
+
event.remove("message")
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
def maybe_set_metadata(event, record)
|
361
|
+
if @metadata_mode.include?(:record_props)
|
362
|
+
event.set("[@metadata][kafka][topic]", record.topic)
|
363
|
+
event.set("[@metadata][kafka][consumer_group]", @group_id)
|
364
|
+
event.set("[@metadata][kafka][partition]", record.partition)
|
365
|
+
event.set("[@metadata][kafka][offset]", record.offset)
|
366
|
+
event.set("[@metadata][kafka][key]", record.key)
|
367
|
+
event.set("[@metadata][kafka][timestamp]", record.timestamp)
|
368
|
+
end
|
369
|
+
if @metadata_mode.include?(:headers)
|
370
|
+
record.headers.each do |header|
|
371
|
+
s = String.from_java_bytes(header.value)
|
372
|
+
s.force_encoding(Encoding::UTF_8)
|
373
|
+
if s.valid_encoding?
|
374
|
+
event.set("[@metadata][kafka][headers][" + header.key + "]", s)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def maybe_commit_offset(consumer)
|
381
|
+
begin
|
382
|
+
consumer.commitSync if @enable_auto_commit.eql?(false)
|
383
|
+
rescue org.apache.kafka.common.errors.WakeupException => e
|
384
|
+
logger.debug("Wake up from commitSync", :kafka_error_message => e)
|
385
|
+
raise e unless stop?
|
386
|
+
rescue StandardError => e
|
387
|
+
# For transient errors, the commit should be successful after the next set of
|
388
|
+
# polled records has been processed.
|
389
|
+
# But, it might also be worth thinking about adding a configurable retry mechanism
|
390
|
+
logger.error("Unable to commit records",
|
391
|
+
:kafka_error_message => e,
|
392
|
+
:cause => e.respond_to?(:getCause) ? e.getCause() : nil)
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
355
396
|
private
|
356
397
|
def create_consumer(client_id)
|
357
398
|
begin
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-integration-kafka'
|
3
|
-
s.version = '10.7.
|
3
|
+
s.version = '10.7.5'
|
4
4
|
s.licenses = ['Apache-2.0']
|
5
5
|
s.summary = "Integration with Kafka - input and output plugins"
|
6
6
|
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline "+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'logstash-integration-kafka_jars'
|
3
|
+
|
4
|
+
describe "[DOCS]" do
|
5
|
+
|
6
|
+
let(:docs_files) do
|
7
|
+
['index.asciidoc', 'input-kafka.asciidoc', 'output-kafka.asciidoc'].map { |name| File.join('docs', name) }
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:kafka_version_properties) do
|
11
|
+
loader = java.lang.Thread.currentThread.getContextClassLoader
|
12
|
+
version = loader.getResource('kafka/kafka-version.properties')
|
13
|
+
fail "kafka-version.properties missing" unless version
|
14
|
+
properties = java.util.Properties.new
|
15
|
+
properties.load version.openStream
|
16
|
+
properties
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'is sync-ed with Kafka client version' do
|
20
|
+
version = kafka_version_properties.get('version') # e.g. '2.5.1'
|
21
|
+
|
22
|
+
fails = docs_files.map do |file|
|
23
|
+
if line = File.readlines(file).find { |line| line.index(':kafka_client:') }
|
24
|
+
puts "found #{line.inspect} in #{file}" if $VERBOSE # e.g. ":kafka_client: 2.5\n"
|
25
|
+
if !version.start_with?(line.strip.split[1])
|
26
|
+
"documentation at #{file} is out of sync with kafka-clients version (#{version.inspect}), detected line: #{line.inspect}"
|
27
|
+
else
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
fail "\n" + fails.join("\n") if fails.flatten.any?
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -198,8 +198,9 @@ def consume_messages(config, queue: Queue.new, timeout:, event_count:)
|
|
198
198
|
wait(timeout).for { queue.length }.to eq(event_count) unless timeout.eql?(false)
|
199
199
|
block_given? ? yield(queue, kafka_input) : queue
|
200
200
|
ensure
|
201
|
+
kafka_input.do_stop
|
201
202
|
t.kill
|
202
|
-
t.join(
|
203
|
+
t.join(30)
|
203
204
|
end
|
204
205
|
end
|
205
206
|
|
@@ -44,7 +44,7 @@ describe "outputs/kafka", :integration => true do
|
|
44
44
|
end
|
45
45
|
|
46
46
|
context 'when outputting messages serialized as Byte Array' do
|
47
|
-
let(:test_topic) { '
|
47
|
+
let(:test_topic) { 'logstash_integration_topicbytearray' }
|
48
48
|
let(:num_events) { 3 }
|
49
49
|
|
50
50
|
before :each do
|
@@ -3,38 +3,178 @@ require "logstash/devutils/rspec/spec_helper"
|
|
3
3
|
require "logstash/inputs/kafka"
|
4
4
|
require "concurrent"
|
5
5
|
|
6
|
-
class MockConsumer
|
7
|
-
def initialize
|
8
|
-
@wake = Concurrent::AtomicBoolean.new(false)
|
9
|
-
end
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
7
|
+
describe LogStash::Inputs::Kafka do
|
8
|
+
let(:common_config) { { 'topics' => ['logstash'] } }
|
9
|
+
let(:config) { common_config }
|
10
|
+
let(:consumer_double) { double(:consumer) }
|
11
|
+
let(:needs_raise) { false }
|
12
|
+
let(:payload) {
|
13
|
+
10.times.map do
|
14
|
+
org.apache.kafka.clients.consumer.ConsumerRecord.new("logstash", 0, 0, "key", "value")
|
15
|
+
end
|
16
|
+
}
|
17
|
+
subject { LogStash::Inputs::Kafka.new(config) }
|
18
|
+
|
19
|
+
describe '#poll' do
|
20
|
+
before do
|
21
|
+
polled = false
|
22
|
+
allow(consumer_double).to receive(:poll) do
|
23
|
+
if polled
|
24
|
+
[]
|
25
|
+
else
|
26
|
+
polled = true
|
27
|
+
payload
|
28
|
+
end
|
20
29
|
end
|
21
30
|
end
|
31
|
+
|
32
|
+
it 'should poll' do
|
33
|
+
expect(consumer_double).to receive(:poll)
|
34
|
+
expect(subject.do_poll(consumer_double)).to eq(payload)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should return nil if Kafka Exception is encountered' do
|
38
|
+
expect(consumer_double).to receive(:poll).and_raise(org.apache.kafka.common.errors.TopicAuthorizationException.new(''))
|
39
|
+
expect(subject.do_poll(consumer_double)).to be_empty
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should not throw if Kafka Exception is encountered' do
|
43
|
+
expect(consumer_double).to receive(:poll).and_raise(org.apache.kafka.common.errors.TopicAuthorizationException.new(''))
|
44
|
+
expect{subject.do_poll(consumer_double)}.not_to raise_error
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should return no records if Assertion Error is encountered' do
|
48
|
+
expect(consumer_double).to receive(:poll).and_raise(java.lang.AssertionError.new(''))
|
49
|
+
expect{subject.do_poll(consumer_double)}.to raise_error(java.lang.AssertionError)
|
50
|
+
end
|
22
51
|
end
|
23
52
|
|
24
|
-
|
53
|
+
describe '#maybe_commit_offset' do
|
54
|
+
context 'with auto commit disabled' do
|
55
|
+
let(:config) { common_config.merge('enable_auto_commit' => false) }
|
56
|
+
|
57
|
+
it 'should call commit on the consumer' do
|
58
|
+
expect(consumer_double).to receive(:commitSync)
|
59
|
+
subject.maybe_commit_offset(consumer_double)
|
60
|
+
end
|
61
|
+
it 'should not throw if a Kafka Exception is encountered' do
|
62
|
+
expect(consumer_double).to receive(:commitSync).and_raise(org.apache.kafka.common.errors.TopicAuthorizationException.new(''))
|
63
|
+
expect{subject.maybe_commit_offset(consumer_double)}.not_to raise_error
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should throw if Assertion Error is encountered' do
|
67
|
+
expect(consumer_double).to receive(:commitSync).and_raise(java.lang.AssertionError.new(''))
|
68
|
+
expect{subject.maybe_commit_offset(consumer_double)}.to raise_error(java.lang.AssertionError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with auto commit enabled' do
|
73
|
+
let(:config) { common_config.merge('enable_auto_commit' => true) }
|
74
|
+
|
75
|
+
it 'should not call commit on the consumer' do
|
76
|
+
expect(consumer_double).not_to receive(:commitSync)
|
77
|
+
subject.maybe_commit_offset(consumer_double)
|
78
|
+
end
|
79
|
+
end
|
25
80
|
end
|
26
81
|
|
27
|
-
|
28
|
-
|
82
|
+
describe '#register' do
|
83
|
+
it "should register" do
|
84
|
+
expect { subject.register }.to_not raise_error
|
85
|
+
end
|
29
86
|
end
|
30
|
-
end
|
31
87
|
|
32
|
-
describe
|
33
|
-
|
34
|
-
|
88
|
+
describe '#running' do
|
89
|
+
let(:q) { Queue.new }
|
90
|
+
let(:config) { common_config.merge('client_id' => 'test') }
|
91
|
+
|
92
|
+
before do
|
93
|
+
expect(subject).to receive(:create_consumer).once.and_return(consumer_double)
|
94
|
+
allow(consumer_double).to receive(:wakeup)
|
95
|
+
allow(consumer_double).to receive(:close)
|
96
|
+
allow(consumer_double).to receive(:subscribe)
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when running' do
|
100
|
+
before do
|
101
|
+
polled = false
|
102
|
+
allow(consumer_double).to receive(:poll) do
|
103
|
+
if polled
|
104
|
+
[]
|
105
|
+
else
|
106
|
+
polled = true
|
107
|
+
payload
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
subject.register
|
112
|
+
t = Thread.new do
|
113
|
+
sleep(1)
|
114
|
+
subject.do_stop
|
115
|
+
end
|
116
|
+
subject.run(q)
|
117
|
+
t.join
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should process the correct number of events' do
|
121
|
+
expect(q.size).to eq(10)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should set the consumer thread name' do
|
125
|
+
expect(subject.instance_variable_get('@runner_threads').first.get_name).to eq("kafka-input-worker-test-0")
|
126
|
+
end
|
127
|
+
end
|
35
128
|
|
36
|
-
|
37
|
-
|
129
|
+
context 'when errors are encountered during poll' do
|
130
|
+
before do
|
131
|
+
raised, polled = false
|
132
|
+
allow(consumer_double).to receive(:poll) do
|
133
|
+
unless raised
|
134
|
+
raised = true
|
135
|
+
raise exception
|
136
|
+
end
|
137
|
+
if polled
|
138
|
+
[]
|
139
|
+
else
|
140
|
+
polled = true
|
141
|
+
payload
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
subject.register
|
146
|
+
t = Thread.new do
|
147
|
+
sleep 2
|
148
|
+
subject.do_stop
|
149
|
+
end
|
150
|
+
subject.run(q)
|
151
|
+
t.join
|
152
|
+
end
|
153
|
+
|
154
|
+
context "when a Kafka exception is raised" do
|
155
|
+
let(:exception) { org.apache.kafka.common.errors.TopicAuthorizationException.new('Invalid topic') }
|
156
|
+
|
157
|
+
it 'should poll successfully' do
|
158
|
+
expect(q.size).to eq(10)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "when a StandardError is raised" do
|
163
|
+
let(:exception) { StandardError.new('Standard Error') }
|
164
|
+
|
165
|
+
it 'should retry and poll successfully' do
|
166
|
+
expect(q.size).to eq(10)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "when a java error is raised" do
|
171
|
+
let(:exception) { java.lang.AssertionError.new('Fatal assertion') }
|
172
|
+
|
173
|
+
it "should not retry" do
|
174
|
+
expect(q.size).to eq(0)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
38
178
|
end
|
39
179
|
|
40
180
|
context "register parameter verification" do
|
@@ -8,6 +8,8 @@ describe "outputs/kafka" do
|
|
8
8
|
let (:event) { LogStash::Event.new({'message' => 'hello', 'topic_name' => 'my_topic', 'host' => '172.0.0.1',
|
9
9
|
'@timestamp' => LogStash::Timestamp.now}) }
|
10
10
|
|
11
|
+
let(:future) { double('kafka producer future') }
|
12
|
+
|
11
13
|
context 'when initializing' do
|
12
14
|
it "should register" do
|
13
15
|
output = LogStash::Plugin.lookup("output", "kafka").new(simple_kafka_config)
|
@@ -24,8 +26,8 @@ describe "outputs/kafka" do
|
|
24
26
|
|
25
27
|
context 'when outputting messages' do
|
26
28
|
it 'should send logstash event to kafka broker' do
|
27
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
28
|
-
|
29
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send).
|
30
|
+
with(an_instance_of(org.apache.kafka.clients.producer.ProducerRecord))
|
29
31
|
kafka = LogStash::Outputs::Kafka.new(simple_kafka_config)
|
30
32
|
kafka.register
|
31
33
|
kafka.multi_receive([event])
|
@@ -33,18 +35,18 @@ describe "outputs/kafka" do
|
|
33
35
|
|
34
36
|
it 'should support Event#sprintf placeholders in topic_id' do
|
35
37
|
topic_field = 'topic_name'
|
36
|
-
expect(org.apache.kafka.clients.producer.ProducerRecord).to receive(:new)
|
37
|
-
|
38
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
38
|
+
expect(org.apache.kafka.clients.producer.ProducerRecord).to receive(:new).
|
39
|
+
with("my_topic", event.to_s).and_call_original
|
40
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
39
41
|
kafka = LogStash::Outputs::Kafka.new({'topic_id' => "%{#{topic_field}}"})
|
40
42
|
kafka.register
|
41
43
|
kafka.multi_receive([event])
|
42
44
|
end
|
43
45
|
|
44
46
|
it 'should support field referenced message_keys' do
|
45
|
-
expect(org.apache.kafka.clients.producer.ProducerRecord).to receive(:new)
|
46
|
-
|
47
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
47
|
+
expect(org.apache.kafka.clients.producer.ProducerRecord).to receive(:new).
|
48
|
+
with("test", "172.0.0.1", event.to_s).and_call_original
|
49
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
48
50
|
kafka = LogStash::Outputs::Kafka.new(simple_kafka_config.merge({"message_key" => "%{host}"}))
|
49
51
|
kafka.register
|
50
52
|
kafka.multi_receive([event])
|
@@ -71,22 +73,24 @@ describe "outputs/kafka" do
|
|
71
73
|
before do
|
72
74
|
count = 0
|
73
75
|
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
74
|
-
.exactly(sendcount).times
|
75
|
-
.and_wrap_original do |m, *args|
|
76
|
+
.exactly(sendcount).times do
|
76
77
|
if count < failcount # fail 'failcount' times in a row.
|
77
78
|
count += 1
|
78
79
|
# Pick an exception at random
|
79
80
|
raise exception_classes.shuffle.first.new("injected exception for testing")
|
80
81
|
else
|
81
|
-
|
82
|
+
count = :done
|
83
|
+
future # return future
|
82
84
|
end
|
83
85
|
end
|
86
|
+
expect(future).to receive :get
|
84
87
|
end
|
85
88
|
|
86
89
|
it "should retry until successful" do
|
87
90
|
kafka = LogStash::Outputs::Kafka.new(simple_kafka_config)
|
88
91
|
kafka.register
|
89
92
|
kafka.multi_receive([event])
|
93
|
+
sleep(1.0) # allow for future.get call
|
90
94
|
end
|
91
95
|
end
|
92
96
|
|
@@ -101,15 +105,13 @@ describe "outputs/kafka" do
|
|
101
105
|
|
102
106
|
before do
|
103
107
|
count = 0
|
104
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
105
|
-
.exactly(1).times
|
106
|
-
.and_wrap_original do |m, *args|
|
108
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send).exactly(1).times do
|
107
109
|
if count < failcount # fail 'failcount' times in a row.
|
108
110
|
count += 1
|
109
111
|
# Pick an exception at random
|
110
112
|
raise exception_classes.shuffle.first.new("injected exception for testing")
|
111
113
|
else
|
112
|
-
|
114
|
+
fail 'unexpected producer#send invocation'
|
113
115
|
end
|
114
116
|
end
|
115
117
|
end
|
@@ -131,25 +133,24 @@ describe "outputs/kafka" do
|
|
131
133
|
|
132
134
|
it "should retry until successful" do
|
133
135
|
count = 0
|
134
|
-
|
135
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
136
|
-
.exactly(sendcount).times
|
137
|
-
.and_wrap_original do |m, *args|
|
136
|
+
success = nil
|
137
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send).exactly(sendcount).times do
|
138
138
|
if count < failcount
|
139
139
|
count += 1
|
140
140
|
# inject some failures.
|
141
141
|
|
142
142
|
# Return a custom Future that will raise an exception to simulate a Kafka send() problem.
|
143
143
|
future = java.util.concurrent.FutureTask.new { raise org.apache.kafka.common.errors.TimeoutException.new("Failed") }
|
144
|
-
future.run
|
145
|
-
future
|
146
144
|
else
|
147
|
-
|
145
|
+
success = true
|
146
|
+
future = java.util.concurrent.FutureTask.new { nil } # return no-op future
|
148
147
|
end
|
148
|
+
future.tap { Thread.start { future.run } }
|
149
149
|
end
|
150
150
|
kafka = LogStash::Outputs::Kafka.new(simple_kafka_config)
|
151
151
|
kafka.register
|
152
152
|
kafka.multi_receive([event])
|
153
|
+
expect( success ).to be true
|
153
154
|
end
|
154
155
|
end
|
155
156
|
|
@@ -158,9 +159,7 @@ describe "outputs/kafka" do
|
|
158
159
|
let(:max_sends) { 1 }
|
159
160
|
|
160
161
|
it "should should only send once" do
|
161
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
162
|
-
.once
|
163
|
-
.and_wrap_original do |m, *args|
|
162
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send).once do
|
164
163
|
# Always fail.
|
165
164
|
future = java.util.concurrent.FutureTask.new { raise org.apache.kafka.common.errors.TimeoutException.new("Failed") }
|
166
165
|
future.run
|
@@ -172,9 +171,7 @@ describe "outputs/kafka" do
|
|
172
171
|
end
|
173
172
|
|
174
173
|
it 'should not sleep' do
|
175
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
176
|
-
.once
|
177
|
-
.and_wrap_original do |m, *args|
|
174
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send).once do
|
178
175
|
# Always fail.
|
179
176
|
future = java.util.concurrent.FutureTask.new { raise org.apache.kafka.common.errors.TimeoutException.new("Failed") }
|
180
177
|
future.run
|
@@ -193,13 +190,10 @@ describe "outputs/kafka" do
|
|
193
190
|
let(:max_sends) { retries + 1 }
|
194
191
|
|
195
192
|
it "should give up after retries are exhausted" do
|
196
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
197
|
-
.at_most(max_sends).times
|
198
|
-
.and_wrap_original do |m, *args|
|
193
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send).at_most(max_sends).times do
|
199
194
|
# Always fail.
|
200
195
|
future = java.util.concurrent.FutureTask.new { raise org.apache.kafka.common.errors.TimeoutException.new("Failed") }
|
201
|
-
future.run
|
202
|
-
future
|
196
|
+
future.tap { Thread.start { future.run } }
|
203
197
|
end
|
204
198
|
kafka = LogStash::Outputs::Kafka.new(simple_kafka_config.merge("retries" => retries))
|
205
199
|
kafka.register
|
@@ -207,9 +201,7 @@ describe "outputs/kafka" do
|
|
207
201
|
end
|
208
202
|
|
209
203
|
it 'should only sleep retries number of times' do
|
210
|
-
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send)
|
211
|
-
.at_most(max_sends).times
|
212
|
-
.and_wrap_original do |m, *args|
|
204
|
+
expect_any_instance_of(org.apache.kafka.clients.producer.KafkaProducer).to receive(:send).at_most(max_sends).times do
|
213
205
|
# Always fail.
|
214
206
|
future = java.util.concurrent.FutureTask.new { raise org.apache.kafka.common.errors.TimeoutException.new("Failed") }
|
215
207
|
future.run
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-integration-kafka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 10.7.
|
4
|
+
version: 10.7.5
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -220,6 +220,7 @@ files:
|
|
220
220
|
- lib/logstash/plugin_mixins/common.rb
|
221
221
|
- lib/logstash/plugin_mixins/kafka_support.rb
|
222
222
|
- logstash-integration-kafka.gemspec
|
223
|
+
- spec/check_docs_spec.rb
|
223
224
|
- spec/fixtures/trust-store_stub.jks
|
224
225
|
- spec/integration/inputs/kafka_spec.rb
|
225
226
|
- spec/integration/outputs/kafka_spec.rb
|
@@ -269,6 +270,7 @@ signing_key:
|
|
269
270
|
specification_version: 4
|
270
271
|
summary: Integration with Kafka - input and output plugins
|
271
272
|
test_files:
|
273
|
+
- spec/check_docs_spec.rb
|
272
274
|
- spec/fixtures/trust-store_stub.jks
|
273
275
|
- spec/integration/inputs/kafka_spec.rb
|
274
276
|
- spec/integration/outputs/kafka_spec.rb
|