Skip to content
Snippets Groups Projects
Verified Commit b571c624 authored by Dorian Koch's avatar Dorian Koch
Browse files

Add visualize.html

parent c0dcdd10
No related branches found
No related tags found
No related merge requests found
postgres/ postgres/
*.csv
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Performance Visualization</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.7.1/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 0 auto;
padding: 20px;
}
#charts-container {
display: flex;
width: 100%;
flex-direction: column;
gap: 50px;
}
#chart-container,
#error-chart-container {
width: 100%;
height: 600px;
}
canvas {
display: block;
}
</style>
</head>
<body>
<div id="charts-container">
<div>
<h2>Latency `requests.get(url, timeout=20)`</h2>
<button id="filter60">Filter sub-60s response time</button>
<button id="filter10">Filter sub-10s response time</button>
<button id="filter1">Filter sub-1s response time</button>
<button id="show100">Show 100%</button>
<button id="show10">Show 10%</button>
<button id="show5">Show 5%</button>
<div id="chart-container">
<canvas id="performanceChart"></canvas>
</div>
</div>
<h2>Error Rate Over Time (HTTP status != 200)</h2>
<div id="error-chart-container">
<canvas id="errorRateChart"></canvas>
</div>
</div>
<script>
// Ensure Papa is loaded before running the script
function initVisualization() {
let outlierFilter = 10; // Initial state for the 10s limit
let showMod = 10;
let chart; // Reference to the chart instance
fetch('exported.csv')
.then(response => response.text())
.then(csvText => {
const parsed = Papa.parse(csvText, { header: true, dynamicTyping: true });
document.getElementById('filter60').addEventListener('click', () => {
outlierFilter = 60;
updateChart();
});
document.getElementById('filter10').addEventListener('click', () => {
outlierFilter = 10;
updateChart();
});
document.getElementById('filter1').addEventListener('click', () => {
outlierFilter = 1;
updateChart();
});
document.getElementById('show100').addEventListener('click', () => {
showMod = 1;
updateChart();
});
document.getElementById('show10').addEventListener('click', () => {
showMod = 10;
updateChart();
});
document.getElementById('show5').addEventListener('click', () => {
showMod = 20;
updateChart();
});
function updateChart() {
const filteredData = parsed.data.filter(row => row.duration <= outlierFilter);
const uniqueUrls = [...new Set(filteredData.map(row => row.url))];
const datasets = uniqueUrls.map((url, index) => {
const urlData = filteredData.filter(row => row.url === url);
const sampledData = urlData.filter((_, idx) => idx % showMod === 0);
return {
label: url,
data: sampledData.map(row => ({
x: new Date(row.timestamp),
y: row.duration
})),
borderColor: `hsl(${index * 360 / uniqueUrls.length}, 70%, 50%)`,
backgroundColor: `hsla(${index * 360 / uniqueUrls.length}, 70%, 50%, 0.1)`,
borderWidth: 1,
pointRadius: 1,
pointHoverRadius: 5
};
});
// Update the chart data
chart.data.datasets = datasets;
chart.options.plugins.tooltip = showMod > 10;
chart.update();
}
const ctx = document.getElementById('performanceChart').getContext('2d');
chart = new Chart(ctx, {
type: 'scatter',
data: {
datasets: []
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Response Time by URL Over Time'
},
tooltip: {
enabled: showMod > 10
}
},
scales: {
x: {
type: 'time',
time: {
unit: 'day'
},
title: {
display: true,
text: 'Timestamp'
}
},
y: {
title: {
display: true,
text: 'Response Time (seconds)'
},
beginAtZero: true
}
},
animation: false
}
});
// Initial chart update
updateChart();
initErrorRateChart(parsed);
})
.catch(error => console.error('Error loading CSV:', error));
}
function initErrorRateChart(parsed) {
// Aggregate error rate per hour
const aggregateErrorRate = (data) => {
const aggregatedData = {};
data.forEach(row => {
if (row.timestamp == null) {
return;
}
const hour = new Date(row.timestamp).toISOString().slice(0, 13); // Format: YYYY-MM-DDTHH
if (!aggregatedData[hour]) {
aggregatedData[hour] = {};
}
if (!aggregatedData[hour][row.url]) {
aggregatedData[hour][row.url] = { total: 0, errors: 0 };
}
aggregatedData[hour][row.url].total += 1;
if (row.status_code !== 200) {
aggregatedData[hour][row.url].errors += 1;
}
});
// Convert aggregated data into a chart-friendly format
const chartData = {};
//console.log("aggregatedData size", Object.keys(aggregatedData).length);
for (const hour in aggregatedData) {
for (const url in aggregatedData[hour]) {
if (!chartData[url]) {
chartData[url] = [];
}
const total = aggregatedData[hour][url].total;
const errors = aggregatedData[hour][url].errors;
// YYYY-MM-DDTHH
const tt = new Date(hour + ":00:00Z");
chartData[url].push({
x: tt,
y: (errors / total) * 100 // Error rate percentage
});
}
}
// sort by time
for (const url in chartData) {
chartData[url].sort((a, b) => a.x - b.x);
}
return chartData;
};
const errorRateData = aggregateErrorRate(parsed.data);
// Create datasets for the error rate chart
const errorRateDatasets = Object.keys(errorRateData).map((url, index) => ({
label: url,
data: errorRateData[url],
borderColor: `hsl(${index * 360 / Object.keys(errorRateData).length}, 70%, 50%)`,
backgroundColor: `hsla(${index * 360 / Object.keys(errorRateData).length}, 70%, 50%, 0.1)`,
borderWidth: 1,
pointRadius: 1,
pointHoverRadius: 5
}));
console.log(errorRateDatasets);
// Create the error rate chart
const errorCtx = document.getElementById('errorRateChart').getContext('2d');
new Chart(errorCtx, {
type: 'line',
data: {
datasets: errorRateDatasets
},
options: {
responsive: true,
plugins: {
title: {
display: true,
text: 'Error Rate by URL Over Time (per hour)'
}
},
scales: {
x: {
type: 'time',
time: {
unit: 'day'
},
title: {
display: true,
text: 'Timestamp'
}
},
y: {
title: {
display: true,
text: 'Error Rate (%)'
},
beginAtZero: true,
//max: 100
}
},
animation: false
}
});
}
// Wait for dependencies to load
if (window.Papa && window.Chart) {
initVisualization();
} else {
window.addEventListener('load', initVisualization);
}
</script>
</body>
</html>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment