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
This commit is contained in:
Andrew Cholakian 2017-07-07 17:24:42 -05:00
parent bff61d59fe
commit 1811283250
5 changed files with 23 additions and 11 deletions

View file

@ -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"]
}

View file

@ -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

View file

@ -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}]

View file

@ -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();

View file

@ -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);