diff options
author | Andrew Cholakian <andrew@andrewvc.com> | 2017-07-07 17:24:42 -0500 |
---|---|---|
committer | Andrew Cholakian <andrew@andrewvc.com> | 2017-07-24 16:38:18 +0000 |
commit | 18112832508dc8abf771806f08dfba9cf4c7b8a3 (patch) | |
tree | 3449ca6accd373d0e163d78d2191abf591b774b2 | |
parent | bff61d59feff97fac6e111dfa04765bc689d580a (diff) |
Fix bug where hot threads did not work with duplicate names / add thread_id
The hot threads endpoint used to require thread names to be unique in a map and would crash otherwise. This patch fixes this by internally using a List instead of a Map to store these thread ids.
Additionally, this patch adds the `thread_id` to the response to allow for disambiguation of of threads for any client applications.
Fixes https://github.com/elastic/logstash/issues/7608
Fixes #7617
5 files changed, 23 insertions, 11 deletions
diff --git a/logstash-core/lib/logstash/api/commands/hot_threads_reporter.rb b/logstash-core/lib/logstash/api/commands/hot_threads_reporter.rb index bd28e60a..384280b2 100644 --- a/logstash-core/lib/logstash/api/commands/hot_threads_reporter.rb +++ b/logstash-core/lib/logstash/api/commands/hot_threads_reporter.rb @@ -17,7 +17,12 @@ class HotThreadsReport report << '=' * STRING_SEPARATOR_LENGTH report << "\n" hash[:threads].each do |thread| - 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" + line_str = 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], + :thread_id => thread[:thread_id]) + thread_report = "#{line_str} \n" thread_report << "#{thread[:path]}\n" if thread[:path] thread[:traces].each do |trace| thread_report << "\t#{trace}\n" @@ -31,9 +36,10 @@ class HotThreadsReport def to_hash hash = { :time => Time.now.iso8601, :busiest_threads => @thread_dump.top_count, :threads => [] } - @thread_dump.each do |thread_name, _hash| + @thread_dump.each do |_hash| thread_name, thread_path = _hash["thread.name"].split(": ") thread = { :name => thread_name, + :thread_id => _hash["thread.id"], :percent_of_cpu_time => cpu_time_as_percent(_hash), :state => _hash["thread.state"] } diff --git a/logstash-core/lib/logstash/util/thread_dump.rb b/logstash-core/lib/logstash/util/thread_dump.rb index 800e6c06..b504f3a9 100644 --- a/logstash-core/lib/logstash/util/thread_dump.rb +++ b/logstash-core/lib/logstash/util/thread_dump.rb @@ -19,12 +19,13 @@ module LogStash def each(&block) i=0 - dump.each_pair do |thread_name, _hash| + dump.each do |hash| + thread_name = hash["thread.name"] break if i >= top_count if ignore - next if idle_thread?(thread_name, _hash) + next if idle_thread?(thread_name, hash) end - block.call(thread_name, _hash) + block.call(hash) i += 1 end end diff --git a/logstash-core/locales/en.yml b/logstash-core/locales/en.yml index 3703c8e1..1f2a214f 100644 --- a/logstash-core/locales/en.yml +++ b/logstash-core/locales/en.yml @@ -81,7 +81,7 @@ en: ::: {%{hostname}} Hot threads at %{time}, busiestThreads=%{top_count}: thread_title: |- - %{percent_of_cpu_time} % of cpu usage, state: %{thread_state}, thread name: '%{thread_name}' + %{percent_of_cpu_time} % of cpu usage, state: %{thread_state}, thread name: '%{thread_name}', thread id: %{thread_id} logging: unrecognized_option: |- unrecognized option [%{option}] diff --git a/logstash-core/src/main/java/org/logstash/instrument/monitors/HotThreadsMonitor.java b/logstash-core/src/main/java/org/logstash/instrument/monitors/HotThreadsMonitor.java index d580239b..9e2572de 100644 --- a/logstash-core/src/main/java/org/logstash/instrument/monitors/HotThreadsMonitor.java +++ b/logstash-core/src/main/java/org/logstash/instrument/monitors/HotThreadsMonitor.java @@ -43,6 +43,7 @@ public final class HotThreadsMonitor { private static final String THREAD_NAME = "thread.name"; private static final String THREAD_STATE = "thread.state"; private static final String THREAD_STACKTRACE = "thread.stacktrace"; + private static final String THREAD_ID = "thread.id"; private Map<String, Object> map = new HashMap<>(); @@ -53,6 +54,7 @@ public final class HotThreadsMonitor { map.put(WAITED_COUNT, info.getWaitedCount()); map.put(WAITED_TIME, info.getWaitedTime()); map.put(THREAD_NAME, info.getThreadName()); + map.put(THREAD_ID, info.getThreadId()); map.put(THREAD_STATE, info.getThreadState().name().toLowerCase()); map.put(THREAD_STACKTRACE, stackTraceAsString(info.getStackTrace())); } @@ -71,6 +73,10 @@ public final class HotThreadsMonitor { return (String) map.get(THREAD_NAME); } + public long getThreadId() { + return (long) map.get(THREAD_ID); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/logstash-core/src/main/java/org/logstash/instrument/reports/ThreadsReport.java b/logstash-core/src/main/java/org/logstash/instrument/reports/ThreadsReport.java index 0d72a66d..25eac46e 100644 --- a/logstash-core/src/main/java/org/logstash/instrument/reports/ThreadsReport.java +++ b/logstash-core/src/main/java/org/logstash/instrument/reports/ThreadsReport.java @@ -21,13 +21,12 @@ public class ThreadsReport { * stacktrace_size - max depth of stack trace * @return A Map containing hot threads information */ - public static Map<String, Object> generate(Map<String, String> options) { + public static List<Map<String, Object>> generate(Map<String, String> options) { List<HotThreadsMonitor.ThreadReport> reports = HotThreadsMonitor.detect(options); return reports .stream() - .collect(Collectors - .toMap(HotThreadsMonitor.ThreadReport::getThreadName, - HotThreadsMonitor.ThreadReport::toMap)); + .map(HotThreadsMonitor.ThreadReport::toMap) + .collect(Collectors.toList()); } @@ -35,7 +34,7 @@ public class ThreadsReport { * Generate a report with current Thread information * @return A Map containing the hot threads information */ - public static Map<String, Object> generate() { + public static List<Map<String, Object>> generate() { Map<String, String> options = new HashMap<>(); options.put("order_by", "cpu"); return generate(options); |