logstash-core 5.0.0.alpha4.snapshot1-java → 5.0.0.alpha4.snapshot2-java

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of logstash-core might be problematic. Click here for more details.

Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/logstash-core/version.rb +1 -1
  3. data/lib/logstash/agent.rb +31 -36
  4. data/lib/logstash/api/command_factory.rb +3 -1
  5. data/lib/logstash/api/commands/base.rb +4 -0
  6. data/lib/logstash/api/commands/node.rb +116 -0
  7. data/lib/logstash/api/commands/stats.rb +28 -77
  8. data/lib/logstash/api/modules/base.rb +2 -2
  9. data/lib/logstash/api/modules/node.rb +23 -6
  10. data/lib/logstash/api/modules/node_stats.rb +15 -1
  11. data/lib/logstash/api/rack_app.rb +9 -6
  12. data/lib/logstash/api/service.rb +8 -47
  13. data/lib/logstash/config/config_ast.rb +11 -3
  14. data/lib/logstash/config/mixin.rb +60 -22
  15. data/lib/logstash/inputs/metrics.rb +2 -2
  16. data/lib/logstash/instrument/collector.rb +5 -6
  17. data/lib/logstash/instrument/metric.rb +1 -1
  18. data/lib/logstash/instrument/metric_store.rb +54 -0
  19. data/lib/logstash/pipeline.rb +10 -4
  20. data/lib/logstash/runner.rb +2 -2
  21. data/lib/logstash/util/safe_uri.rb +48 -0
  22. data/lib/logstash/version.rb +1 -1
  23. data/lib/logstash/webserver.rb +8 -7
  24. data/logstash-core.gemspec +1 -1
  25. data/spec/api/lib/api/node_plugins_spec.rb +32 -0
  26. data/spec/api/lib/api/node_spec.rb +41 -7
  27. data/spec/api/lib/api/node_stats_spec.rb +31 -6
  28. data/spec/api/lib/api/plugins_spec.rb +1 -7
  29. data/spec/api/lib/api/root_spec.rb +2 -7
  30. data/spec/api/lib/api/support/resource_dsl_methods.rb +14 -7
  31. data/spec/api/spec_helper.rb +24 -50
  32. data/spec/logstash/agent_spec.rb +36 -13
  33. data/spec/logstash/config/config_ast_spec.rb +43 -0
  34. data/spec/logstash/config/mixin_spec.rb +138 -0
  35. data/spec/logstash/inputs/metrics_spec.rb +10 -11
  36. data/spec/logstash/instrument/collector_spec.rb +1 -1
  37. data/spec/logstash/instrument/metric_store_spec.rb +61 -0
  38. data/spec/logstash/instrument/periodic_poller/jvm_spec.rb +6 -3
  39. data/spec/logstash/pipeline_spec.rb +9 -9
  40. data/spec/support/mocks_classes.rb +2 -1
  41. metadata +39 -35
@@ -3,6 +3,11 @@ module LogStash
3
3
  module Api
4
4
  module Modules
5
5
  class NodeStats < ::LogStash::Api::Modules::Base
6
+ #set :environment, :test
7
+ #set :dump_errors, true
8
+ #set :raise_errors, true
9
+ #set :logging, Logger.new(STDERR)
10
+
6
11
 
7
12
  before do
8
13
  @stats = factory.build(:stats)
@@ -14,7 +19,8 @@ module LogStash
14
19
  payload = {
15
20
  :events => events_payload,
16
21
  :jvm => jvm_payload,
17
- :process => process_payload
22
+ :process => process_payload,
23
+ :mem => mem_payload
18
24
  }
19
25
 
20
26
  respond_with payload
@@ -40,6 +46,10 @@ module LogStash
40
46
  respond_with :process => process_payload
41
47
  end
42
48
 
49
+ get "/mem" do
50
+ respond_with :mem => mem_payload
51
+ end
52
+
43
53
  private
44
54
 
45
55
  def events_payload
@@ -53,6 +63,10 @@ module LogStash
53
63
  def process_payload
54
64
  @stats.process
55
65
  end
66
+
67
+ def mem_payload
68
+ @stats.memory
69
+ end
56
70
  end
57
71
  end
58
72
  end
@@ -73,8 +73,9 @@ module LogStash
73
73
  end
