logstash-core 2.2.4.snapshot1

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.

Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/lib/logstash-core.rb +1 -0
  3. data/lib/logstash-core/logstash-core.rb +3 -0
  4. data/lib/logstash-core/version.rb +8 -0
  5. data/lib/logstash/agent.rb +391 -0
  6. data/lib/logstash/codecs/base.rb +50 -0
  7. data/lib/logstash/config/config_ast.rb +550 -0
  8. data/lib/logstash/config/cpu_core_strategy.rb +32 -0
  9. data/lib/logstash/config/defaults.rb +12 -0
  10. data/lib/logstash/config/file.rb +39 -0
  11. data/lib/logstash/config/grammar.rb +3503 -0
  12. data/lib/logstash/config/mixin.rb +518 -0
  13. data/lib/logstash/config/registry.rb +13 -0
  14. data/lib/logstash/environment.rb +98 -0
  15. data/lib/logstash/errors.rb +12 -0
  16. data/lib/logstash/filters/base.rb +205 -0
  17. data/lib/logstash/inputs/base.rb +116 -0
  18. data/lib/logstash/inputs/threadable.rb +18 -0
  19. data/lib/logstash/java_integration.rb +116 -0
  20. data/lib/logstash/json.rb +61 -0
  21. data/lib/logstash/logging.rb +91 -0
  22. data/lib/logstash/namespace.rb +13 -0
  23. data/lib/logstash/output_delegator.rb +172 -0
  24. data/lib/logstash/outputs/base.rb +91 -0
  25. data/lib/logstash/patches.rb +5 -0
  26. data/lib/logstash/patches/bugfix_jruby_2558.rb +51 -0
  27. data/lib/logstash/patches/cabin.rb +35 -0
  28. data/lib/logstash/patches/profile_require_calls.rb +47 -0
  29. data/lib/logstash/patches/rubygems.rb +38 -0
  30. data/lib/logstash/patches/stronger_openssl_defaults.rb +68 -0
  31. data/lib/logstash/pipeline.rb +499 -0
  32. data/lib/logstash/pipeline_reporter.rb +114 -0
  33. data/lib/logstash/plugin.rb +120 -0
  34. data/lib/logstash/program.rb +14 -0
  35. data/lib/logstash/runner.rb +124 -0
  36. data/lib/logstash/shutdown_watcher.rb +100 -0
  37. data/lib/logstash/util.rb +203 -0
  38. data/lib/logstash/util/buftok.rb +139 -0
  39. data/lib/logstash/util/charset.rb +35 -0
  40. data/lib/logstash/util/decorators.rb +52 -0
  41. data/lib/logstash/util/defaults_printer.rb +31 -0
  42. data/lib/logstash/util/filetools.rb +186 -0
  43. data/lib/logstash/util/java_version.rb +66 -0
  44. data/lib/logstash/util/password.rb +25 -0
  45. data/lib/logstash/util/plugin_version.rb +56 -0
  46. data/lib/logstash/util/prctl.rb +10 -0
  47. data/lib/logstash/util/retryable.rb +40 -0
  48. data/lib/logstash/util/socket_peer.rb +7 -0
  49. data/lib/logstash/util/unicode_trimmer.rb +81 -0
  50. data/lib/logstash/util/worker_threads_default_printer.rb +29 -0
  51. data/lib/logstash/util/wrapped_synchronous_queue.rb +41 -0
  52. data/lib/logstash/version.rb +14 -0
  53. data/locales/en.yml +204 -0
  54. data/logstash-core.gemspec +58 -0
  55. data/spec/conditionals_spec.rb +429 -0
  56. data/spec/logstash/agent_spec.rb +85 -0
  57. data/spec/logstash/config/config_ast_spec.rb +146 -0
  58. data/spec/logstash/config/cpu_core_strategy_spec.rb +123 -0
  59. data/spec/logstash/config/defaults_spec.rb +10 -0
  60. data/spec/logstash/config/mixin_spec.rb +158 -0
  61. data/spec/logstash/environment_spec.rb +56 -0
  62. data/spec/logstash/filters/base_spec.rb +251 -0
  63. data/spec/logstash/inputs/base_spec.rb +74 -0
  64. data/spec/logstash/java_integration_spec.rb +304 -0
  65. data/spec/logstash/json_spec.rb +96 -0
  66. data/spec/logstash/output_delegator_spec.rb +144 -0
  67. data/spec/logstash/outputs/base_spec.rb +40 -0
  68. data/spec/logstash/patches_spec.rb +90 -0
  69. data/spec/logstash/pipeline_reporter_spec.rb +85 -0
  70. data/spec/logstash/pipeline_spec.rb +455 -0
  71. data/spec/logstash/plugin_spec.rb +169 -0
  72. data/spec/logstash/runner_spec.rb +68 -0
  73. data/spec/logstash/shutdown_watcher_spec.rb +113 -0
  74. data/spec/logstash/util/buftok_spec.rb +31 -0
  75. data/spec/logstash/util/charset_spec.rb +74 -0
  76. data/spec/logstash/util/defaults_printer_spec.rb +50 -0
  77. data/spec/logstash/util/java_version_spec.rb +79 -0
  78. data/spec/logstash/util/plugin_version_spec.rb +64 -0
  79. data/spec/logstash/util/unicode_trimmer_spec.rb +55 -0
  80. data/spec/logstash/util/worker_threads_default_printer_spec.rb +45 -0
  81. data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +28 -0
  82. data/spec/logstash/util_spec.rb +35 -0
  83. metadata +364 -0
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+ require 'ostruct'
3
+
4
+ module LogStash; class PipelineReporter
5
+ attr_reader :logger, :pipeline
6
+
7
+ # This is an immutable copy of the pipeline state,
8
+ # It is a proxy to a hash to allow us to add methods dynamically to the hash
9
+ class Snapshot
10
+ def initialize(data)
11
+ @data = data
12
+ end
13
+
14
+ def to_hash
15
+ @data
16
+ end
17
+
18
+ def to_simple_hash
19
+ {"inflight_count" => inflight_count, "stalling_thread_info" => format_threads_by_plugin}
20
+ end
21
+
22
+ def to_str
23
+ to_simple_hash.to_s
24
+ end
25
+ alias_method :to_s, :to_str
26
+
27
+ def method_missing(meth)
28
+ @data[meth]
29
+ end
30
+
31
+ def format_threads_by_plugin
32
+ stalled_plugins = {}
33
+ stalling_threads_info.each do |thr|
34
+ key = (thr.delete("plugin") || "other")
35
+ stalled_plugins[key] ||= []
36
+ stalled_plugins[key] << thr
37
+ end
38
+ stalled_plugins
39
+ end
40
+ end
41
+
42
+ def initialize(logger,pipeline)
43
+ @logger = logger
44
+ @pipeline = pipeline
45
+ end
46
+
47
+ # The main way of accessing data from the reporter,,
48
+ # this provides a (more or less) consistent snapshot of what's going on in the
49
+ # pipeline with some extra decoration
50
+ def snapshot
51
+ Snapshot.new(self.to_hash)
52
+ end
53
+
54
+ def to_hash
55
+ pipeline.inflight_batches_synchronize do |batch_map|
56
+ worker_states_snap = worker_states(batch_map) # We only want to run this once
57
+ inflight_count = worker_states_snap.map {|s| s[:inflight_count] }.reduce(0, :+)
58
+
59
+ {
60
+ :events_filtered => events_filtered,
61
+ :events_consumed => events_consumed,
62
+ :worker_count => pipeline.worker_threads.size,
63
+ :inflight_count => inflight_count,
64
+ :worker_states => worker_states_snap,
65
+ :output_info => output_info,
66
+ :thread_info => pipeline.plugin_threads_info,
67
+ :stalling_threads_info => pipeline.stalling_threads_info
68
+ }
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def events_filtered
75
+ pipeline.events_filtered.value
76
+ end
77
+
78
+ def events_consumed
79
+ pipeline.events_consumed.value
80
+ end
81
+
82
+ def plugin_threads
83
+ pipeline.plugin_threads
84
+ end
85
+
86
+ # Not threadsafe! must be called within an `inflight_batches_synchronize` block
87
+ def worker_states(batch_map)
88
+ pipeline.worker_threads.map.with_index do |thread,idx|
89
+ status = thread.status || "dead"
90
+ inflight_count = batch_map[thread] ? batch_map[thread].size : 0
91
+ {
92
+ :status => status,
93
+ :alive => thread.alive?,
94
+ :index => idx,
95
+ :inflight_count => inflight_count
96
+ }
97
+ end
98
+ end
99
+
100
+ def output_info
101
+ pipeline.outputs.map do |output_delegator|
102
+ is_multi_worker = output_delegator.worker_count > 1
103
+
104
+ {
105
+ :type => output_delegator.config_name,
106
+ :config => output_delegator.config,
107
+ :is_multi_worker => is_multi_worker,
108
+ :events_received => output_delegator.events_received,
109
+ :workers => output_delegator.workers,
110
+ :busy_workers => output_delegator.busy_workers
111
+ }
112
+ end
113
+ end
114
+ end end
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "logstash/logging"
4
+ require "logstash/config/mixin"
5
+ require "cabin"
6
+ require "concurrent"
7
+
8
+ class LogStash::Plugin
9
+ attr_accessor :params
10
+ attr_accessor :logger
11
+
12
+ NL = "\n"
13
+
14
+ public
15
+ def hash
16
+ params.hash ^
17
+ self.class.name.hash
18
+ end
19
+
20
+ public
21
+ def eql?(other)
22
+ self.class.name == other.class.name && @params == other.params
23
+ end
24
+
25
+ public
26
+ def initialize(params=nil)
27
+ @params = LogStash::Util.deep_clone(params)
28
+ @logger = Cabin::Channel.get(LogStash)
29
+ end
30
+
31
+ # close is called during shutdown, after the plugin worker
32
+ # main task terminates
33
+ public
34
+ def do_close
35
+ @logger.debug("closing", :plugin => self)
36
+ close
37
+ end
38
+
39
+ # Subclasses should implement this close method if you need to perform any
40
+ # special tasks during shutdown (like flushing, etc.)
41
+ public
42
+ def close
43
+ # ..
44
+ end
45
+
46
+ def to_s
47
+ return "#{self.class.name}: #{@params}"
48
+ end
49
+
50
+ public
51
+ def inspect
52
+ if !@params.nil?
53
+ description = @params
54
+ .reject { |k, v| v.nil? || (v.respond_to?(:empty?) && v.empty?) }
55
+ .collect { |k, v| "#{k}=>#{v.inspect}" }
56
+ return "<#{self.class.name} #{description.join(", ")}>"
57
+ else
58
+ return "<#{self.class.name} --->"
59
+ end
60
+ end
61
+
62
+ public
63
+ def debug_info
64
+ [self.class.to_s, original_params]
65
+ end
66
+
67
+ # Look up a plugin by type and name.
68
+ public
69
+ def self.lookup(type, name)
70
+ path = "logstash/#{type}s/#{name}"
71
+
72
+ # first check if plugin already exists in namespace and continue to next step if not
73
+ begin
74
+ return namespace_lookup(type, name)
75
+ rescue NameError
76
+ logger.debug("Plugin not defined in namespace, checking for plugin file", :type => type, :name => name, :path => path)
77
+ end
78
+
79
+ # try to load the plugin file. ex.: lookup("filter", "grok") will require logstash/filters/grok
80
+ require(path)
81
+
82
+ # check again if plugin is now defined in namespace after the require
83
+ namespace_lookup(type, name)
84
+ rescue LoadError, NameError => e
85
+ raise(LogStash::PluginLoadingError, I18n.t("logstash.pipeline.plugin-loading-error", :type => type, :name => name, :path => path, :error => e.to_s))
86
+ end
87
+
88
+ private
89
+
90
+ # lookup a plugin by type and name in the existing LogStash module namespace
91
+ # ex.: namespace_lookup("filter", "grok") looks for LogStash::Filters::Grok
92
+ # @param type [String] plugin type, "input", "ouput", "filter"
93
+ # @param name [String] plugin name, ex.: "grok"
94
+ # @return [Class] the plugin class or raises NameError
95
+ # @raise NameError if plugin class does not exist or is invalid
96
+ def self.namespace_lookup(type, name)
97
+ type_const = "#{type.capitalize}s"
98
+ namespace = LogStash.const_get(type_const)
99
+ # the namespace can contain constants which are not for plugins classes (do not respond to :config_name)
100
+ # namespace.constants is the shallow collection of all constants symbols in namespace
101
+ # note that below namespace.const_get(c) should never result in a NameError since c is from the constants collection
102
+ klass_sym = namespace.constants.find { |c| is_a_plugin?(namespace.const_get(c), name) }
103
+ klass = klass_sym && namespace.const_get(klass_sym)
104
+ raise(NameError) unless klass
105
+ klass
106
+ end
107
+
108
+ # check if klass is a valid plugin for name
109
+ # @param klass [Class] plugin class
110
+ # @param name [String] plugin name
111
+ # @return [Boolean] true if klass is a valid plugin for name
112
+ def self.is_a_plugin?(klass, name)
113
+ klass.ancestors.include?(LogStash::Plugin) && klass.respond_to?(:config_name) && klass.config_name == name
114
+ end
115
+
116
+ # @return [Cabin::Channel] logger channel for class methods
117
+ def self.logger
118
+ @logger ||= Cabin::Channel.get(LogStash)
119
+ end
120
+ end # class LogStash::Plugin
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+
4
+ module LogStash::Program
5
+ public
6
+ def exit(value)
7
+ if RUBY_ENGINE == "jruby"
8
+ # Kernel::exit() in jruby just tosses an exception? Let's actually exit.
9
+ Java::java.lang.System.exit(value)
10
+ else
11
+ Kernel::exit(value)
12
+ end
13
+ end # def exit
14
+ end # module LogStash::Program
@@ -0,0 +1,124 @@
1
+ # encoding: utf-8
2
+ Thread.abort_on_exception = true
3
+ Encoding.default_external = Encoding::UTF_8
4
+ $DEBUGLIST = (ENV["DEBUG"] || "").split(",")
5
+
6
+ require "logstash/environment"
7
+
8
+ LogStash::Environment.load_locale!
9
+
10
+ require "logstash/namespace"
11
+ require "logstash/program"
12
+
13
+ class LogStash::Runner
14
+ include LogStash::Program
15
+
16
+ def main(args)
17
+ require "logstash/util"
18
+ require "logstash/util/java_version"
19
+ require "stud/trap"
20
+ require "stud/task"
21
+ @startup_interruption_trap = Stud::trap("INT") { puts "Interrupted"; exit 0 }
22
+
23
+ LogStash::Util::set_thread_name(self.class.name)
24
+
25
+ if RUBY_VERSION < "1.9.2"
26
+ $stderr.puts "Ruby 1.9.2 or later is required. (You are running: " + RUBY_VERSION + ")"
27
+ return 1
28
+ end
29
+
30
+ # Print a warning to STDERR for bad java versions
31
+ LogStash::Util::JavaVersion.warn_on_bad_java_version
32
+
33
+ Stud::untrap("INT", @startup_interruption_trap)
34
+
35
+ task = run(args)
36
+ exit(task.wait)
37
+ end # def self.main
38
+
39
+ def run(args)
40
+ command = args.shift
41
+ commands = {
42
+ "version" => lambda do
43
+ require "logstash/agent"
44
+ agent_args = ["--version"]
45
+ if args.include?("--verbose")
46
+ agent_args << "--verbose"
47
+ end
48
+ return LogStash::Agent.run($0, agent_args)
49
+ end,
50
+ "irb" => lambda do
51
+ require "irb"
52
+ return IRB.start(__FILE__)
53
+ end,
54
+ "pry" => lambda do
55
+ require "pry"
56
+ return binding.pry
57
+ end,
58
+ "docgen" => lambda do
59
+ require 'docs/asciidocgen'
60
+ opts = OptionParser.new
61
+ settings = {}
62
+ opts.on("-o DIR", "--output DIR",
63
+ "Directory to output to; optional. If not specified,"\
64
+ "we write to stdout.") do |val|
65
+ settings[:output] = val
66
+ end
67
+ args = opts.parse(ARGV)
68
+ docs = LogStashConfigAsciiDocGenerator.new
69
+ args.each do |arg|
70
+ docs.generate(arg, settings)
71
+ end
72
+ return 0
73
+ end,
74
+ "agent" => lambda do
75
+ require "logstash/agent"
76
+ # Hack up a runner
77
+ agent = LogStash::Agent.new("/bin/logstash agent", $0)
78
+ begin
79
+ agent.parse(args)
80
+ rescue Clamp::HelpWanted => e
81
+ show_help(e.command)
82
+ return 0
83
+ rescue Clamp::UsageError => e
84
+ # If 'too many arguments' then give the arguments to
85
+ # the next command. Otherwise it's a real error.
86
+ raise if e.message != "too many arguments"
87
+ remaining = agent.remaining_arguments
88
+ end
89
+
90
+ return agent.execute
91
+ end
92
+ } # commands
93
+
94
+ if commands.include?(command)
95
+ return Stud::Task.new { commands[command].call }
96
+ else
97
+ if command.nil?
98
+ $stderr.puts "No command given"
99
+ else
100
+ if !%w(--help -h help).include?(command)
101
+ # Emit 'no such command' if it's not someone asking for help.
102
+ $stderr.puts "No such command #{command.inspect}"
103
+ end
104
+ end
105
+ $stderr.puts %q[
106
+ Usage: logstash <command> [command args]
107
+ Run a command with the --help flag to see the arguments.
108
+ For example: logstash agent --help
109
+
110
+ Available commands:
111
+ agent - runs the logstash agent
112
+ version - emits version info about this logstash
113
+ ]
114
+ #$stderr.puts commands.keys.map { |s| " #{s}" }.join("\n")
115
+ return Stud::Task.new { 1 }
116
+ end
117
+ end # def run
118
+
119
+ private
120
+
121
+ def show_help(command)
122
+ puts command.help
123
+ end
124
+ end # class LogStash::Runner
@@ -0,0 +1,100 @@
1
+ # encoding: utf-8
2
+
3
+ module LogStash
4
+ class ShutdownWatcher
5
+
6
+ CHECK_EVERY = 1 # second
7
+ REPORT_EVERY = 5 # checks
8
+ ABORT_AFTER = 3 # stalled reports
9
+
10
+ attr_reader :cycle_period, :report_every, :abort_threshold
11
+
12
+ def initialize(pipeline, cycle_period=CHECK_EVERY, report_every=REPORT_EVERY, abort_threshold=ABORT_AFTER)
13
+ @pipeline = pipeline
14
+ @cycle_period = cycle_period
15
+ @report_every = report_every
16
+ @abort_threshold = abort_threshold
17
+ @reports = []
18
+ end
19
+
20
+ def self.unsafe_shutdown=(boolean)
21
+ @unsafe_shutdown = boolean
22
+ end
23
+
24
+ def self.unsafe_shutdown?
25
+ @unsafe_shutdown
26
+ end
27
+
28
+ def self.logger=(logger)
29
+ @logger = logger
30
+ end
31
+
32
+ def self.logger
33
+ @logger ||= Cabin::Channel.get(LogStash)
34
+ end
35
+
36
+ def self.start(pipeline, cycle_period=CHECK_EVERY, report_every=REPORT_EVERY, abort_threshold=ABORT_AFTER)
37
+ watcher = self.new(pipeline, cycle_period, report_every, abort_threshold)
38
+ Thread.new(watcher) { |watcher| watcher.start }
39
+ end
40
+
41
+ def logger
42
+ self.class.logger
43
+ end
44
+
45
+ def start
46
+ sleep(@cycle_period)
47
+ cycle_number = 0
48
+ stalled_count = 0
49
+ Stud.interval(@cycle_period) do
50
+ @reports << pipeline_report_snapshot
51
+ @reports.delete_at(0) if @reports.size > @report_every # expire old report
52
+ if cycle_number == (@report_every - 1) # it's report time!
53
+ logger.warn(@reports.last)
54
+
55
+ if shutdown_stalled?
56
+ logger.error("The shutdown process appears to be stalled due to busy or blocked plugins. Check the logs for more information.") if stalled_count == 0
57
+ stalled_count += 1
58
+
59
+ if self.class.unsafe_shutdown? && @abort_threshold == stalled_count
60
+ logger.fatal("Forcefully quitting logstash..")
61
+ force_exit()
62
+ break
63
+ end
64
+ else
65
+ stalled_count = 0
66
+ end
67
+ end
68
+ cycle_number = (cycle_number + 1) % @report_every
69
+ end
70
+ end
71
+
72
+ def pipeline_report_snapshot
73
+ @pipeline.reporter.snapshot
74
+ end
75
+
76
+ # A pipeline shutdown is stalled if
77
+ # * at least REPORT_EVERY reports have been created
78
+ # * the inflight event count is in monotonically increasing
79
+ # * there are worker threads running which aren't blocked on SizedQueue pop/push
80
+ # * the stalled thread list is constant in the previous REPORT_EVERY reports
81
+ def shutdown_stalled?
82
+ return false unless @reports.size == @report_every #
83
+ # is stalled if inflight count is either constant or increasing
84
+ stalled_event_count = @reports.each_cons(2).all? do |prev_report, next_report|
85
+ prev_report.inflight_count <= next_report.inflight_count
86
+ end
87
+ if stalled_event_count
88
+ @reports.each_cons(2).all? do |prev_report, next_report|
89
+ prev_report.stalling_threads == next_report.stalling_threads
90
+ end
91
+ else
92
+ false
93
+ end
94
+ end
95
+
96
+ def force_exit
97
+ exit(-1)
98
+ end
99
+ end
100
+ end