logstash-core 5.0.0.alpha4.snapshot3-java → 5.0.0.alpha5.snapshot1-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.
Potentially problematic release.
This version of logstash-core might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/logstash/agent.rb +6 -2
- data/lib/logstash/api/app_helpers.rb +15 -0
- data/lib/logstash/api/command_factory.rb +3 -1
- data/lib/logstash/api/commands/base.rb +3 -5
- data/lib/logstash/api/commands/default_metadata.rb +27 -0
- data/lib/logstash/api/commands/hot_threads_reporter.rb +61 -0
- data/lib/logstash/api/commands/node.rb +9 -63
- data/lib/logstash/api/commands/stats.rb +5 -61
- data/lib/logstash/api/commands/system/basicinfo_command.rb +3 -6
- data/lib/logstash/api/modules/base.rb +3 -1
- data/lib/logstash/api/modules/node.rb +8 -18
- data/lib/logstash/api/modules/node_stats.rb +5 -41
- data/lib/logstash/api/modules/stats.rb +13 -33
- data/lib/logstash/build.rb +6 -0
- data/lib/logstash/environment.rb +9 -0
- data/lib/logstash/filter_delegator.rb +1 -1
- data/lib/logstash/instrument/metric.rb +7 -6
- data/lib/logstash/instrument/metric_type/base.rb +1 -4
- data/lib/logstash/instrument/namespaced_metric.rb +1 -1
- data/lib/logstash/instrument/null_metric.rb +6 -1
- data/lib/logstash/output_delegator.rb +2 -0
- data/lib/logstash/pipeline.rb +62 -93
- data/lib/logstash/pipeline_reporter.rb +14 -13
- data/lib/logstash/plugin.rb +8 -2
- data/lib/logstash/runner.rb +7 -1
- data/lib/logstash/settings.rb +17 -7
- data/lib/logstash/util/wrapped_synchronous_queue.rb +220 -0
- data/lib/logstash/version.rb +1 -1
- data/lib/logstash/webserver.rb +4 -0
- data/lib/logstash-core/version.rb +1 -1
- data/locales/en.yml +4 -0
- data/logstash-core.gemspec +2 -2
- data/spec/api/lib/api/node_spec.rb +0 -1
- data/spec/api/lib/api/node_stats_spec.rb +36 -34
- data/spec/api/lib/api/support/resource_dsl_methods.rb +15 -0
- data/spec/api/spec_helper.rb +5 -2
- data/spec/logstash/inputs/metrics_spec.rb +1 -1
- data/spec/logstash/instrument/metric_type/counter_spec.rb +1 -6
- data/spec/logstash/instrument/metric_type/gauge_spec.rb +1 -4
- data/spec/logstash/instrument/namespaced_metric_spec.rb +61 -2
- data/spec/logstash/instrument/null_metric_spec.rb +7 -9
- data/spec/logstash/pipeline_spec.rb +7 -7
- data/spec/logstash/plugin_spec.rb +73 -0
- data/spec/logstash/settings/string_spec.rb +21 -0
- data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +70 -22
- data/spec/support/shared_examples.rb +98 -0
- metadata +11 -4
@@ -9,20 +9,25 @@ module LogStash module Instrument
|
|
9
9
|
attr_reader :namespace_name, :collector
|
10
10
|
|
11
11
|
def increment(key, value = 1)
|
12
|
+
Metric.validate_key!(key)
|
12
13
|
end
|
13
14
|
|
14
|
-
def decrement(
|
15
|
+
def decrement(key, value = 1)
|
16
|
+
Metric.validate_key!(key)
|
15
17
|
end
|
16
18
|
|
17
19
|
def gauge(key, value)
|
20
|
+
Metric.validate_key!(key)
|
18
21
|
end
|
19
22
|
|
20
23
|
def report_time(key, duration)
|
24
|
+
Metric.validate_key!(key)
|
21
25
|
end
|
22
26
|
|
23
27
|
# We have to manually redefine this method since it can return an
|
24
28
|
# object this object also has to be implemented as a NullObject
|
25
29
|
def time(key)
|
30
|
+
Metric.validate_key!(key)
|
26
31
|
if block_given?
|
27
32
|
yield
|
28
33
|
else
|
@@ -27,6 +27,8 @@ module LogStash class OutputDelegator
|
|
27
27
|
|
28
28
|
# Scope the metrics to the plugin
|
29
29
|
namespaced_metric = metric.namespace(output.plugin_unique_name.to_sym)
|
30
|
+
output.metric = namespaced_metric
|
31
|
+
|
30
32
|
@metric_events = namespaced_metric.namespace(:events)
|
31
33
|
namespaced_metric.gauge(:name, config_name)
|
32
34
|
|
data/lib/logstash/pipeline.rb
CHANGED
@@ -33,7 +33,9 @@ module LogStash; class Pipeline
|
|
33
33
|
:thread,
|
34
34
|
:config_str,
|
35
35
|
:settings,
|
36
|
-
:metric
|
36
|
+
:metric,
|
37
|
+
:filter_queue_client,
|
38
|
+
:input_queue_client
|
37
39
|
|
38
40
|
MAX_INFLIGHT_WARN_THRESHOLD = 10_000
|
39
41
|
|
@@ -82,14 +84,18 @@ module LogStash; class Pipeline
|
|
82
84
|
raise
|
83
85
|
end
|
84
86
|
|
85
|
-
|
87
|
+
queue = LogStash::Util::WrappedSynchronousQueue.new
|
88
|
+
@input_queue_client = queue.write_client
|
89
|
+
@filter_queue_client = queue.read_client
|
90
|
+
# Note that @infilght_batches as a central mechanism for tracking inflight
|
91
|
+
# batches will fail if we have multiple read clients here.
|
92
|
+
@filter_queue_client.set_events_metric(metric.namespace([:stats, :events]))
|
93
|
+
@filter_queue_client.set_pipeline_metric(
|
94
|
+
metric.namespace([:stats, :pipelines, pipeline_id.to_s.to_sym, :events])
|
95
|
+
)
|
86
96
|
@events_filtered = Concurrent::AtomicFixnum.new(0)
|
87
97
|
@events_consumed = Concurrent::AtomicFixnum.new(0)
|
88
98
|
|
89
|
-
# We generally only want one thread at a time able to access pop/take/poll operations
|
90
|
-
# from this queue. We also depend on this to be able to block consumers while we snapshot
|
91
|
-
# in-flight buffers
|
92
|
-
@input_queue_pop_mutex = Mutex.new
|
93
99
|
@input_threads = []
|
94
100
|
# @ready requires thread safety since it is typically polled from outside the pipeline thread
|
95
101
|
@ready = Concurrent::AtomicBoolean.new(false)
|
@@ -176,8 +182,6 @@ module LogStash; class Pipeline
|
|
176
182
|
end
|
177
183
|
|
178
184
|
def start_workers
|
179
|
-
@inflight_batches = {}
|
180
|
-
|
181
185
|
@worker_threads.clear # In case we're restarting the pipeline
|
182
186
|
begin
|
183
187
|
start_inputs
|
@@ -187,13 +191,14 @@ module LogStash; class Pipeline
|
|
187
191
|
pipeline_workers = safe_pipeline_worker_count
|
188
192
|
batch_size = @settings.get("pipeline.batch.size")
|
189
193
|
batch_delay = @settings.get("pipeline.batch.delay")
|
194
|
+
|
190
195
|
max_inflight = batch_size * pipeline_workers
|
191
196
|
|
192
|
-
config_metric = metric.namespace([:stats, :pipelines, pipeline_id.to_s.to_sym, :config])
|
197
|
+
config_metric = metric.namespace([:stats, :pipelines, pipeline_id.to_s.to_sym, :config])
|
193
198
|
config_metric.gauge(:workers, pipeline_workers)
|
194
199
|
config_metric.gauge(:batch_size, batch_size)
|
195
200
|
config_metric.gauge(:batch_delay, batch_delay)
|
196
|
-
|
201
|
+
|
197
202
|
@logger.info("Starting pipeline",
|
198
203
|
"id" => self.pipeline_id,
|
199
204
|
"pipeline.workers" => pipeline_workers,
|
@@ -211,7 +216,7 @@ module LogStash; class Pipeline
|
|
211
216
|
end
|
212
217
|
end
|
213
218
|
ensure
|
214
|
-
# it is important to
|
219
|
+
# it is important to guarantee @ready to be true after the startup sequence has been completed
|
215
220
|
# to potentially unblock the shutdown method which may be waiting on @ready to proceed
|
216
221
|
@ready.make_true
|
217
222
|
end
|
@@ -222,73 +227,39 @@ module LogStash; class Pipeline
|
|
222
227
|
def worker_loop(batch_size, batch_delay)
|
223
228
|
running = true
|
224
229
|
|
225
|
-
|
226
|
-
namespace_pipeline = metric.namespace([:stats, :pipelines, pipeline_id.to_s.to_sym, :events])
|
230
|
+
@filter_queue_client.set_batch_dimensions(batch_size, batch_delay)
|
227
231
|
|
228
232
|
while running
|
229
|
-
|
230
|
-
|
231
|
-
running = false if
|
232
|
-
|
233
|
-
@events_consumed.increment(input_batch.size)
|
234
|
-
namespace_events.increment(:in, input_batch.size)
|
235
|
-
namespace_pipeline.increment(:in, input_batch.size)
|
233
|
+
batch = @filter_queue_client.take_batch
|
234
|
+
@events_consumed.increment(batch.size)
|
235
|
+
running = false if batch.shutdown_signal_received?
|
236
|
+
filter_batch(batch)
|
236
237
|
|
237
|
-
|
238
|
-
|
239
|
-
if signal # Flush on SHUTDOWN or FLUSH
|
240
|
-
flush_options = (signal == LogStash::SHUTDOWN) ? {:final => true} : {}
|
241
|
-
flush_filters_to_batch(filtered_batch, flush_options)
|
238
|
+
if batch.shutdown_signal_received? || batch.flush_signal_received?
|
239
|
+
flush_filters_to_batch(batch)
|
242
240
|
end
|
243
241
|
|
244
|
-
|
245
|
-
|
246
|
-
namespace_events.increment(:filtered, filtered_batch.size)
|
247
|
-
namespace_pipeline.increment(:filtered, filtered_batch.size)
|
248
|
-
|
249
|
-
output_batch(filtered_batch)
|
250
|
-
|
251
|
-
namespace_events.increment(:out, filtered_batch.size)
|
252
|
-
namespace_pipeline.increment(:out, filtered_batch.size)
|
253
|
-
|
254
|
-
inflight_batches_synchronize { set_current_thread_inflight_batch(nil) }
|
242
|
+
output_batch(batch)
|
243
|
+
@filter_queue_client.close_batch(batch)
|
255
244
|
end
|
256
245
|
end
|
257
246
|
|
258
|
-
def take_batch(batch_size, batch_delay)
|
259
|
-
batch = []
|
260
|
-
# Since this is externally synchronized in `worker_look` wec can guarantee that the visibility of an insight batch
|
261
|
-
# guaranteed to be a full batch not a partial batch
|
262
|
-
set_current_thread_inflight_batch(batch)
|
263
|
-
|
264
|
-
signal = false
|
265
|
-
batch_size.times do |t|
|
266
|
-
event = (t == 0) ? @input_queue.take : @input_queue.poll(batch_delay)
|
267
|
-
|
268
|
-
if event.nil?
|
269
|
-
next
|
270
|
-
elsif event == LogStash::SHUTDOWN || event == LogStash::FLUSH
|
271
|
-
# We MUST break here. If a batch consumes two SHUTDOWN events
|
272
|
-
# then another worker may have its SHUTDOWN 'stolen', thus blocking
|
273
|
-
# the pipeline. We should stop doing work after flush as well.
|
274
|
-
signal = event
|
275
|
-
break
|
276
|
-
else
|
277
|
-
batch << event
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
[batch, signal]
|
282
|
-
end
|
283
|
-
|
284
247
|
def filter_batch(batch)
|
285
|
-
batch.
|
286
|
-
if
|
287
|
-
filtered = filter_func(
|
288
|
-
filtered.each
|
248
|
+
batch.each do |event|
|
249
|
+
if event.is_a?(LogStash::Event)
|
250
|
+
filtered = filter_func(event)
|
251
|
+
filtered.each do |e|
|
252
|
+
#these are both original and generated events
|
253
|
+
if e.cancelled?
|
254
|
+
batch.cancel(e)
|
255
|
+
else
|
256
|
+
batch.merge(e)
|
257
|
+
end
|
258
|
+
end
|
289
259
|
end
|
290
|
-
acc
|
291
260
|
end
|
261
|
+
@filter_queue_client.add_filtered_metrics(batch)
|
262
|
+
@events_filtered.increment(batch.size)
|
292
263
|
rescue Exception => e
|
293
264
|
# Plugins authors should manage their own exceptions in the plugin code
|
294
265
|
# but if an exception is raised up to the worker thread they are considered
|
@@ -304,31 +275,21 @@ module LogStash; class Pipeline
|
|
304
275
|
# Take an array of events and send them to the correct output
|
305
276
|
def output_batch(batch)
|
306
277
|
# Build a mapping of { output_plugin => [events...]}
|
307
|
-
|
278
|
+
output_events_map = Hash.new { |h, k| h[k] = [] }
|
279
|
+
batch.each do |event|
|
308
280
|
# We ask the AST to tell us which outputs to send each event to
|
309
281
|
# Then, we stick it in the correct bin
|
310
282
|
|
311
283
|
# output_func should never return anything other than an Array but we have lots of legacy specs
|
312
284
|
# that monkeypatch it and return nil. We can deprecate "|| []" after fixing these specs
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
acc
|
285
|
+
(output_func(event) || []).each do |output|
|
286
|
+
output_events_map[output].push(event)
|
287
|
+
end
|
317
288
|
end
|
318
|
-
|
319
289
|
# Now that we have our output to event mapping we can just invoke each output
|
320
290
|
# once with its list of events
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
def set_current_thread_inflight_batch(batch)
|
325
|
-
@inflight_batches[Thread.current] = batch
|
326
|
-
end
|
327
|
-
|
328
|
-
def inflight_batches_synchronize
|
329
|
-
@input_queue_pop_mutex.synchronize do
|
330
|
-
yield(@inflight_batches)
|
331
|
-
end
|
291
|
+
output_events_map.each { |output, events| output.multi_receive(events) }
|
292
|
+
@filter_queue_client.add_output_metrics(batch)
|
332
293
|
end
|
333
294
|
|
334
295
|
def wait_inputs
|
@@ -359,7 +320,7 @@ module LogStash; class Pipeline
|
|
359
320
|
def inputworker(plugin)
|
360
321
|
LogStash::Util::set_thread_name("[#{pipeline_id}]<#{plugin.class.config_name}")
|
361
322
|
begin
|
362
|
-
plugin.run(@
|
323
|
+
plugin.run(@input_queue_client)
|
363
324
|
rescue => e
|
364
325
|
if plugin.stop?
|
365
326
|
@logger.debug("Input plugin raised exception during shutdown, ignoring it.",
|
@@ -413,7 +374,7 @@ module LogStash; class Pipeline
|
|
413
374
|
# Each worker thread will receive this exactly once!
|
414
375
|
@worker_threads.each do |t|
|
415
376
|
@logger.debug("Pushing shutdown", :thread => t.inspect)
|
416
|
-
@
|
377
|
+
@input_queue_client.push(LogStash::SHUTDOWN)
|
417
378
|
end
|
418
379
|
|
419
380
|
@worker_threads.each do |t|
|
@@ -437,7 +398,11 @@ module LogStash; class Pipeline
|
|
437
398
|
elsif plugin_type == "filter"
|
438
399
|
LogStash::FilterDelegator.new(@logger, klass, pipeline_scoped_metric.namespace(:filters), *args)
|
439
400
|
else
|
440
|
-
klass.new(*args)
|
401
|
+
new_plugin = klass.new(*args)
|
402
|
+
inputs_metric = pipeline_scoped_metric.namespace(:inputs)
|
403
|
+
namespaced_metric = inputs_metric.namespace(new_plugin.plugin_unique_name.to_sym)
|
404
|
+
new_plugin.metric = namespaced_metric
|
405
|
+
new_plugin
|
441
406
|
end
|
442
407
|
end
|
443
408
|
|
@@ -449,7 +414,7 @@ module LogStash; class Pipeline
|
|
449
414
|
end
|
450
415
|
|
451
416
|
|
452
|
-
# perform filters flush and
|
417
|
+
# perform filters flush and yield flushed event to the passed block
|
453
418
|
# @param options [Hash]
|
454
419
|
# @option options [Boolean] :final => true to signal a final shutdown flush
|
455
420
|
def flush_filters(options = {}, &block)
|
@@ -479,7 +444,7 @@ module LogStash; class Pipeline
|
|
479
444
|
def flush
|
480
445
|
if @flushing.compare_and_set(false, true)
|
481
446
|
@logger.debug? && @logger.debug("Pushing flush onto pipeline")
|
482
|
-
@
|
447
|
+
@input_queue_client.push(LogStash::FLUSH)
|
483
448
|
end
|
484
449
|
end
|
485
450
|
|
@@ -493,18 +458,22 @@ module LogStash; class Pipeline
|
|
493
458
|
end
|
494
459
|
|
495
460
|
# perform filters flush into the output queue
|
461
|
+
#
|
462
|
+
# @param batch [ReadClient::ReadBatch]
|
496
463
|
# @param options [Hash]
|
497
|
-
# @option options [Boolean] :final => true to signal a final shutdown flush
|
498
464
|
def flush_filters_to_batch(batch, options = {})
|
465
|
+
options[:final] = batch.shutdown_signal_received?
|
499
466
|
flush_filters(options) do |event|
|
500
|
-
|
467
|
+
if event.cancelled?
|
468
|
+
batch.cancel(event)
|
469
|
+
else
|
501
470
|
@logger.debug? and @logger.debug("Pushing flushed events", :event => event)
|
502
|
-
batch
|
471
|
+
batch.merge(event)
|
503
472
|
end
|
504
473
|
end
|
505
474
|
|
506
475
|
@flushing.set(false)
|
507
|
-
end #
|
476
|
+
end # flush_filters_to_batch
|
508
477
|
|
509
478
|
def plugin_threads_info
|
510
479
|
input_threads = @input_threads.select {|t| t.alive? }
|
@@ -39,7 +39,7 @@ module LogStash; class PipelineReporter
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def initialize(logger,pipeline)
|
42
|
+
def initialize(logger, pipeline)
|
43
43
|
@logger = logger
|
44
44
|
@pipeline = pipeline
|
45
45
|
end
|
@@ -52,7 +52,8 @@ module LogStash; class PipelineReporter
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def to_hash
|
55
|
-
pipeline.
|
55
|
+
# pipeline.filter_queue_client.inflight_batches is synchronized
|
56
|
+
pipeline.filter_queue_client.inflight_batches do |batch_map|
|
56
57
|
worker_states_snap = worker_states(batch_map) # We only want to run this once
|
57
58
|
inflight_count = worker_states_snap.map {|s| s[:inflight_count] }.reduce(0, :+)
|
58
59
|
|
@@ -83,17 +84,17 @@ module LogStash; class PipelineReporter
|
|
83
84
|
pipeline.plugin_threads
|
84
85
|
end
|
85
86
|
|
86
|
-
# Not threadsafe!
|
87
|
+
# Not threadsafe! ensure synchronization
|
87
88
|
def worker_states(batch_map)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
89
|
+
pipeline.worker_threads.map.with_index do |thread, idx|
|
90
|
+
status = thread.status || "dead"
|
91
|
+
inflight_count = batch_map[thread] ? batch_map[thread].size : 0
|
92
|
+
{
|
93
|
+
:status => status,
|
94
|
+
:alive => thread.alive?,
|
95
|
+
:index => idx,
|
96
|
+
:inflight_count => inflight_count
|
97
|
+
}
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
@@ -111,4 +112,4 @@ module LogStash; class PipelineReporter
|
|
111
112
|
}
|
112
113
|
end
|
113
114
|
end
|
114
|
-
end end
|
115
|
+
end end
|
data/lib/logstash/plugin.rb
CHANGED
@@ -105,9 +105,15 @@ class LogStash::Plugin
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def metric
|
108
|
-
|
108
|
+
# We can disable metric per plugin if we want in the configuration
|
109
|
+
# we will use the NullMetric in this case.
|
110
|
+
@metric_plugin ||= if @enable_metric
|
111
|
+
# Fallback when testing plugin and no metric collector are correctly configured.
|
112
|
+
@metric.nil? ? LogStash::Instrument::NullMetric.new : @metric
|
113
|
+
else
|
114
|
+
LogStash::Instrument::NullMetric.new
|
115
|
+
end
|
109
116
|
end
|
110
|
-
|
111
117
|
# return the configured name of this plugin
|
112
118
|
# @return [String] The name of the plugin defined by `config_name`
|
113
119
|
def config_name
|
data/lib/logstash/runner.rb
CHANGED
@@ -16,6 +16,7 @@ require "logstash/config/defaults"
|
|
16
16
|
require "logstash/shutdown_watcher"
|
17
17
|
require "logstash/patches/clamp"
|
18
18
|
require "logstash/settings"
|
19
|
+
require "logstash/version"
|
19
20
|
|
20
21
|
class LogStash::Runner < Clamp::StrictCommand
|
21
22
|
# The `path.settings` need to be defined in the runner instead of the `logstash-core/lib/logstash/environment.rb`
|
@@ -63,6 +64,12 @@ class LogStash::Runner < Clamp::StrictCommand
|
|
63
64
|
:attribute_name => "pipeline.unsafe_shutdown",
|
64
65
|
:default => LogStash::SETTINGS.get_default("pipeline.unsafe_shutdown")
|
65
66
|
|
67
|
+
# Data Path Setting
|
68
|
+
option ["--path.data"] , "PATH",
|
69
|
+
I18n.t("logstash.runner.flag.datapath"),
|
70
|
+
:attribute_name => "path.data",
|
71
|
+
:default => LogStash::SETTINGS.get_default("path.data")
|
72
|
+
|
66
73
|
# Plugins Settings
|
67
74
|
option ["-p", "--path.plugins"] , "PATH",
|
68
75
|
I18n.t("logstash.runner.flag.pluginpath"),
|
@@ -256,7 +263,6 @@ class LogStash::Runner < Clamp::StrictCommand
|
|
256
263
|
end # def show_version
|
257
264
|
|
258
265
|
def show_version_logstash
|
259
|
-
require "logstash/version"
|
260
266
|
puts "logstash #{LOGSTASH_VERSION}"
|
261
267
|
end # def show_version_logstash
|
262
268
|
|
data/lib/logstash/settings.rb
CHANGED
@@ -210,12 +210,6 @@ module LogStash
|
|
210
210
|
end
|
211
211
|
end
|
212
212
|
|
213
|
-
class String < Setting
|
214
|
-
def initialize(name, default=nil, strict=true)
|
215
|
-
super(name, ::String, default, strict)
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
213
|
class Numeric < Setting
|
220
214
|
def initialize(name, default=nil, strict=true)
|
221
215
|
super(name, ::Numeric, default, strict)
|
@@ -241,11 +235,15 @@ module LogStash
|
|
241
235
|
|
242
236
|
class String < Setting
|
243
237
|
def initialize(name, default=nil, strict=true, possible_strings=[])
|
238
|
+
@possible_strings = possible_strings
|
244
239
|
super(name, ::String, default, strict)
|
245
240
|
end
|
246
241
|
|
247
242
|
def validate(value)
|
248
|
-
super(value)
|
243
|
+
super(value)
|
244
|
+
unless @possible_strings.empty? || @possible_strings.include?(value)
|
245
|
+
raise ArgumentError.new("invalid value \"#{value}\". Options are: #{@possible_strings.inspect}")
|
246
|
+
end
|
249
247
|
end
|
250
248
|
end
|
251
249
|
|
@@ -261,6 +259,18 @@ module LogStash
|
|
261
259
|
end
|
262
260
|
end
|
263
261
|
|
262
|
+
class WritableDirectory < Setting
|
263
|
+
def initialize(name, default=nil, strict=true)
|
264
|
+
super(name, ::String, default, strict) do |path|
|
265
|
+
if ::File.directory?(path) && ::File.writable?(path)
|
266
|
+
true
|
267
|
+
else
|
268
|
+
raise ::ArgumentError.new("Path \"#{path}\" is not a directory or not writable.")
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
264
274
|
end
|
265
275
|
|
266
276
|
SETTINGS = Settings.new
|