74
74
  end
75
75
 
76
- def self.app(logger, environment)
77
- namespaces = rack_namespaces
76
+ def self.app(logger, agent, environment)
77
+ namespaces = rack_namespaces(agent)
78
+
78
79
  Rack::Builder.new do
79
80
  # Custom logger object. Rack CommonLogger does not work with cabin
80
81
  use ApiLogger, logger
@@ -87,21 +88,23 @@ module LogStash
87
88
  use ApiErrorHandler, logger
88
89
  end
89
90
 
90
- run LogStash::Api::Modules::Root
91
+ run LogStash::Api::Modules::Root.new(nil, agent)
91
92
  namespaces.each_pair do |namespace, app|
92
93
  map(namespace) do
93
- run app
94
+ # Pass down a reference to the current agent
95
+ # This allow the API to have direct access to the collector
96
+ run app.new(nil, agent)
94
97
  end
95
98
  end
96
99
  end
97
100
  end
98
101
 
99
- def self.rack_namespaces
102
+ def self.rack_namespaces(agent)
100
103
  {
101
104
  "/_node" => LogStash::Api::Modules::Node,
102
105
  "/_stats" => LogStash::Api::Modules::Stats,
103
106
  "/_node/stats" => LogStash::Api::Modules::NodeStats,
104
- "/_plugins" => LogStash::Api::Modules::Plugins
107
+ "/_node/plugins" => LogStash::Api::Modules::Plugins
105
108
  }
106
109
  end
107
110
  end
@@ -5,68 +5,29 @@ require "logstash/util/loggable"
5
5
  module LogStash
6
6
  module Api
7
7
  class Service
8
-
9
- include Singleton
10
8
  include LogStash::Util::Loggable
11
9
 
12
- def initialize
13
- @snapshot_rotation_mutex = Mutex.new
14
- @snapshot = nil
15
- logger.debug("[api-service] start") if logger.debug?
16
- LogStash::Instrument::Collector.instance.add_observer(self)
17
- end
10
+ attr_reader :agent
18
11
 
19
- def stop
20
- logger.debug("[api-service] stop") if logger.debug?
21
- LogStash::Instrument::Collector.instance.delete_observer(self)
22
- end
23
-
24
- def agent
25
- LogStash::Instrument::Collector.instance.agent
12
+ def initialize(agent)
13
+ @agent = agent
14
+ logger.debug("[api-service] start") if logger.debug?
26
15
  end
27
16
 
28
17
  def started?
29
- !@snapshot.nil? && has_counters?
30
- end
31
-
32
- def update(snapshot)
33
- logger.debug("[api-service] snapshot received", :snapshot_time => snapshot.created_at) if logger.debug?
34
-
35
- @snapshot_rotation_mutex.synchronize do
36
- @snapshot = snapshot
37
- end
18
+ true
38
19
  end
39
20
 
40
21
  def snapshot
41
- @snapshot_rotation_mutex.synchronize { @snapshot }
22
+ agent.metric.collector.snapshot_metric
42
23
  end
43
24
 
44
25
  def get_shallow(*path)
45
26
  snapshot.metric_store.get_shallow(*path)
46
27
  end
47
28
 
48
- def get(key)
49
- metric_store = @snapshot_rotation_mutex.synchronize { @snapshot.metric_store }
50
- if key == :jvm_memory_stats
51
- data = metric_store.get_shallow(:jvm, :memory)
52
- else
53
- data = metric_store.get_with_path("stats/events")
54
- end
55
- LogStash::Json.dump(data)
56
- end
57
-
58
- private
59
-
60
- def has_counters?
61
- (["LogStash::Instrument::MetricType::Counter", "LogStash::Instrument::MetricType::Gauge"] - metric_types).empty?
62
- end
63
-
64
- def metric_types
65
- types = []
66
- @snapshot_rotation_mutex.synchronize do
67
- types = @snapshot.metric_store.all.map { |t| t.class.to_s }
68
- end
69
- return types
29
+ def extract_metrics(path, *keys)
30
+ snapshot.metric_store.extract_metrics(path, *keys)
70
31
  end
71
32
  end
72
33
  end
@@ -76,6 +76,14 @@ module LogStash; module Config; module AST
76
76
  @defered_conditionals_index = val
