diff --git a/logstash-core/lib/logstash/api/commands/stats.rb b/logstash-core/lib/logstash/api/commands/stats.rb index d3ad27f25..533d3e7e8 100644 --- a/logstash-core/lib/logstash/api/commands/stats.rb +++ b/logstash-core/lib/logstash/api/commands/stats.rb @@ -34,7 +34,8 @@ module LogStash end def pipeline - service.get_shallow(:stats, :pipelines) + stats = service.get_shallow(:stats, :pipelines) + PluginsStats.report(stats) end def memory @@ -53,7 +54,98 @@ module LogStash acc end } - end + end + + def hot_threads(options={}) + HotThreadsReport.new(self, options) + end + + class HotThreadsReport + HOT_THREADS_STACK_TRACES_SIZE_DEFAULT = 10.freeze + + def initialize(cmd, options) + @cmd = cmd + filter = { :stacktrace_size => options.fetch(:stacktrace_size, HOT_THREADS_STACK_TRACES_SIZE_DEFAULT) } + jr_dump = JRMonitor.threads.generate(filter) + @thread_dump = ::LogStash::Util::ThreadDump.new(options.merge(:dump => jr_dump)) + end + + def to_s + hash = to_hash + report = "#{I18n.t("logstash.web_api.hot_threads.title", :hostname => hash[:hostname], :time => hash[:time], :top_count => @thread_dump.top_count )} \n" + report << '=' * 80 + report << "\n" + hash[:threads].each do |thread| + thread_report = "" + thread_report = "#{I18n.t("logstash.web_api. + hot_threads.thread_title", :percent_of_cpu_time => thread[:percent_of_cpu_time], :thread_state => thread[:state], :thread_name => thread[:name])} \n" + thread_report = "#{thread[:percent_of_cpu_time]} % of of cpu usage by #{thread[:state]} thread named '#{thread[:name]}'\n" + thread_report << "#{thread[:path]}\n" if thread[:path] + thread[:traces].each do |trace| + thread_report << "\t#{trace}\n" + end + report << thread_report + report << '-' * 80 + report << "\n" + end + report + end + + def to_hash + hash = { :hostname => @cmd.hostname, :time => Time.now.iso8601, :busiest_threads => @thread_dump.top_count, :threads => [] } + @thread_dump.each do |thread_name, _hash| + thread_name, thread_path = _hash["thread.name"].split(": ") + thread = { :name => thread_name, + :percent_of_cpu_time => cpu_time_as_percent(_hash), + :state => _hash["thread.state"] + } + thread[:path] = thread_path if thread_path + traces = [] + _hash["thread.stacktrace"].each do |trace| + traces << trace + end + thread[:traces] = traces unless traces.empty? + hash[:threads] << thread + end + hash + end + + def cpu_time_as_percent(hash) + (((cpu_time(hash) / @cmd.uptime * 1.0)*10000).to_i)/100.0 + end + + def cpu_time(hash) + hash["cpu.time"] / 1000000.0 + end + end # class HotThreadsReport + + module PluginsStats + module_function + + def plugin_stats(stats, plugin_type) + # Turn the `plugins` stats hash into an array of [ {}, {}, ... ] + # This is to produce an array of data points, one point for each + # plugin instance. + return [] unless stats[:plugins].include?(plugin_type) + stats[:plugins][plugin_type].collect do |id, data| + { :id => id }.merge(data) + end + end + + def report(stats) + # Only one pipeline right now. + stats = stats[:main] + + { + :events => stats[:events], + :pipeline => { + :inputs => plugin_stats(stats, :inputs), + :filters => plugin_stats(stats, :filters), + :outputs => plugin_stats(stats, :outputs) + } + } + end + end # module PluginsStats end end end