logstash-codec-multiline 3.1.0 → 3.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4ecbf8730e688adbacb1af57c98f7c5105b5677337b140a22b6ccb41e1468ea
4
- data.tar.gz: 299167720ec0457af53b7f118b6e55bd6c078f6ac1b8e4978ca68872a04cbc7c
3
+ metadata.gz: 3e46243c32a2f87ede3a266ee117dd89086a5a48454813d8979fe04a25454692
4
+ data.tar.gz: fedc22e883fd7e842a4de3f8cf599268c57f7b1d7de06a40a5d5fb27cd86febb
5
5
  SHA512:
6
- metadata.gz: 798b7fe3f963ade7578c655a00ba2ba7ea602dced4e463b170d8d7bf8b9e0585bedda761f9ee5bbbcb8026d99e0cbc3dc22bdfc19abe063c5d3716c2b8e0fd03
7
- data.tar.gz: b4758f3f75e0ad1e470800aff9ccf353f8f4493cbcd6b30f687ef3fa74973e5baf0aba65a25450b83b3269879065ab5878141914dd3c35998e0bad4ca631b8bd
6
+ metadata.gz: faa8892c245b03438c1cd63f5812b8e5c80c145b55fd437e8eb3ab82baf4bb94f23754d720c40016fc8bfecf23a6eae7cbb1c1ab4e0a34f5a5a4e37efe1d8fec
7
+ data.tar.gz: 415515b5dc715d12df012febfc9a91cf986d8c05fd73f975bc284807bbd034e436168a6d624299bc53aea5417bae4d391f10b97b32a07b0b43be7ac197de8a7a
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 3.1.2
2
+ - Fix: periodic runner fails to stop and prevent pipeline shutdown [#72](https://github.com/logstash-plugins/logstash-codec-multiline/pull/72)
3
+
4
+ ## 3.1.1
5
+ - Fix: avoid reusing per-identity codec instances for differing identities. Removes a very minor optimization so that stateful codecs like CSV can work reliably [#70](https://github.com/logstash-plugins/logstash-codec-multiline/pull/70)
6
+
1
7
  ## 3.1.0
2
8
  - Feat: ECS compatibility [#69](https://github.com/logstash-plugins/logstash-codec-multiline/pull/69)
3
9
 
@@ -42,15 +42,20 @@ module LogStash module Codecs class IdentityMapCodec
42
42
  def initialize(listener, interval, method_symbol)
43
43
  @listener, @interval = listener, interval
44
44
  @method_symbol = method_symbol
45
- @running = Concurrent::AtomicBoolean.new(false)
45
+ @running = Concurrent::AtomicBoolean.new(true)
46
46
  end
47
47
 
48
+ # The goal of periodic runner is to have a single thread to do clean up / auto flush
49
+ # This method is expected to access by a single thread,
50
+ # otherwise, multiple Thread.start could create more than one periodic runner
48
51
  def start
49
- return self if running?
50
- @running.make_true
52
+ return self unless running?
53
+ return self if running? && !@thread.nil?
54
+
51
55
  @thread = Thread.start do
52
56
  class_name = @listener.class.name.split('::').last # IdentityMapCodec
53
57
  LogStash::Util.set_thread_name("#{class_name}##{@method_symbol}")
58
+ @listener.logger.debug("Start periodic runner")
54
59
 
55
60
  while running? do
56
61
  sleep @interval
@@ -67,9 +72,11 @@ module LogStash module Codecs class IdentityMapCodec
67
72
  end
68
73
 
69
74
  def stop
70
- return if !running?
75
+ return unless running?
76
+
77
+ @listener.logger.debug("Stop periodic runner")
71
78
  @running.make_false
72
- while @thread.alive?
79
+ while @thread&.alive?
73
80
  begin
74
81
  @thread.wakeup
75
82
  rescue ThreadError
@@ -290,6 +297,7 @@ module LogStash module Codecs class IdentityMapCodec
290
297
  end
291
298
 
292
299
  def codec_without_usage_update(identity)
300
+ return base_codec if identity.nil? # mirror optimization in `stream_codec`
293
301
  find_codec_value(identity).codec
294
302
  end
295
303
 
@@ -312,7 +320,7 @@ module LogStash module Codecs class IdentityMapCodec
312
320
  def record_codec_usage(identity)
313
321
  check_map_limits
314
322
  # only start the cleaner if streams are in use
315
- # continuous calls to start are OK
323
+ # continuous calls to start are OK if this codec method is accessed by a single thread
316
324
  cleaner.start
317
325
  auto_flusher.start
318
326
  compo = find_codec_value(identity)
@@ -336,7 +344,7 @@ module LogStash module Codecs class IdentityMapCodec
336
344
  end
337
345
 
338
346
  def codec_builder(hash, k)
339
- codec = hash.empty? ? @base_codec : @base_codec.clone
347
+ codec = @base_codec.clone
340
348
  codec.use_mapper_auto_flush if using_mapped_auto_flush?
341
349
  compo = CodecValue.new(codec).tap do |o|
342
350
  now = Time.now
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-codec-multiline'
4
- s.version = '3.1.0'
4
+ s.version = '3.1.2'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Merges multiline messages into a single event"
7
7
  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"
@@ -29,7 +29,8 @@ describe LogStash::Codecs::IdentityMapCodec do
29
29
  let(:stream1) { nil }
30
30
 
31
31
  it "transparently refers to the original codec" do
32
- expect(codec).to eql(codec1)
32
+ resolved_codec = demuxer.codec_without_usage_update(stream1)
33
+ expect(resolved_codec).to eq(codec)
33
34
  end
34
35
  end
35
36
 
@@ -37,8 +38,8 @@ describe LogStash::Codecs::IdentityMapCodec do
37
38
 
38
39
  before { demuxer.decode(arg1, stream1) }
39
40
 
40
- it "the first identity refers to the original codec" do
41
- expect(codec).to eql(codec1)
41
+ it "the first identity refers to a copy of original codec" do
42
+ expect(codec1).to_not eql(codec)
42
43
  end
43
44
  end
44
45
 
@@ -212,8 +213,11 @@ describe LogStash::Codecs::IdentityMapCodec do
212
213
  let(:demuxer) { described_class.new(codec).evict_timeout(1).cleaner_interval(1) }
213
214
  it "the cleaner evicts the codec and flushes it first" do
214
215
  demuxer.decode(Object.new, "stream1"){|*| 42}
216
+ mapped_codec = demuxer.codec_without_usage_update("stream1") # get before eviction
217
+
215
218
  sleep(2.1)
216
- expect(codec.trace_for(:flush)).to eq(42)
219
+
220
+ expect(mapped_codec.trace_for(:flush)).to eq(42)
217
221
  expect(demuxer.identity_map.keys).not_to include("stream1")
218
222
  end
219
223
  end
@@ -227,9 +231,12 @@ describe LogStash::Codecs::IdentityMapCodec do
227
231
  end
228
232
  it "the cleaner evicts the codec and flushes it first using the eviction_block" do
229
233
  demuxer.decode(Object.new, "stream1"){|*| 42}
234
+ mapped_codec = demuxer.codec_without_usage_update("stream1") # get before eviction
235
+
230
236
  sleep(2.1)
231
- expect(codec.trace_for(:flush)).to eq(24)
232
- expect(demuxer.identity_map.keys).not_to include("stream1")
237
+
238
+ expect(mapped_codec.trace_for(:flush)).to eq(24)
239
+ expect(demuxer.identity_map.keys).to_not include("stream1")
233
240
  end
234
241
  end
235
242
  end
@@ -251,7 +258,9 @@ describe LogStash::Codecs::IdentityMapCodec do
251
258
  it "no events are generated (the line is buffered)" do
252
259
  expect(imc.identity_count).to eq(1)
253
260
  expect(queue.size).to eq(0)
254
- expect(mlc.internal_buffer[0]).to eq("foo")
261
+
262
+ mapped_codec = imc.codec_without_usage_update(identity)
263
+ expect(mapped_codec.internal_buffer[0]).to eq("foo")
255
264
  end
256
265
  end
257
266
 
@@ -290,3 +299,79 @@ describe LogStash::Codecs::IdentityMapCodec do
290
299
  end
291
300
  end
292
301
  end
302
+
303
+ describe LogStash::Codecs::IdentityMapCodec::PeriodicRunner do
304
+ let(:listener) do
305
+ double('Listener', logger: double('Logger').as_null_object,
306
+ some_method: double('method_symbol').as_null_object)
307
+ end
308
+ let(:interval) { 1 }
309
+ let(:method_symbol) { :some_method }
310
+ subject(:runner) { described_class.new(listener, interval, method_symbol) }
311
+
312
+ before(:all) do
313
+ Thread.abort_on_exception = true
314
+ end
315
+
316
+ describe "normal shutdown" do
317
+ it "starts first and stops successfully" do
318
+ start_thread = Thread.start do
319
+ runner.start
320
+ end
321
+
322
+ stop_thread = Thread.start do
323
+ sleep(1)
324
+ runner.stop
325
+ end
326
+
327
+ start_thread.join
328
+ stop_thread.join
329
+
330
+ expect(runner.instance_variable_get('@listener')).to be_nil
331
+ end
332
+ end
333
+
334
+ describe "race condition" do
335
+ let(:atomic_bool) { Concurrent::AtomicBoolean.new(true) }
336
+ let(:running) { double('AtomicBooleanStub') }
337
+
338
+ before do
339
+ allow(running).to receive(:make_true) do
340
+ atomic_bool.make_true
341
+ end
342
+
343
+ allow(running).to receive(:make_false) do
344
+ atomic_bool.make_false
345
+ sleep(2)
346
+ end
347
+
348
+ allow(running).to receive(:true?) do
349
+ atomic_bool.true?
350
+ end
351
+ end
352
+
353
+ before :each do
354
+ runner.instance_variable_set('@running', running)
355
+ end
356
+
357
+ it "starts several times and stops successfully" do
358
+ start_thread = Thread.start do
359
+ 5.times do
360
+ runner.start
361
+ sleep(1)
362
+ end
363
+ end
364
+
365
+ stop_thread = Thread.start do
366
+ sleep(1) # give time to runner start
367
+ runner.stop
368
+ end
369
+
370
+ start_thread.join
371
+ stop_thread.join
372
+
373
+ expect(runner.instance_variable_get('@listener')).to be_nil
374
+ expect(runner.instance_variable_get('@thread').alive?).to be_falsey
375
+ end
376
+ end
377
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-codec-multiline
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-27 00:00:00.000000000 Z
11
+ date: 2024-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -20,8 +20,8 @@ dependencies:
20
20
  - !ruby/object:Gem::Version
21
21
  version: '2.99'
22
22
  name: logstash-core-plugin-api
23
- type: :runtime
24
23
  prerelease: false
24
+ type: :runtime
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
@@ -37,8 +37,8 @@ dependencies:
37
37
  - !ruby/object:Gem::Version
38
38
  version: '1.3'
39
39
  name: logstash-mixin-ecs_compatibility_support
40
- type: :runtime
41
40
  prerelease: false
41
+ type: :runtime
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
@@ -51,8 +51,8 @@ dependencies:
51
51
  - !ruby/object:Gem::Version
52
52
  version: '1.0'
53
53
  name: logstash-mixin-event_support
54
- type: :runtime
55
54
  prerelease: false
55
+ type: :runtime
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
@@ -65,8 +65,8 @@ dependencies:
65
65
  - !ruby/object:Gem::Version
66
66
  version: '0'
67
67
  name: logstash-patterns-core
68
- type: :runtime
69
68
  prerelease: false
69
+ type: :runtime
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
@@ -79,8 +79,8 @@ dependencies:
79
79
  - !ruby/object:Gem::Version
80
80
  version: 0.11.1
81
81
  name: jls-grok
82
- type: :runtime
83
82
  prerelease: false
83
+ type: :runtime
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
@@ -93,8 +93,8 @@ dependencies:
93
93
  - !ruby/object:Gem::Version
94
94
  version: '0'
95
95
  name: concurrent-ruby
96
- type: :runtime
97
96
  prerelease: false
97
+ type: :runtime
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - ">="
@@ -107,8 +107,8 @@ dependencies:
107
107
  - !ruby/object:Gem::Version
108
108
  version: '0'
109
109
  name: logstash-devutils
110
- type: :development
111
110
  prerelease: false
111
+ type: :development
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - ">="
@@ -121,8 +121,8 @@ dependencies:
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
123
  name: insist
124
- type: :development
125
124
  prerelease: false
125
+ type: :development
126
126
  version_requirements: !ruby/object:Gem::Requirement
127
127
  requirements:
128
128
  - - ">="
@@ -173,7 +173,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
173
  - !ruby/object:Gem::Version
174
174
  version: '0'
175
175
  requirements: []
176
- rubygems_version: 3.0.6
176
+ rubygems_version: 3.2.33
177
177
  signing_key:
178
178
  specification_version: 4
179
179
  summary: Merges multiline messages into a single event