77
77
  end
78
78
 
79
+ def self.plugin_instance_index
80
+ @plugin_instance_index
81
+ end
82
+
83
+ def self.plugin_instance_index=(val)
84
+ @plugin_instance_index = val
85
+ end
86
+
79
87
  class Node < Treetop::Runtime::SyntaxNode
80
88
  def text_value_for_comments
81
89
  text_value.gsub(/[\r\n]/, " ")
@@ -86,6 +94,7 @@ module LogStash; module Config; module AST
86
94
  def compile
87
95
  LogStash::Config::AST.defered_conditionals = []
88
96
  LogStash::Config::AST.defered_conditionals_index = 0
97
+ LogStash::Config::AST.plugin_instance_index = 0
89
98
  code = []
90
99
 
91
100
  code << <<-CODE
@@ -140,7 +149,6 @@ module LogStash; module Config; module AST
140
149
  # like @filter_<name>_1
141
150
  def initialize(*args)
142
151
  super(*args)
143
- @i = 0
144
152
  end
145
153
 
146
154
  # Generate ruby code to initialize all the plugins.
@@ -196,9 +204,9 @@ module LogStash; module Config; module AST
196
204
 
197
205
  plugins.each do |plugin|
198
206
  # Unique number for every plugin.
199
- @i += 1
207
+ LogStash::Config::AST.plugin_instance_index += 1
200
208
  # store things as ivars, like @filter_grok_3
201
- var = :"#{plugin.plugin_type}_#{plugin.plugin_name}_#{@i}"
209
+ var = :"#{plugin.plugin_type}_#{plugin.plugin_name}_#{LogStash::Config::AST.plugin_instance_index}"
202
210
  # puts("var=#{var.inspect}")
203
211
  @variables[plugin] = var
204
212
  end
@@ -4,6 +4,7 @@ require "logstash/config/registry"
4
4
  require "logstash/plugins/registry"
5
5
  require "logstash/logging"
6
6
  require "logstash/util/password"
7
+ require "logstash/util/safe_uri"
7
8
  require "logstash/version"
8
9
  require "logstash/environment"
9
10
  require "logstash/util/plugin_version"
@@ -327,58 +328,88 @@ module LogStash::Config::Mixin
327
328
  return true
328
329
  end # def validate_check_invalid_parameter_names
329
330
 
331
+ def validate_check_required_parameter(config_key, config_opts, k, v)
332
+ if config_key.is_a?(Regexp)
333
+ (k =~ config_key && v)
334
+ elsif config_key.is_a?(String)
335
+ k && v
336
+ end
337
+ end
338
+
330
339
  def validate_check_required_parameter_names(params)
331
340
  is_valid = true
332
341
 
333
342
  @config.each do |config_key, config|
334
343
  next unless config[:required]
335
344
 
336
- if config_key.is_a?(Regexp)
337
- next if params.keys.select { |k| k =~ config_key }.length > 0
338
- elsif config_key.is_a?(String)
339
- next if params.keys.member?(config_key)
345
+ if config_key.is_a?(Regexp) && !params.keys.any? { |k| k =~ config_key }
346
+ is_valid = false
340
347
  end
341
- @logger.error(I18n.t("logstash.runner.configuration.setting_missing",
342
- :setting => config_key, :plugin => @plugin_name,
343
- :type => @plugin_type))
344
- is_valid = false
348
+
349
+ value = params[config_key]
350
+ if value.nil? || (config[:list] && Array(value).empty?)
351
+ @logger.error(I18n.t("logstash.runner.configuration.setting_missing",
352
+ :setting => config_key, :plugin => @plugin_name,
353
+ :type => @plugin_type))
354
+ is_valid = false
355
+ end
345
356
  end
346
357
 
347
358
  return is_valid
348
359
  end
349
360
 
361
+ def process_parameter_value(value, config_settings)
362
+ config_val = config_settings[:validate]
363
+
364
+ if config_settings[:list]
365
+ value = Array(value) # coerce scalars to lists
366
+ # Empty lists are converted to nils
367
+ return true, nil if value.empty?
368
+
369
+ validated_items = value.map {|v| validate_value(v, config_val)}
370
+ is_valid = validated_items.all? {|sr| sr[0] }
371
+ processed_value = validated_items.map {|sr| sr[1]}
372
+ else
373
+ is_valid, processed_value = validate_value(value, config_val)
374
+ end
375
+
376
+ return [is_valid, processed_value]
377
+ end
378
+
350
379
  def validate_check_parameter_values(params)
