949 lines
35 KiB
JavaScript
949 lines
35 KiB
JavaScript
/*
|
|
* This file is part of Cockpit.
|
|
*
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
*
|
|
* Cockpit is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Cockpit is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import $ from "jquery";
|
|
import cockpit from "cockpit";
|
|
import { journal } from "journal";
|
|
import moment from "moment";
|
|
import { init_reporting } from "./reporting.jsx";
|
|
|
|
import ReactDOM from 'react-dom';
|
|
import React from 'react';
|
|
import { EmptyStatePanel } from "cockpit-components-empty-state.jsx";
|
|
|
|
import "../../node_modules/@patternfly/react-styles/css/components/Alert/alert.css";
|
|
|
|
$(function() {
|
|
cockpit.translate();
|
|
const _ = cockpit.gettext;
|
|
|
|
var update_services_list = true;
|
|
var current_services = new Set();
|
|
|
|
var problems_client = cockpit.dbus('org.freedesktop.problems', { superuser: "try" });
|
|
var service = problems_client.proxy('org.freedesktop.Problems2', '/org/freedesktop/Problems2');
|
|
var problems = problems_client.proxies('org.freedesktop.Problems2.Entry', '/org/freedesktop/Problems2/Entry');
|
|
|
|
// A map of ABRT's problems items and it's callback for rendering
|
|
var problem_render_callbacks = {
|
|
core_backtrace: render_backtrace,
|
|
os_info: render_table_eq,
|
|
environ: render_table_eq,
|
|
limits: render_limits,
|
|
cgroup: render_cgroup,
|
|
namespaces: render_table_co,
|
|
maps: render_maps,
|
|
dso_list: render_dso_list,
|
|
mountinfo: render_mountinfo,
|
|
proc_pid_status: render_table_co,
|
|
open_fds: render_open_fds,
|
|
var_log_messages: render_multiline,
|
|
'not-reportable': render_multiline,
|
|
exploitable: render_multiline,
|
|
suspend_stats: render_table_co,
|
|
dmesg: render_multiline,
|
|
container_rootfs: render_multiline,
|
|
docker_inspect: render_multiline
|
|
};
|
|
|
|
var problem_info_1 = ['reason', 'cmdline', 'executable', 'package', 'component',
|
|
'crash_function', 'pid', 'pwd', 'hostname', 'count',
|
|
'type', 'analyzer', 'rootdir', 'duphash', 'exception_type',
|
|
'container', 'container_uuid', 'container_cmdline',
|
|
'container_id', 'container_image'];
|
|
|
|
var problem_info_2 = ['Directory', 'username', 'abrt_version', 'architecture', 'global_pid', 'kernel',
|
|
'last_occurrence', 'os_release', 'pkg_fingerprint', 'pkg_vendor',
|
|
'runlevel', 'tid', 'time', 'uid', 'uuid'];
|
|
|
|
var displayable_problems = {};
|
|
|
|
// Get list of all problems that can be displayed
|
|
var find_problems = function () {
|
|
var r = $.Deferred();
|
|
problems.wait(function() {
|
|
try {
|
|
service.GetProblems(0, {})
|
|
.done(function(problem_paths, options) {
|
|
update_problems(problem_paths);
|
|
r.resolve();
|
|
});
|
|
} catch (err) {
|
|
// ABRT is not installed. Suggest installing?
|
|
r.resolve();
|
|
}
|
|
});
|
|
return r;
|
|
};
|
|
|
|
function update_problems(problem_paths) {
|
|
for (var i in problem_paths) {
|
|
var p = problems[problem_paths[i]];
|
|
displayable_problems[p.ID] = { count: p.Count, problem_path: p.path };
|
|
displayable_problems[p.UUID] = { count: p.Count, problem_path: p.path };
|
|
displayable_problems[p.Duphash] = { count: p.Count, problem_path: p.path };
|
|
}
|
|
}
|
|
|
|
function manage_start_box(loading, show_icon, title, text, action, onAction) {
|
|
ReactDOM.render(
|
|
React.createElement(EmptyStatePanel, {
|
|
loading: loading,
|
|
showIcon: show_icon,
|
|
title: title,
|
|
paragraph: text,
|
|
action: action,
|
|
onAction: onAction,
|
|
}),
|
|
document.getElementById("start-box"));
|
|
}
|
|
|
|
/* Not public API */
|
|
function journalbox(outer, start, match, day_box) {
|
|
var box = $('<div class="panel panel-default cockpit-log-panel" role="table">');
|
|
var start_box = $('<div class="journal-start" id="start-box" role="rowgroup">');
|
|
|
|
outer.empty().append(box, start_box);
|
|
|
|
var query_count = 5000;
|
|
var query_more = 1000;
|
|
|
|
var renderer = journal.renderer(box);
|
|
/* cache to store offsets for days */
|
|
var renderitems_day_cache = null;
|
|
var procs = [];
|
|
|
|
var loading_services = false;
|
|
|
|
function query_error(error) {
|
|
/* TODO: blank slate */
|
|
console.warn(cockpit.message(error));
|
|
}
|
|
|
|
function prepend_entries(entries) {
|
|
for (var i = 0; i < entries.length; i++) {
|
|
renderer.prepend(entries[i]);
|
|
current_services.add(entries[i].SYSLOG_IDENTIFIER);
|
|
}
|
|
renderer.prepend_flush();
|
|
show_service_filters();
|
|
/* empty cache for day offsets */
|
|
renderitems_day_cache = null;
|
|
}
|
|
|
|
function append_entries(entries) {
|
|
for (var i = 0; i < entries.length; i++)
|
|
renderer.append(entries[i]);
|
|
renderer.append_flush();
|
|
/* empty cache for day offsets */
|
|
renderitems_day_cache = null;
|
|
}
|
|
|
|
function didnt_reach_start(first) {
|
|
const no_logs = document.querySelector("#journal-box .cockpit-log-panel").innerHTML === "";
|
|
manage_start_box(false, no_logs,
|
|
no_logs ? _("No Logs Found") : "",
|
|
no_logs ? _("You may try to load older entries.") : "",
|
|
_("Load earlier entries"),
|
|
() => {
|
|
var count = 0;
|
|
var stopped = null;
|
|
manage_start_box(true, true, "Loading...", "", "");
|
|
procs.push(journal.journalctl(match, { follow: false, reverse: true, cursor: first })
|
|
.fail(query_error)
|
|
.stream(function(entries) {
|
|
if (entries[0].__CURSOR == first)
|
|
entries.shift();
|
|
count += entries.length;
|
|
append_entries(entries);
|
|
if (count >= query_more) {
|
|
stopped = entries[entries.length - 1].__CURSOR;
|
|
didnt_reach_start(stopped);
|
|
this.stop();
|
|
}
|
|
})
|
|
.done(function() {
|
|
if (document.querySelector("#journal-box .cockpit-log-panel").innerHTML === "")
|
|
manage_start_box(false, true, _("No Logs Found"), _("Can not find any logs using the current combination of filters."));
|
|
else if (count < query_more)
|
|
ReactDOM.unmountComponentAtNode(document.getElementById("start-box"));
|
|
}));
|
|
});
|
|
}
|
|
|
|
function follow(cursor) {
|
|
procs.push(journal.journalctl(match, { follow: true, count: 0, cursor: cursor })
|
|
.fail(query_error)
|
|
.stream(function(entries) {
|
|
if (entries[0].__CURSOR == cursor)
|
|
entries.shift();
|
|
prepend_entries(entries);
|
|
const sb_title = document.querySelector("#start-box .pf-c-title");
|
|
if (sb_title && sb_title.innerHTML === "No Logs Found") {
|
|
ReactDOM.unmountComponentAtNode(document.getElementById("start-box"));
|
|
}
|
|
update_day_box();
|
|
}));
|
|
}
|
|
|
|
function update_day_box() {
|
|
/* Build cache if empty
|
|
*/
|
|
if (renderitems_day_cache === null) {
|
|
renderitems_day_cache = [];
|
|
for (var d = box[0].firstChild; d; d = d.nextSibling) {
|
|
if ($(d).hasClass('panel-heading'))
|
|
renderitems_day_cache.push([$(d).offset().top, $(d).text()]);
|
|
}
|
|
}
|
|
if (renderitems_day_cache.length > 0) {
|
|
/* Find the last day that begins above top
|
|
*/
|
|
var currentIndex = 0;
|
|
var top = window.scrollY;
|
|
while ((currentIndex + 1) < renderitems_day_cache.length &&
|
|
renderitems_day_cache[currentIndex + 1][0] < top) {
|
|
currentIndex++;
|
|
}
|
|
day_box.text(renderitems_day_cache[currentIndex][1]);
|
|
} else {
|
|
/* No visible day headers
|
|
*/
|
|
day_box.text(_("Go to"));
|
|
}
|
|
}
|
|
|
|
function clear_service_list() {
|
|
if (loading_services) {
|
|
$('#journal-services-list').empty()
|
|
.append($('<li>').text(_("Loading...")));
|
|
return;
|
|
}
|
|
|
|
$('#journal-services-list').empty()
|
|
.append($('<li>').append($('<a>')
|
|
.text(_("All"))
|
|
.attr('data-service', "")))
|
|
.append($('<li>')
|
|
.addClass("divider"));
|
|
}
|
|
|
|
function load_service_filters(match, options) {
|
|
loading_services = true;
|
|
current_services = new Set();
|
|
var service_options = Object.assign({ output: "verbose" }, options);
|
|
var cmd = journal.build_cmd(match, service_options)[0].join(" ");
|
|
cmd += " | grep SYSLOG_IDENTIFIER= | sort -u";
|
|
cockpit.spawn(["sh", "-ec", cmd], { host: options.host, superuser: "try" })
|
|
.then(function(entries) {
|
|
entries.split("\n").forEach(function(entry) {
|
|
if (entry)
|
|
current_services.add(entry.substr(entry.indexOf('=') + 1));
|
|
});
|
|
})
|
|
.done(function () {
|
|
loading_services = false;
|
|
show_service_filters();
|
|
});
|
|
}
|
|
|
|
function show_service_filters() {
|
|
clear_service_list();
|
|
|
|
if (loading_services)
|
|
return;
|
|
|
|
// Sort and put into list
|
|
Array.from(current_services).sort((a, b) =>
|
|
a.toLowerCase().localeCompare(b.toLowerCase())
|
|
)
|
|
.forEach(function(unit) {
|
|
$('#journal-services-list').append(
|
|
$('<li>').append($('<a>')
|
|
.text(unit)
|
|
.attr('data-service', unit)));
|
|
});
|
|
}
|
|
|
|
manage_start_box(true, true, "Loading...", "", "");
|
|
|
|
$('#journal-service-menu').on("click", "a", function() {
|
|
update_services_list = false;
|
|
cockpit.location.go([], $.extend(cockpit.location.options, { tag: $(this).attr('data-service') }));
|
|
});
|
|
|
|
$(window).on('scroll', update_day_box);
|
|
|
|
var options = {
|
|
follow: false,
|
|
reverse: true
|
|
};
|
|
|
|
var last = null;
|
|
var count = 0;
|
|
var oldest = null;
|
|
var stopped = false;
|
|
|
|
var all = false;
|
|
if (start == 'boot') {
|
|
options.boot = null;
|
|
} else if (start == 'previous-boot') {
|
|
options.boot = "-1";
|
|
last = 1; // Do not try to get newer logs
|
|
} else if (start == 'last-24h') {
|
|
options.since = "-1days";
|
|
} else if (start == 'last-week') {
|
|
options.since = "-7days";
|
|
} else {
|
|
all = true;
|
|
}
|
|
|
|
var tags_match = [];
|
|
match.forEach(function (field) {
|
|
if (!field.startsWith("SYSLOG_IDENTIFIER"))
|
|
tags_match.push(field);
|
|
});
|
|
|
|
if (update_services_list) {
|
|
clear_service_list();
|
|
load_service_filters(tags_match, options);
|
|
}
|
|
|
|
procs.push(journal.journalctl(match, options)
|
|
.fail(query_error)
|
|
.stream(function(entries) {
|
|
if (!last) {
|
|
last = entries[0].__CURSOR;
|
|
follow(last);
|
|
update_day_box();
|
|
}
|
|
count += entries.length;
|
|
append_entries(entries);
|
|
oldest = entries[entries.length - 1].__CURSOR;
|
|
if (count >= query_count) {
|
|
stopped = true;
|
|
didnt_reach_start(oldest);
|
|
this.stop();
|
|
}
|
|
})
|
|
.done(function() {
|
|
if (document.querySelector("#journal-box .cockpit-log-panel").innerHTML === "")
|
|
manage_start_box(false, true, _("No Logs Found"), _("Can not find any logs using the current combination of filters."));
|
|
else if (count < query_count)
|
|
ReactDOM.unmountComponentAtNode(document.getElementById("start-box"));
|
|
if (!last) {
|
|
procs.push(journal.journalctl(match, {
|
|
follow: true, count: 0,
|
|
boot: options.boot,
|
|
since: options.since
|
|
})
|
|
.fail(query_error)
|
|
.stream(function(entries) {
|
|
prepend_entries(entries);
|
|
const sb_title = document.querySelector("#start-box .pf-c-title");
|
|
if (sb_title && sb_title.innerHTML === "No Logs Found") {
|
|
ReactDOM.unmountComponentAtNode(document.getElementById("start-box"));
|
|
}
|
|
update_day_box();
|
|
}));
|
|
}
|
|
if (!all || stopped)
|
|
didnt_reach_start(oldest);
|
|
}));
|
|
|
|
outer.stop = function stop() {
|
|
$(window).off('scroll', update_day_box);
|
|
$.each(procs, function(i, proc) {
|
|
proc.stop();
|
|
});
|
|
};
|
|
|
|
return outer;
|
|
}
|
|
|
|
var filler;
|
|
|
|
function stop_query() {
|
|
if (filler)
|
|
filler.stop();
|
|
}
|
|
|
|
function update_query() {
|
|
stop_query();
|
|
|
|
var match = [];
|
|
|
|
var query_prio = cockpit.location.options.prio || "3";
|
|
var prio_level = parseInt(query_prio, 10);
|
|
|
|
// Set selected item into priority dropdown menu
|
|
var all_prios = document.getElementById('prio-lists').childNodes;
|
|
var item;
|
|
for (var j = 0; j < all_prios.length; j++) {
|
|
if (all_prios[j].nodeName === 'LI') {
|
|
item = all_prios[j].childNodes[0];
|
|
if (item.getAttribute('data-prio') === query_prio) {
|
|
$('#journal-prio').text(item.text);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isNaN(prio_level)) {
|
|
for (var i = 0; i <= prio_level; i++)
|
|
match.push('PRIORITY=' + i.toString());
|
|
}
|
|
|
|
var options = cockpit.location.options;
|
|
if (options.service)
|
|
match.push('_SYSTEMD_UNIT=' + options.service);
|
|
else if (options.tag)
|
|
match.push('SYSLOG_IDENTIFIER=' + options.tag);
|
|
$('#journal-service').text(options.tag || _("All"));
|
|
|
|
var query_start = cockpit.location.options.start || "recent";
|
|
if (query_start == 'recent')
|
|
$(window).scrollTop($(document).height());
|
|
|
|
journalbox($("#journal-box"), query_start, match, $('#journal-current-day'));
|
|
}
|
|
|
|
function update_entry() {
|
|
var cursor = cockpit.location.path[0];
|
|
var out = $('#journal-entry-fields');
|
|
|
|
const reportingTable = document.getElementById("journal-entry-reporting-table");
|
|
if (reportingTable != null) {
|
|
reportingTable.remove();
|
|
}
|
|
|
|
out.empty();
|
|
|
|
function show_entry(entry) {
|
|
var id;
|
|
if (entry.SYSLOG_IDENTIFIER)
|
|
id = entry.SYSLOG_IDENTIFIER;
|
|
else if (entry._SYSTEMD_UNIT)
|
|
id = entry._SYSTEMD_UNIT;
|
|
else
|
|
id = _("Journal entry");
|
|
|
|
var is_problem = false;
|
|
if (id === 'abrt-notification') {
|
|
is_problem = true;
|
|
id = entry.PROBLEM_BINARY;
|
|
}
|
|
|
|
$('#journal-entry-heading').text(id);
|
|
|
|
const crumb = $("#journal-entry-crumb");
|
|
const date = moment(new Date(entry.__REALTIME_TIMESTAMP / 1000));
|
|
|
|
if (is_problem) {
|
|
crumb.text(cockpit.format(_("$0: crash at $1"), id, date.format("YYYY-MM-DD HH:mm:ss")));
|
|
|
|
find_problems().done(function() {
|
|
create_problem(out, entry);
|
|
});
|
|
} else {
|
|
crumb.text(cockpit.format(_("Entry at $0"), date.format("YYYY-MM-DD HH:mm:ss")));
|
|
|
|
create_entry(out, entry);
|
|
}
|
|
}
|
|
|
|
function show_error(error) {
|
|
out.append(
|
|
$('<tr>').append(
|
|
$('<td>')
|
|
.text(error)));
|
|
}
|
|
|
|
journal.journalctl({ cursor: cursor, count: 1, follow: false })
|
|
.done(function (entries) {
|
|
if (entries.length >= 1 && entries[0].__CURSOR == cursor)
|
|
show_entry(entries[0]);
|
|
else
|
|
show_error(_("Journal entry not found"));
|
|
})
|
|
.fail(function (error) {
|
|
show_error(error);
|
|
});
|
|
}
|
|
|
|
function create_message_row(entry) {
|
|
const reasonColumn = document.createElement("th");
|
|
reasonColumn.setAttribute("colspan", 2);
|
|
reasonColumn.setAttribute("id", "journal-entry-message");
|
|
reasonColumn.appendChild(document.createTextNode(journal.printable(entry.MESSAGE)));
|
|
|
|
const reason = document.createElement("tr");
|
|
reason.appendChild(reasonColumn);
|
|
|
|
return reason;
|
|
}
|
|
|
|
function create_entry(out, entry) {
|
|
out.append(create_message_row(entry));
|
|
|
|
var keys = Object.keys(entry).sort();
|
|
$.each(keys, function (i, key) {
|
|
if (key !== 'MESSAGE') {
|
|
out.append(
|
|
$('<tr>').append(
|
|
$('<td>').css('text-align', 'right')
|
|
.text(key),
|
|
$('<td>').css('text-align', 'left')
|
|
.text(journal.printable(entry[key]))));
|
|
}
|
|
});
|
|
}
|
|
|
|
function create_problem(out, entry) {
|
|
var problem = null;
|
|
var all_p = [entry.PROBLEM_DIR, entry.PROBLEM_DUPHASH, entry.PROBLEM_UUID];
|
|
for (var i = 0; i < all_p.length; i++) {
|
|
if (all_p[i] in displayable_problems) {
|
|
problem = problems[displayable_problems[all_p[i]].problem_path];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Display unknown problems as standard logs
|
|
// unknown problem = deleted problem | problem of different user
|
|
if (problem === null) {
|
|
create_entry(out, entry);
|
|
return;
|
|
}
|
|
|
|
function switch_tab(new_tab, new_content) {
|
|
out.find('li').removeClass('active');
|
|
new_tab.addClass('active');
|
|
out.find('tbody.tab').first()
|
|
.replaceWith(new_content);
|
|
}
|
|
|
|
const heading = document.createElement("h3");
|
|
heading.appendChild(document.createTextNode(_("Extended Information")));
|
|
|
|
const caption = document.createElement("caption");
|
|
caption.appendChild(heading);
|
|
|
|
var ge_t = $('<li class="active">').append($('<a tabindex="0">').append($('<span translate="yes">').text(_("General"))));
|
|
var pi_t = $('<li>').append($('<a tabindex="0">').append($('<span translate="yes">').text(_("Problem info"))));
|
|
var pd_t = $('<li>').append($('<a tabindex="0">').append($('<span translate="yes">').text(_("Problem details"))));
|
|
|
|
var ge = $('<tbody>').addClass('tab');
|
|
var pi = $('<tbody>').addClass('tab');
|
|
var pd = $('<tbody>').addClass('tab')
|
|
.append(
|
|
$('<tr>').append($('<div class="panel-group" id="accordion-markup">')));
|
|
|
|
var tab = $('<ul class="nav nav-tabs nav-tabs-pf">')
|
|
.attr("id", "problem-navigation");
|
|
|
|
var d_btn = $('<button class="btn btn-danger problem-btn btn-delete pficon pficon-delete">');
|
|
|
|
const reportingTable = document.createElement("div");
|
|
reportingTable.setAttribute("id", "journal-entry-reporting-table");
|
|
|
|
const journalTable = document.getElementById("journal-entry-fields");
|
|
journalTable.insertAdjacentElement("beforebegin", reportingTable);
|
|
|
|
init_reporting(problem, reportingTable);
|
|
|
|
ge_t.click(function() {
|
|
switch_tab(ge_t, ge);
|
|
});
|
|
|
|
pi_t.click(function() {
|
|
switch_tab(pi_t, pi);
|
|
});
|
|
|
|
pd_t.click(function() {
|
|
switch_tab(pd_t, pd);
|
|
});
|
|
|
|
d_btn.click(function() {
|
|
service.DeleteProblems([problem.path]);
|
|
displayable_problems = { };
|
|
find_problems().done(function() {
|
|
cockpit.location.go('/');
|
|
});
|
|
});
|
|
|
|
// write into general tab non-ABRT related items
|
|
var keys = Object.keys(entry).sort();
|
|
$.each(keys, function(i, key) {
|
|
if (key !== 'MESSAGE' && key.indexOf('PROBLEM_') !== 0) {
|
|
ge.append(
|
|
$('<tr>').append(
|
|
$('<td>').css('text-align', 'right')
|
|
.text(key),
|
|
$('<td>').css('text-align', 'left')
|
|
.text(journal.printable(entry[key]))));
|
|
}
|
|
});
|
|
|
|
tab.html(ge_t);
|
|
tab.append(pi_t);
|
|
tab.append(pd_t);
|
|
tab.append(d_btn);
|
|
|
|
var header = $('<tr>').append(
|
|
$('<th colspan=2>').append(tab));
|
|
|
|
out.html(header).append(create_message_row(entry));
|
|
out.append(ge);
|
|
out.prepend(caption);
|
|
out.css("margin-bottom", "0px");
|
|
|
|
create_problem_details(problem, pi, pd);
|
|
}
|
|
|
|
function create_problem_details(problem, pi, pd) {
|
|
service.GetProblemData(problem.path).done(function(args, options) {
|
|
var i, elem, val;
|
|
// Render first column of problem info
|
|
var c1 = $('<table>').css('display', 'inline-block')
|
|
.css('padding-right', '200px')
|
|
.css('vertical-align', 'top')
|
|
.addClass('info-table-ct');
|
|
pi.append(c1);
|
|
for (i = 0; i < problem_info_1.length; i++) {
|
|
elem = problem_info_1[i];
|
|
if (elem in args) {
|
|
val = args[elem][2];
|
|
c1.append(
|
|
$('<tr>').append(
|
|
$('<td>').css('text-align', 'right')
|
|
.text(elem),
|
|
$('<td>').css('text-align', 'left')
|
|
.text(String(val))));
|
|
}
|
|
}
|
|
|
|
// Render second column of problem info
|
|
var c2 = $('<table>').css('display', 'inline-block')
|
|
.css('vertical-align', 'top')
|
|
.addClass('info-table-ct');
|
|
pi.append(c2);
|
|
for (i = 0; i < problem_info_2.length; i++) {
|
|
elem = problem_info_2[i];
|
|
if (elem in args) {
|
|
val = args[elem][2];
|
|
// Display date properly
|
|
if (['last_occurrence', 'time'].indexOf(elem) !== -1) {
|
|
var d = new Date(val / 1000);
|
|
val = d.toString();
|
|
}
|
|
c2.append(
|
|
$('<tr>').append(
|
|
$('<td>').css('text-align', 'right')
|
|
.text(elem),
|
|
$('<td>').css('text-align', 'left')
|
|
.text(String(val))));
|
|
}
|
|
}
|
|
|
|
// Render problem details
|
|
var problem_details_elems = Object.keys(problem_render_callbacks);
|
|
$.each(problem_details_elems, function(i, key) {
|
|
if (key in args) {
|
|
val = problem_render_callbacks[key](args[key]);
|
|
$('.panel-group', pd).append(
|
|
$('<div class="panel panel-default">')
|
|
.css("border-width", "0px 0px 2px 0px")
|
|
.css("margin-bottom", "0px")
|
|
.append(
|
|
$('<div class="panel-heading problem-panel">')
|
|
.attr('data-toggle', 'collapse')
|
|
.attr('data-target', '#' + key)
|
|
.attr('data-parent', '#accordion-markup')
|
|
.append($('<h4 class="panel-title">')
|
|
.append($('<a tabindex="0" class="accordion-toggle">')
|
|
.text(key))),
|
|
$('<div class="panel-collapse collapse">')
|
|
.attr('id', key)
|
|
.append(
|
|
$('<div class="panel-body">')
|
|
.html(val))));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function render_table_eq(orig) {
|
|
return render_table(orig, '=');
|
|
}
|
|
|
|
function render_table_co(orig) {
|
|
return render_table(orig, ':');
|
|
}
|
|
|
|
function render_table(orig, delimiter) {
|
|
var lines = orig[2].split('\n');
|
|
var result = '<table class="detail_table">';
|
|
|
|
for (var i = 0; i < lines.length - 1; i++) {
|
|
var line = lines[i].split(delimiter);
|
|
result += '<tr> <td class="text-right">' + line[0];
|
|
result += '<td class="text-left">' + line[1];
|
|
result += '</tr>';
|
|
}
|
|
|
|
result += '</table>';
|
|
return result;
|
|
}
|
|
|
|
function render_multiline(orig) {
|
|
var rendered = orig[2].replace(/\n/g, '<br>');
|
|
return rendered;
|
|
}
|
|
|
|
function render_multitable(orig, delimiter) {
|
|
var rendered = orig.replace(RegExp(delimiter, 'g'), '</td><td>');
|
|
rendered = rendered.replace(/\n/g, '</td></tr><tr><td>');
|
|
return '<table class="detail_table"><tr><td>' + rendered + '</td></tr></table>';
|
|
}
|
|
|
|
function render_dso_list(orig) {
|
|
var rendered = orig[2].replace(/^(\S+\s+)(\S+)(.*)$/gm, '$1<b>$2</b>$3');
|
|
return render_multitable(rendered, ' ');
|
|
}
|
|
|
|
function render_open_fds(orig) {
|
|
var lines = orig[2].split('\n');
|
|
for (var i = 0; i < lines.length - 1; i++) {
|
|
if (i % 5 !== 0) {
|
|
lines[i] = ':' + lines[i];
|
|
}
|
|
}
|
|
return render_multitable(lines.join('\n'), ':');
|
|
}
|
|
|
|
function render_cgroup(orig) {
|
|
return render_multitable(orig[2], ':');
|
|
}
|
|
|
|
function render_mountinfo(orig) {
|
|
return render_multitable(orig[2].replace(/ +/g, ':'), ' ');
|
|
}
|
|
|
|
function render_maps(orig) {
|
|
return render_multitable(orig[2].replace(/ +/g, ':'), ' ');
|
|
}
|
|
|
|
function render_limits(orig) {
|
|
var lines = orig[2].split('\n');
|
|
lines[0] = '":' + lines[0].replace(/(\S+) (\S+) /g, '$1:$2 ');
|
|
for (var i = 1; i < lines.length - 1; i++) {
|
|
lines[i] = lines[i].replace(/ +/g, ':');
|
|
}
|
|
|
|
return render_multitable(lines.join('\n'), ':');
|
|
}
|
|
|
|
function render_backtrace(content) {
|
|
var content_json = JSON.parse(content[2]);
|
|
|
|
var crash_thread = null;
|
|
var other_threads = [];
|
|
var other_items = {};
|
|
|
|
for (var item in content_json) {
|
|
if (item === 'stacktrace') {
|
|
var threads = content_json[item];
|
|
for (var thread_key in threads) {
|
|
var thread = threads[thread_key];
|
|
|
|
if (thread.crash_thread) {
|
|
if (thread.frames) {
|
|
crash_thread = thread.frames;
|
|
}
|
|
} else {
|
|
if (thread.frames) {
|
|
other_threads.push(thread.frames);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
other_items[item] = content_json[item];
|
|
}
|
|
}
|
|
return create_detail_from_parsed_core_backtrace(crash_thread, other_threads, other_items);
|
|
}
|
|
|
|
function create_detail_from_parsed_core_backtrace(crash_thread, other_threads, other_items) {
|
|
var detail_content = '';
|
|
for (var item in other_items) {
|
|
detail_content += item;
|
|
detail_content += ': ' + other_items[item] + " ";
|
|
}
|
|
|
|
detail_content += create_table_from_thread(crash_thread);
|
|
|
|
if (other_threads.length !== 0) {
|
|
detail_content += '<div id="other_threads_btn_div"><button class="btn btn-default other-threads-btn" title="">Show all threads</button></div>';
|
|
detail_content += '<div class="hidden other_threads">';
|
|
|
|
var thread_num = 1;
|
|
for (var thread_key in other_threads) {
|
|
detail_content += '\n';
|
|
detail_content += 'thread: ' + thread_num++ + '\n';
|
|
detail_content += create_table_from_thread(other_threads[thread_key]);
|
|
}
|
|
detail_content += '</div>';
|
|
}
|
|
|
|
return detail_content;
|
|
}
|
|
|
|
function create_table_from_thread(thread) {
|
|
var all_keys = get_all_keys_from_frames(thread);
|
|
|
|
/* create table legend */
|
|
var table = '<table class="detail_table"><thead><tr><th>Fr #</th>';
|
|
for (var key in all_keys) {
|
|
table += '<th>';
|
|
table += all_keys[key].replace(/_/g, ' ');
|
|
table += '</th>';
|
|
}
|
|
table += '</tr></thead><tbody>';
|
|
|
|
var frame_num = 1;
|
|
for (var frame_key in thread) {
|
|
table += '<tr>';
|
|
table += '<td>';
|
|
table += frame_num++;
|
|
table += '</td>';
|
|
|
|
var frame = thread[frame_key];
|
|
for (var key_key in all_keys) {
|
|
key = all_keys[key_key];
|
|
|
|
var title = '';
|
|
var row_content = '';
|
|
if (key in frame) {
|
|
row_content = frame[key].toString();
|
|
if (row_content.length > 8)
|
|
title = row_content;
|
|
} else
|
|
row_content = '';
|
|
|
|
table += '<td title="' + title + '">';
|
|
table += row_content;
|
|
table += '</td>';
|
|
}
|
|
table += '</tr>';
|
|
}
|
|
|
|
table += '</tbody></table>';
|
|
return table;
|
|
}
|
|
|
|
function get_all_keys_from_frames(thread) {
|
|
var all_keys = [];
|
|
|
|
for (var frame_key in thread) {
|
|
var frame = thread[frame_key];
|
|
var keys = Object.keys(frame);
|
|
|
|
for (var key in keys) {
|
|
if (all_keys.indexOf(keys[key]) === -1)
|
|
all_keys.push(keys[key]);
|
|
}
|
|
}
|
|
|
|
/* order keys */
|
|
var desired_ordered_of_keys = ['function_name', 'file_name', 'address', 'build_id', 'build_id_offset'];
|
|
|
|
var all_ordered_keys = [];
|
|
|
|
for (var key_key in desired_ordered_of_keys) {
|
|
var in_key = desired_ordered_of_keys[key_key];
|
|
var key_index = all_keys.indexOf(in_key);
|
|
if (key_index !== -1) {
|
|
all_ordered_keys.push(in_key);
|
|
delete all_keys[key_index];
|
|
}
|
|
}
|
|
|
|
for (key_key in all_keys) {
|
|
all_ordered_keys.push(all_keys[key_key]);
|
|
}
|
|
|
|
return all_ordered_keys;
|
|
}
|
|
|
|
function update() {
|
|
var path = cockpit.location.path;
|
|
if (path.length === 0) {
|
|
$("#journal-entry").hide();
|
|
update_query();
|
|
$("#journal").show();
|
|
} else if (path.length == 1) {
|
|
stop_query();
|
|
$("#journal").hide();
|
|
update_entry();
|
|
$("#journal-entry").show();
|
|
} else { /* redirect */
|
|
console.warn("not a journal location: " + path);
|
|
cockpit.location = '';
|
|
}
|
|
$("body").show();
|
|
}
|
|
|
|
$(cockpit).on("locationchanged", function() {
|
|
update_services_list = true;
|
|
update();
|
|
});
|
|
|
|
$('#journal-current-day-menu a').on('click', function() {
|
|
update_services_list = true;
|
|
cockpit.location.go([], $.extend(cockpit.location.options, { start: $(this).attr("data-op") }));
|
|
});
|
|
|
|
$('#journal-box').on('click', '.cockpit-logline', function() {
|
|
var cursor = $(this).attr('data-cursor');
|
|
if (cursor)
|
|
cockpit.location.go([cursor], { parent_options: JSON.stringify(cockpit.location.options) });
|
|
});
|
|
|
|
$('#journal-prio-menu a').on('click', function() {
|
|
update_services_list = true;
|
|
cockpit.location.go([], $.extend(cockpit.location.options, { prio: $(this).attr('data-prio') }));
|
|
});
|
|
|
|
$('#journal-navigate-home').on("click", function() {
|
|
if (current_services.size > 0)
|
|
update_services_list = false;
|
|
else
|
|
update_services_list = true;
|
|
|
|
var parent_options;
|
|
if (cockpit.location.options.parent_options) {
|
|
parent_options = JSON.parse(cockpit.location.options.parent_options);
|
|
}
|
|
cockpit.location.go('/', parent_options);
|
|
});
|
|
|
|
update();
|
|
});
|