ClickHouse/utils/trace-visualizer/index.html
2022-06-17 09:30:08 +02:00

160 lines
6.0 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Trace Gantt</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/d3-gantt.css">
</head>
<body>
<script language="javascript" type="text/javascript" src="js/jquery.min.js"></script>
<script language="javascript" type="text/javascript" src="js/bootstrap.min.js"></script>
<script language="javascript" type="text/javascript" src="js/d3.v4.min.js"></script>
<script language="javascript" type="text/javascript" src="js/d3-tip-0.8.0-alpha.1.js"></script>
<script language="javascript" type="text/javascript" src="js/d3-gantt.js"></script>
<div class="container-fluid" id="toolbar" style="margin:20px;margin-right:50px">
<div class="pull-right">
<button type="button" class="btn btn-primary" id="toolbar-load" data-toggle="modal" data-target="#loadModal">Load</button>
</div>
</div>
<div id="placeholder" class="chart-placeholder"></div>
<div class="modal fade" id="loadModal" tabindex="-1" role="dialog" aria-labelledby="loadModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="loadModalLabel">Load Trace JSON</h4>
</div>
<div class="modal-body">
<input type="file" id="loadFiles" value="Load" /><br />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="btnDoLoad">Load</button>
</div>
</div>
</div>
</div>
</body>
</html>
<script language="javascript">
var example_json = [
{ t1: 100, t2: 200, band: "band1", color: "#888", text: "text1" },
{ t1: 300, t2: 400, band: "band2", color: "#ff8", text: "text2" },
{ t1: 100, t2: 400, band: "band3", color: "#888", text: "some very long text with a lot of letters in it" },
{ t1: 300, t2: 400, band: "band1", color: "#8ff", text: "some_very_long_identifier_with_a_lot_of_letters_in_it" },
{ t1: 500, t2: 800, band: "band2", color: "#f8f", text: "test\nif\nnew\nline\nworks\nhere?" }
];
let chart_height = window.innerHeight - $("#placeholder")[0].getBoundingClientRect().y - 80;
var data = null;
var chart = null;
function renderChart(parsed) {
data = parsed;
chart = d3.gantt().height(chart_height).selector("#placeholder");
chart(data);
}
$("<div id='errmsg'></div>").css({
position: "absolute",
display: "none",
border: "1px solid #faa",
padding: "2px",
"background-color": "#fcc",
opacity: 0.80
}).appendTo("body");
function fetchData(dataurl, parser = x => x) {
function onDataReceived(json, textStatus, xhr) {
$("#errmsg").hide();
renderChart(parser(json));
}
function onDataError(xhr, error) {
console.log(arguments);
$("#errmsg").text("Fetch data error: " + error + (xhr.status == 200? xhr.responseText: ""))
.css({bottom: "5px", left: "25%", width: "50%"})
.fadeIn(200);
}
if (dataurl) {
$.ajax({
url: dataurl,
type: "GET",
dataType: "json",
success: function (json, textStatus, xhr) { onDataReceived(json, textStatus, xhr); },
error: onDataError
});
} else {
onDataReceived(example_json, "textStatus", "xhr");
}
}
$("#btnDoLoad").click(function(){
let element = document.getElementById('loadFiles');
let files = element.files;
if (files.length <= 0) {
return false;
}
let fr = new FileReader();
fr.onload = function(e) {
$("#errmsg").hide();
renderChart(parseClickHouseTrace(JSON.parse(e.target.result)));
}
fr.readAsText(files.item(0));
element.value = '';
$('#loadModal').modal('hide');
});
function parseClickHouseTrace(json) {
let min_time_us = Number.MAX_VALUE;
for (let i = 0; i < json.data.length; i++) {
let span = json.data[i];
min_time_us = Math.min(min_time_us, +span.start_time_us);
}
let max_time_ms = 0;
function convertTime(us) {
let value = (us - min_time_us) / 1000;
max_time_ms = Math.max(max_time_ms, value);
return value;
}
function strHash(str) {
var hash = 0;
if (str.length === 0)
return hash;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash) + str.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
if (hash < 0)
hash = -hash;
return hash;
}
let result = [];
for (let i = 0; i < json.data.length; i++) {
let span = json.data[i];
result.push({
t1: convertTime(+span.start_time_us),
t2: convertTime(+span.finish_time_us),
band: Object.values(span.group).join(' '),
color: d3.interpolateRainbow((strHash(span.color) % 256) / 256),
text: span.operation_name
});
}
chart.timeDomain([0, max_time_ms]);
return result;
}
fetchData(); // do not fetch, just draw example_json w/o parsing
//fetchData("your-traces.json", parseClickHouseTrace);
</script>