351
380
  # Filter out parametrs that match regexp keys.
352
381
  # These are defined in plugins like this:
353
382
  # config /foo.*/ => ...
354
- is_valid = true
383
+ all_params_valid = true
355
384
 
356
385
  params.each do |key, value|
357
386
  @config.keys.each do |config_key|
358
387
  next unless (config_key.is_a?(Regexp) && key =~ config_key) \
359
388
  || (config_key.is_a?(String) && key == config_key)
360
- config_val = @config[config_key][:validate]
361
- #puts " Key matches."
362
- success, result = validate_value(value, config_val)
363
- if success
364
- # Accept coerced value if success
389
+
390
+ config_settings = @config[config_key]
391
+
392
+ is_valid, processed_value = process_parameter_value(value, config_settings)
393
+
394
+ if is_valid
395
+ # Accept coerced value if valid
365
396
  # Used for converting values in the config to proper objects.
366
- params[key] = result if !result.nil?
397
+ params[key] = processed_value
367
398
  else
368
399
  @logger.error(I18n.t("logstash.runner.configuration.setting_invalid",
369
400
  :plugin => @plugin_name, :type => @plugin_type,
370
401
  :setting => key, :value => value.inspect,
371
- :value_type => config_val,
372
- :note => result))
402
+ :value_type => config_settings[:validate],
403
+ :note => processed_value))
373
404
  end
374
- #puts "Result: #{key} / #{result.inspect} / #{success}"
375
- is_valid &&= success
405
+
406
+ all_params_valid &&= is_valid
376
407
 
377
408
  break # done with this param key
378
409
  end # config.each
379
410
  end # params.each
380
411
 
381
- return is_valid
412
+ return all_params_valid
382
413
  end # def validate_check_parameter_values
383
414
 
384
415
  def validator_find(key)
@@ -513,6 +544,12 @@ module LogStash::Config::Mixin
513
544
  end
514
545
 
515
546
  result = value.first.is_a?(::LogStash::Util::Password) ? value.first : ::LogStash::Util::Password.new(value.first)
547
+ when :uri
548
+ if value.size > 1
549
+ return false, "Expected uri (one value), got #{value.size} values?"
550
+ end
551
+
552
+ result = value.first.is_a?(::LogStash::Util::SafeURI) ? value.first : ::LogStash::Util::SafeURI.new(value.first)
516
553
  when :path
517
554
  if value.size > 1 # Only 1 value wanted
518
555
  return false, "Expected path (one value), got #{value.size} values?"
@@ -548,8 +585,9 @@ module LogStash::Config::Mixin
548
585
 
549
586
  def secure_params!(params)
550
587
  params.each do |key, value|
551
- if @config[key][:validate] == :password && !value.is_a?(::LogStash::Util::Password)
552
- params[key] = ::LogStash::Util::Password.new(value)
588
+ if [:uri, :password].include? @config[key][:validate]
589
+ is_valid, processed_value = process_parameter_value(value, @config[key])
590
+ params[key] = processed_value
553
591
  end
554
592
  end
555
593
  end
@@ -21,7 +21,7 @@ module LogStash module Inputs
21
21
  @queue = queue
22
22
 
23
23
  # we register to the collector after receiving the pipeline queue
24
- LogStash::Instrument::Collector.instance.add_observer(self)
24
+ metric.collector.add_observer(self)
25
25
 
26
26
  # Keep this plugin thread alive,
27
27
  # until we shutdown the metric pipeline
@@ -30,7 +30,7 @@ module LogStash module Inputs
30
30
 
31
31
  def stop
32
32
  @logger.debug("Metrics input: stopped")
33
- LogStash::Instrument::Collector.instance.delete_observer(self)
33
+ metric.collector.delete_observer(self)
34
34
  end
35
35
 
36
36
  def update(snapshot)
@@ -8,7 +8,7 @@ require "singleton"
8
8
  require "thread"
9
9
 
10
10
  module LogStash module Instrument
