logstash-output-kusto 2.1.5-java → 2.2.0-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 +8 -0
- data/README.md +42 -2
- data/lib/logstash/outputs/kusto/ingestor.rb +314 -13
- data/lib/logstash/outputs/kusto/streaming_chunker.rb +42 -0
- data/lib/logstash/outputs/kusto.rb +373 -41
- data/spec/outputs/kusto/ingestor_spec.rb +480 -0
- data/spec/outputs/kusto/streaming_chunker_spec.rb +57 -0
- data/spec/outputs/kusto_spec.rb +391 -0
- data/version +1 -1
- metadata +6 -3
- /data/vendor/jar-dependencies/org/logstash/outputs/logstash-output-kusto/{2.1.5/logstash-output-kusto-2.1.5.jar → 2.2.0/logstash-output-kusto-2.2.0.jar} +0 -0
data/spec/outputs/kusto_spec.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
require 'logstash/outputs/kusto'
|
|
3
3
|
require 'logstash/codecs/plain'
|
|
4
4
|
require 'logstash/event'
|
|
5
|
+
require 'tmpdir'
|
|
5
6
|
|
|
6
7
|
describe LogStash::Outputs::Kusto do
|
|
7
8
|
|
|
@@ -19,6 +20,17 @@ describe LogStash::Outputs::Kusto do
|
|
|
19
20
|
} }
|
|
20
21
|
|
|
21
22
|
describe '#register' do
|
|
23
|
+
it 'defaults ingestion mode to queued' do
|
|
24
|
+
kusto = described_class.new(options)
|
|
25
|
+
|
|
26
|
+
expect(kusto.ingestion_mode).to eq('queued')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'requires the explicit streaming mode name' do
|
|
30
|
+
expect do
|
|
31
|
+
described_class.new(options.merge('ingestion_mode' => 'managed_streaming'))
|
|
32
|
+
end.to raise_error(LogStash::ConfigurationError)
|
|
33
|
+
end
|
|
22
34
|
|
|
23
35
|
it 'doesnt allow the path to start with a dynamic string' do
|
|
24
36
|
kusto = described_class.new(options.merge( {'path' => '/%{name}'} ))
|
|
@@ -51,6 +63,385 @@ describe LogStash::Outputs::Kusto do
|
|
|
51
63
|
kusto.close
|
|
52
64
|
end
|
|
53
65
|
|
|
66
|
+
it 'requires path for queued ingestion' do
|
|
67
|
+
queued_options = options.reject { |key, _| key == 'path' }
|
|
68
|
+
kusto = described_class.new(queued_options)
|
|
69
|
+
|
|
70
|
+
expect { kusto.register }.to raise_error(LogStash::ConfigurationError, /path/)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'does not require path for streaming ingestion' do
|
|
74
|
+
Dir.mktmpdir('kusto-streaming') do |directory|
|
|
75
|
+
streaming_options = options
|
|
76
|
+
.reject { |key, _| key == 'path' }
|
|
77
|
+
.merge(
|
|
78
|
+
'ingestion_mode' => 'streaming',
|
|
79
|
+
'streaming_temp_directory' => directory
|
|
80
|
+
)
|
|
81
|
+
ingestor = instance_double(LogStash::Outputs::Kusto::Ingestor, stop: nil)
|
|
82
|
+
allow(LogStash::Outputs::Kusto::Ingestor).to receive(:new).and_return(ingestor)
|
|
83
|
+
kusto = described_class.new(streaming_options)
|
|
84
|
+
|
|
85
|
+
expect { kusto.register }.not_to raise_error
|
|
86
|
+
kusto.close
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it 'uses a private destination spool below path.data by default' do
|
|
91
|
+
Dir.mktmpdir('kusto-path-data') do |path_data|
|
|
92
|
+
allow(LogStash::SETTINGS).to receive(:get).and_call_original
|
|
93
|
+
allow(LogStash::SETTINGS).to receive(:get).with('path.data').and_return(path_data)
|
|
94
|
+
ingestor = instance_double(LogStash::Outputs::Kusto::Ingestor, stop: nil)
|
|
95
|
+
allow(LogStash::Outputs::Kusto::Ingestor).to receive(:new).and_return(ingestor)
|
|
96
|
+
kusto = described_class.new(
|
|
97
|
+
options.reject { |key, _| key == 'path' }.merge(
|
|
98
|
+
'ingestion_mode' => 'streaming'
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
kusto.register
|
|
103
|
+
|
|
104
|
+
spool = kusto.instance_variable_get(:@streaming_temp_directory)
|
|
105
|
+
expect(spool).to start_with(File.realpath(path_data))
|
|
106
|
+
expect(File.stat(spool).mode & 0o777).to eq(0o700)
|
|
107
|
+
kusto.close
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'rejects unsafe streaming file and directory modes' do
|
|
112
|
+
{
|
|
113
|
+
'dir_mode' => 0o777,
|
|
114
|
+
'file_mode' => 0o666
|
|
115
|
+
}.each do |setting, mode|
|
|
116
|
+
Dir.mktmpdir('kusto-unsafe-mode') do |directory|
|
|
117
|
+
kusto = described_class.new(options.merge(
|
|
118
|
+
'ingestion_mode' => 'streaming',
|
|
119
|
+
'streaming_temp_directory' => directory,
|
|
120
|
+
setting => mode
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
expect { kusto.register }.to raise_error(LogStash::ConfigurationError, /must not allow/)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
it 'rejects a symlink as the streaming spool directory' do
|
|
129
|
+
skip 'Windows symlink creation requires elevated privileges' if Gem.win_platform?
|
|
130
|
+
|
|
131
|
+
Dir.mktmpdir('kusto-streaming-symlink') do |directory|
|
|
132
|
+
target = File.join(directory, 'target')
|
|
133
|
+
link = File.join(directory, 'link')
|
|
134
|
+
Dir.mkdir(target)
|
|
135
|
+
File.symlink(target, link)
|
|
136
|
+
kusto = described_class.new(options.merge(
|
|
137
|
+
'ingestion_mode' => 'streaming',
|
|
138
|
+
'streaming_temp_directory' => link
|
|
139
|
+
))
|
|
140
|
+
|
|
141
|
+
expect { kusto.register }.to raise_error(LogStash::ConfigurationError, /must not be a symlink/)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'rejects a spool below a non-sticky world-writable parent' do
|
|
146
|
+
skip 'POSIX permission validation is not available on Windows' if Gem.win_platform?
|
|
147
|
+
|
|
148
|
+
Dir.mktmpdir('kusto-streaming-parent') do |directory|
|
|
149
|
+
unsafe_parent = File.join(directory, 'unsafe')
|
|
150
|
+
Dir.mkdir(unsafe_parent, 0o777)
|
|
151
|
+
File.chmod(0o777, unsafe_parent)
|
|
152
|
+
spool = File.join(unsafe_parent, 'spool')
|
|
153
|
+
kusto = described_class.new(options.merge(
|
|
154
|
+
'ingestion_mode' => 'streaming',
|
|
155
|
+
'streaming_temp_directory' => spool
|
|
156
|
+
))
|
|
157
|
+
|
|
158
|
+
expect { kusto.register }.to raise_error(
|
|
159
|
+
LogStash::ConfigurationError,
|
|
160
|
+
/writable by untrusted users/
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'skips unsupported directory fsync on Windows' do
|
|
166
|
+
output = described_class.allocate
|
|
167
|
+
allow(Gem).to receive(:win_platform?).and_return(true)
|
|
168
|
+
expect(File).not_to receive(:open)
|
|
169
|
+
|
|
170
|
+
expect { output.send(:fsync_directory, 'C:/spool') }.not_to raise_error
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'recovers streaming spool files after restart' do
|
|
174
|
+
Dir.mktmpdir('kusto-streaming-recovery') do |directory|
|
|
175
|
+
batch_directory = File.join(directory, 'batch-existing.ready')
|
|
176
|
+
FileUtils.mkdir_p(batch_directory)
|
|
177
|
+
recovered_file = File.join(
|
|
178
|
+
batch_directory,
|
|
179
|
+
'stream-000000-2a11f3ee-9dd7-42ae-99bc-89046a8b8d65.json'
|
|
180
|
+
)
|
|
181
|
+
File.write(recovered_file, "{\"id\":1}\n")
|
|
182
|
+
ingestor = instance_double(
|
|
183
|
+
LogStash::Outputs::Kusto::Ingestor,
|
|
184
|
+
upload_async: nil,
|
|
185
|
+
stop: nil
|
|
186
|
+
)
|
|
187
|
+
allow(LogStash::Outputs::Kusto::Ingestor).to receive(:new).and_return(ingestor)
|
|
188
|
+
kusto = described_class.new(options.merge(
|
|
189
|
+
'ingestion_mode' => 'streaming',
|
|
190
|
+
'streaming_temp_directory' => directory
|
|
191
|
+
))
|
|
192
|
+
|
|
193
|
+
expect(ingestor).to receive(:upload_async).with(File.realpath(recovered_file), true)
|
|
194
|
+
kusto.register
|
|
195
|
+
kusto.close
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
it 'discards incomplete streaming batches during recovery' do
|
|
200
|
+
Dir.mktmpdir('kusto-streaming-recovery') do |directory|
|
|
201
|
+
incomplete_directory = File.join(directory, '.batch-incomplete.tmp')
|
|
202
|
+
FileUtils.mkdir_p(incomplete_directory)
|
|
203
|
+
File.write(File.join(incomplete_directory, 'stream-000000.json'), "{\"id\":1}\n")
|
|
204
|
+
ingestor = instance_double(
|
|
205
|
+
LogStash::Outputs::Kusto::Ingestor,
|
|
206
|
+
upload_async: nil,
|
|
207
|
+
stop: nil
|
|
208
|
+
)
|
|
209
|
+
allow(LogStash::Outputs::Kusto::Ingestor).to receive(:new).and_return(ingestor)
|
|
210
|
+
kusto = described_class.new(options.merge(
|
|
211
|
+
'ingestion_mode' => 'streaming',
|
|
212
|
+
'streaming_temp_directory' => directory
|
|
213
|
+
))
|
|
214
|
+
|
|
215
|
+
kusto.register
|
|
216
|
+
|
|
217
|
+
expect(ingestor).not_to have_received(:upload_async)
|
|
218
|
+
expect(File.exist?(incomplete_directory)).to be(false)
|
|
219
|
+
kusto.close
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
it 'prevents multiple outputs from sharing one streaming spool' do
|
|
224
|
+
Dir.mktmpdir('kusto-streaming-lock') do |directory|
|
|
225
|
+
ingestor = instance_double(LogStash::Outputs::Kusto::Ingestor, stop: nil)
|
|
226
|
+
allow(LogStash::Outputs::Kusto::Ingestor).to receive(:new).and_return(ingestor)
|
|
227
|
+
first = described_class.new(options.merge(
|
|
228
|
+
'ingestion_mode' => 'streaming',
|
|
229
|
+
'streaming_temp_directory' => directory
|
|
230
|
+
))
|
|
231
|
+
second = described_class.new(options.merge(
|
|
232
|
+
'ingestion_mode' => 'streaming',
|
|
233
|
+
'streaming_temp_directory' => directory
|
|
234
|
+
))
|
|
235
|
+
|
|
236
|
+
first.register
|
|
237
|
+
expect { second.register }.to raise_error(LogStash::ConfigurationError, /already in use/)
|
|
238
|
+
first.close
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it 'rejects unsafe files during streaming recovery' do
|
|
243
|
+
skip 'Windows symlink creation requires elevated privileges' if Gem.win_platform?
|
|
244
|
+
|
|
245
|
+
Dir.mktmpdir('kusto-streaming-recovery') do |directory|
|
|
246
|
+
batch_directory = File.join(directory, 'batch-existing.ready')
|
|
247
|
+
FileUtils.mkdir_p(batch_directory)
|
|
248
|
+
outside_file = File.join(directory, 'outside.json')
|
|
249
|
+
File.write(outside_file, "{\"id\":\"forged\"}\n")
|
|
250
|
+
recovered_file = File.join(
|
|
251
|
+
batch_directory,
|
|
252
|
+
'stream-000000-2a11f3ee-9dd7-42ae-99bc-89046a8b8d65.json'
|
|
253
|
+
)
|
|
254
|
+
File.symlink(outside_file, recovered_file)
|
|
255
|
+
ingestor = instance_double(LogStash::Outputs::Kusto::Ingestor, stop: nil)
|
|
256
|
+
allow(LogStash::Outputs::Kusto::Ingestor).to receive(:new).and_return(ingestor)
|
|
257
|
+
kusto = described_class.new(options.merge(
|
|
258
|
+
'ingestion_mode' => 'streaming',
|
|
259
|
+
'streaming_temp_directory' => directory
|
|
260
|
+
))
|
|
261
|
+
|
|
262
|
+
expect { kusto.register }.to raise_error(LogStash::ConfigurationError, /unsafe spool file/)
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
it 'rejects a non-positive streaming request size' do
|
|
267
|
+
kusto = described_class.new(options.merge(
|
|
268
|
+
'ingestion_mode' => 'streaming',
|
|
269
|
+
'streaming_max_request_bytes' => 0
|
|
270
|
+
))
|
|
271
|
+
|
|
272
|
+
expect { kusto.register }.to raise_error(LogStash::ConfigurationError, /streaming_max_request_bytes/)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
{
|
|
276
|
+
'streaming_max_retry_attempts' => -1,
|
|
277
|
+
'streaming_retry_backoff_seconds' => 0,
|
|
278
|
+
'streaming_concurrent_requests' => 0
|
|
279
|
+
}.each do |setting, value|
|
|
280
|
+
it "rejects invalid #{setting}" do
|
|
281
|
+
kusto = described_class.new(options.merge(
|
|
282
|
+
'ingestion_mode' => 'streaming',
|
|
283
|
+
setting => value
|
|
284
|
+
))
|
|
285
|
+
|
|
286
|
+
expect { kusto.register }.to raise_error(LogStash::ConfigurationError, /#{setting}/)
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
describe '#multi_receive_encoded with streaming' do
|
|
293
|
+
let(:streaming_temp_directory) { Dir.mktmpdir('kusto-streaming-spec') }
|
|
294
|
+
let(:streaming_options) do
|
|
295
|
+
options.reject { |key, _| key == 'path' }.merge(
|
|
296
|
+
'ingestion_mode' => 'streaming',
|
|
297
|
+
'streaming_max_request_bytes' => 10,
|
|
298
|
+
'streaming_temp_directory' => streaming_temp_directory,
|
|
299
|
+
'delete_temp_files' => false
|
|
300
|
+
)
|
|
301
|
+
end
|
|
302
|
+
let(:ingestor) do
|
|
303
|
+
instance_double(LogStash::Outputs::Kusto::Ingestor, upload_async: nil, stop: nil)
|
|
304
|
+
end
|
|
305
|
+
let(:kusto) { described_class.new(streaming_options) }
|
|
306
|
+
|
|
307
|
+
before do
|
|
308
|
+
@uploads = []
|
|
309
|
+
@uploads_mutex = Mutex.new
|
|
310
|
+
allow(ingestor).to receive(:upload_async) do |path, delete_on_success|
|
|
311
|
+
@uploads_mutex.synchronize do
|
|
312
|
+
@uploads << {
|
|
313
|
+
path: path,
|
|
314
|
+
payload: File.binread(path),
|
|
315
|
+
delete_on_success: delete_on_success
|
|
316
|
+
}
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
allow(LogStash::Outputs::Kusto::Ingestor).to receive(:new).and_return(ingestor)
|
|
320
|
+
kusto.register
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
after do
|
|
324
|
+
kusto.close
|
|
325
|
+
FileUtils.rm_rf(streaming_temp_directory)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it 'writes byte-bounded spool files and enqueues them in order' do
|
|
329
|
+
events_and_encoded = [
|
|
330
|
+
[LogStash::Event.new('id' => 1), "123456\n"],
|
|
331
|
+
[LogStash::Event.new('id' => 2), "789\n"],
|
|
332
|
+
[LogStash::Event.new('id' => 3), "abcde\n"]
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
kusto.multi_receive_encoded(events_and_encoded)
|
|
336
|
+
|
|
337
|
+
expect(@uploads.map { |upload| upload[:payload] })
|
|
338
|
+
.to eq(["123456\n", "789\nabcde\n"])
|
|
339
|
+
expect(@uploads).to all(include(delete_on_success: false))
|
|
340
|
+
expect(@uploads.map { |upload| File.basename(File.dirname(upload[:path])) }.uniq.length).to eq(1)
|
|
341
|
+
expect(File.basename(File.dirname(@uploads.first[:path]))).to match(/\Abatch-.*\.ready\z/)
|
|
342
|
+
expect(Dir.glob(File.join(streaming_temp_directory, '.batch-*.tmp'))).to be_empty
|
|
343
|
+
expect(File.stat(File.dirname(@uploads.first[:path])).mode & 0o777).to eq(0o700)
|
|
344
|
+
expect(File.stat(@uploads.first[:path]).mode & 0o777).to eq(0o600)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it 'writes a single event larger than the threshold intact' do
|
|
348
|
+
oversized = "#{'x' * 20}\n"
|
|
349
|
+
|
|
350
|
+
kusto.multi_receive_encoded([[LogStash::Event.new('id' => 1), oversized]])
|
|
351
|
+
|
|
352
|
+
expect(@uploads.map { |upload| upload[:payload] }).to eq([oversized])
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
it 'does not call Kusto for an empty Logstash batch' do
|
|
356
|
+
kusto.multi_receive_encoded([])
|
|
357
|
+
|
|
358
|
+
expect(@uploads).to be_empty
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
it 'uses encoded byte size when creating requests' do
|
|
362
|
+
multibyte = "\u20ac\u20ac\n"
|
|
363
|
+
|
|
364
|
+
kusto.multi_receive_encoded([
|
|
365
|
+
[LogStash::Event.new('id' => 1), multibyte],
|
|
366
|
+
[LogStash::Event.new('id' => 2), "1234\n"]
|
|
367
|
+
])
|
|
368
|
+
|
|
369
|
+
expect(@uploads.map { |upload| upload[:payload].bytes })
|
|
370
|
+
.to eq([multibyte.bytes, "1234\n".bytes])
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
it 'creates every spool file before starting ingestion' do
|
|
374
|
+
events_and_encoded = [
|
|
375
|
+
[LogStash::Event.new('id' => 1), "123456\n"],
|
|
376
|
+
[LogStash::Event.new('id' => 2), "789\n"],
|
|
377
|
+
[LogStash::Event.new('id' => 3), "abcde\n"]
|
|
378
|
+
]
|
|
379
|
+
observed_file_count = nil
|
|
380
|
+
allow(ingestor).to receive(:upload_async) do |path, _delete_on_success|
|
|
381
|
+
observed_file_count ||= Dir.glob(
|
|
382
|
+
File.join(streaming_temp_directory, 'batch-*.ready', '*.json')
|
|
383
|
+
).length
|
|
384
|
+
@uploads << { path: path, payload: File.binread(path) }
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
kusto.multi_receive_encoded(events_and_encoded)
|
|
388
|
+
|
|
389
|
+
expect(observed_file_count).to eq(2)
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
it 'cleans the entire uncommitted batch when a spool write fails' do
|
|
393
|
+
allow(kusto).to receive(:write_streaming_file).and_call_original
|
|
394
|
+
allow(kusto).to receive(:write_streaming_file).with(anything, ["abcde\n"])
|
|
395
|
+
.and_raise(Errno::ENOSPC)
|
|
396
|
+
|
|
397
|
+
expect do
|
|
398
|
+
kusto.multi_receive_encoded([
|
|
399
|
+
[LogStash::Event.new('id' => 1), "123456\n"],
|
|
400
|
+
[LogStash::Event.new('id' => 2), "abcde\n"]
|
|
401
|
+
])
|
|
402
|
+
end.to raise_error(Errno::ENOSPC)
|
|
403
|
+
|
|
404
|
+
expect(@uploads).to be_empty
|
|
405
|
+
expect(Dir.glob(File.join(streaming_temp_directory, '.batch-*.tmp'))).to be_empty
|
|
406
|
+
expect(Dir.glob(File.join(streaming_temp_directory, 'batch-*.ready'))).to be_empty
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
it 'preserves a committed batch when a post-rename operation fails' do
|
|
410
|
+
spool_directory = kusto.instance_variable_get(:@streaming_temp_directory)
|
|
411
|
+
allow(kusto).to receive(:fsync_directory).and_call_original
|
|
412
|
+
allow(kusto).to receive(:fsync_directory).with(spool_directory)
|
|
413
|
+
.and_raise(Errno::EIO)
|
|
414
|
+
|
|
415
|
+
expect do
|
|
416
|
+
kusto.multi_receive_encoded([
|
|
417
|
+
[LogStash::Event.new('id' => 1), "durable\n"]
|
|
418
|
+
])
|
|
419
|
+
end.to raise_error(Errno::EIO)
|
|
420
|
+
|
|
421
|
+
expect(@uploads).to be_empty
|
|
422
|
+
expect(Dir.glob(File.join(streaming_temp_directory, '.batch-*.tmp'))).to be_empty
|
|
423
|
+
committed_files = Dir.glob(
|
|
424
|
+
File.join(streaming_temp_directory, 'batch-*.ready', 'stream-*.json')
|
|
425
|
+
)
|
|
426
|
+
expect(committed_files.length).to eq(1)
|
|
427
|
+
expect(File.binread(committed_files.first)).to eq("durable\n")
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
it 'creates collision-free atomic batches from concurrent Logstash workers' do
|
|
431
|
+
threads = 8.times.map do |index|
|
|
432
|
+
Thread.new do
|
|
433
|
+
kusto.multi_receive_encoded([
|
|
434
|
+
[LogStash::Event.new('id' => index), "#{index}-payload\n"]
|
|
435
|
+
])
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
threads.each(&:join)
|
|
439
|
+
|
|
440
|
+
paths = @uploads.map { |upload| upload[:path] }
|
|
441
|
+
expect(paths.uniq.length).to eq(8)
|
|
442
|
+
expect(paths.map { |path| File.dirname(path) }.uniq.length).to eq(8)
|
|
443
|
+
expect(Dir.glob(File.join(streaming_temp_directory, '.batch-*.tmp'))).to be_empty
|
|
444
|
+
end
|
|
54
445
|
end
|
|
55
446
|
|
|
56
447
|
end
|
data/version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.2.0
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: logstash-output-kusto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.2.0
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Tamir Kamara
|
|
@@ -145,8 +145,10 @@ files:
|
|
|
145
145
|
- lib/logstash/outputs/kusto.rb
|
|
146
146
|
- lib/logstash/outputs/kusto/ingestor.rb
|
|
147
147
|
- lib/logstash/outputs/kusto/interval.rb
|
|
148
|
+
- lib/logstash/outputs/kusto/streaming_chunker.rb
|
|
148
149
|
- logstash-output-kusto.gemspec
|
|
149
150
|
- spec/outputs/kusto/ingestor_spec.rb
|
|
151
|
+
- spec/outputs/kusto/streaming_chunker_spec.rb
|
|
150
152
|
- spec/outputs/kusto_spec.rb
|
|
151
153
|
- spec/spec_helpers.rb
|
|
152
154
|
- vendor/jar-dependencies/com/azure/azure-core-http-netty/1.16.3/azure-core-http-netty-1.16.3.jar
|
|
@@ -215,7 +217,7 @@ files:
|
|
|
215
217
|
- vendor/jar-dependencies/org/codehaus/woodstox/stax2-api/4.2.2/stax2-api-4.2.2.jar
|
|
216
218
|
- vendor/jar-dependencies/org/jetbrains/annotations/24.1.0/annotations-24.1.0.jar
|
|
217
219
|
- vendor/jar-dependencies/org/jspecify/jspecify/1.0.0/jspecify-1.0.0.jar
|
|
218
|
-
- vendor/jar-dependencies/org/logstash/outputs/logstash-output-kusto/2.
|
|
220
|
+
- vendor/jar-dependencies/org/logstash/outputs/logstash-output-kusto/2.2.0/logstash-output-kusto-2.2.0.jar
|
|
219
221
|
- vendor/jar-dependencies/org/ow2/asm/asm/9.9.1/asm-9.9.1.jar
|
|
220
222
|
- vendor/jar-dependencies/org/reactivestreams/reactive-streams/1.0.4/reactive-streams-1.0.4.jar
|
|
221
223
|
- vendor/jar-dependencies/org/slf4j/slf4j-api/2.0.17/slf4j-api-2.0.17.jar
|
|
@@ -242,10 +244,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
242
244
|
- !ruby/object:Gem::Version
|
|
243
245
|
version: '0'
|
|
244
246
|
requirements: []
|
|
245
|
-
rubygems_version:
|
|
247
|
+
rubygems_version: 4.0.3
|
|
246
248
|
specification_version: 4
|
|
247
249
|
summary: Writes events to Azure Data Explorer (Kusto)
|
|
248
250
|
test_files:
|
|
249
251
|
- spec/outputs/kusto/ingestor_spec.rb
|
|
252
|
+
- spec/outputs/kusto/streaming_chunker_spec.rb
|
|
250
253
|
- spec/outputs/kusto_spec.rb
|
|
251
254
|
- spec/spec_helpers.rb
|