11
- # The Collector singleton is the single point of reference for all
11
+ # The Collector is the single point of reference for all
12
12
  # the metrics collection inside logstash, the metrics library will make
13
13
  # direct calls to this class.
14
14
  #
@@ -17,7 +17,6 @@ module LogStash module Instrument
17
17
  class Collector
18
18
  include LogStash::Util::Loggable
19
19
  include Observable
20
- include Singleton
21
20
 
22
21
  SNAPSHOT_ROTATION_TIME_SECS = 1 # seconds
23
22
  SNAPSHOT_ROTATION_TIMEOUT_INTERVAL_SECS = 10 * 60 # seconds
@@ -59,10 +58,6 @@ module LogStash module Instrument
59
58
  end
60
59
  end
61
60
 
62
- def clear
63
- @metric_store = MetricStore.new
64
- end
65
-
66
61
  # Monitor the `Concurrent::TimerTask` this update is triggered on every successful or not
67
62
  # run of the task, TimerTask implement Observable and the collector acts as
68
63
  # the observer and will keep track if something went wrong in the execution.
@@ -96,6 +91,10 @@ module LogStash module Instrument
96
91
  @snapshot_task.execute
97
92
  end
98
93
 
94
+ def stop
95
+ @snapshot_task.shutdown
96
+ end
97
+
99
98
  # Create a snapshot of the MetricStore and send it to to the registered observers
100
99
  # The observer will receive the following signature in the update methode.
101
100
  #
@@ -13,7 +13,7 @@ module LogStash module Instrument
13
13
  class Metric
14
14
  attr_reader :collector
15
15
 
16
- def initialize(collector = LogStash::Instrument::Collector.instance)
16
+ def initialize(collector)
17
17
  @collector = collector
18
18
  end
19
19
 
@@ -110,6 +110,60 @@ module LogStash module Instrument
110
110
  key_paths.reduce(get(*key_paths)) {|acc, p| acc[p]}
111
111
  end
112
112
 
113
+
114
+ # Return a hash including the values of the keys given at the path given
115
+ #
116
+ # Example Usage:
117
+ # extract_metrics(
118
+ # [:jvm, :process],
119
+ # :open_file_descriptors,
120
+ # [:cpu, [:total_in_millis, :percent]]
121
+ # [:pipelines, [:one, :two], :size]
122
+ # )
123
+ #
124
+ # Returns:
125
+ # # From the jvm.process metrics namespace
126
+ # {
127
+ # :open_file_descriptors => 123
128
+ # :cpu => { :total_in_millis => 456, :percent => 789 }
129
+ # :pipelines => {
130
+ # :one => {:size => 90210},
131
+ # :two => {:size => 8675309}
132
+ # }
133
+ # }
134
+ def extract_metrics(path, *keys)
135
+ keys.reduce({}) do |acc,k|
136
+ # Simplifiy 1-length keys
137
+ k = k.first if k.is_a?(Array) && k.size == 1
138
+
139
+ # If we have array values here we need to recurse
140
+ # There are two levels of looping here, one for the paths we might pass in
141
+ # one for the upcoming keys we might pass in
142
+ if k.is_a?(Array)
143
+ # We need to build up future executions to extract_metrics
144
+ # which means building up the path and keys arguments.
145
+ # We need a nested loop her to execute all permutations of these in case we hit
146
+ # something like [[:a,:b],[:c,:d]] which produces 4 different metrics
147
+ next_paths = Array(k.first)
148
+ next_keys = Array(k[1])
149
+ rest = k[2..-1]
150
+ next_paths.each do |next_path|
151
+ # If there already is a hash at this location use that so we don't overwrite it
152
+ np_hash = acc[next_path] || {}
153
+
154
+ acc[next_path] = next_keys.reduce(np_hash) do |a,next_key|
155
+ a.merge! extract_metrics(path + [next_path], [next_key, *rest])
156
+ end
157
+ end
158
+ else # Scalar value
159
+ res = get_shallow(*path)[k]
160
+ acc[k] = res ? res.value : nil
161
+ end
162
+
163
+ acc
164
+ end
165
+ end
166
+
113
167
  # Return all the individuals Metric,
114
168
  # This call mimic a Enum's each if a block is provided
115
169
  #