Files
go-otel/index.html
T
mats 8f5c267444
Build and Publish / Metadata (push) Successful in 2s
Build and Publish / BuildAndDeployAmd64 (push) Successful in 1m3s
ml
2026-09-19 06:50:19 +00:00

269 lines
8.2 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OTel Debug Visualizer</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
color: #333;
text-align: center;
}
.status {
padding: 10px;
margin-bottom: 20px;
border-radius: 5px;
text-align: center;
}
.connected {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.disconnected {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.data-section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.data-section h2 {
margin-top: 0;
color: #333;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
}
.data-content {
max-height: 400px;
overflow-y: auto;
background: #f8f9fa;
padding: 10px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
white-space: pre-wrap;
}
.clear-btn {
background: #dc3545;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
.clear-btn:hover {
background: #c82333;
}
.stats {
display: flex;
justify-content: space-around;
margin-bottom: 20px;
}
.stat {
background: white;
padding: 15px;
border-radius: 8px;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
flex: 1;
margin: 0 10px;
}
.stat h3 {
margin: 0;
color: #007bff;
}
.stat p {
margin: 5px 0 0 0;
font-size: 24px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container">
<h1>OpenTelemetry Debug Visualizer</h1>
<div id="status" class="status disconnected">
Disconnected from server
</div>
<div class="stats">
<div class="stat">
<h3>Traces</h3>
<p id="traces-count">0</p>
</div>
<div class="stat">
<h3>Metrics</h3>
<p id="metrics-count">0</p>
</div>
<div class="stat">
<h3>Logs</h3>
<p id="logs-count">0</p>
</div>
</div>
<div class="data-section">
<h2>Traces</h2>
<div id="traces" class="data-content"></div>
<button class="clear-btn" onclick="clearData('traces')">
Clear Traces
</button>
</div>
<div class="data-section">
<h2>Metrics</h2>
<div id="metrics" class="data-content"></div>
<button class="clear-btn" onclick="clearData('metrics')">
Clear Metrics
</button>
</div>
<div class="data-section">
<h2>Logs</h2>
<div id="logs" class="data-content"></div>
<button class="clear-btn" onclick="clearData('logs')">
Clear Logs
</button>
</div>
<div class="data-section" style="border-left: 4px solid #10b981;">
<h2 style="border-bottom-color: #10b981; color: #065f46;">Needle 3 AI Telemetry Triage</h2>
<p style="font-size: 13px; color: #666; margin-top: 0;">Test on-device telemetry classification with Cactus-Compute/needle3:</p>
<div style="display: flex; gap: 8px; margin-bottom: 10px;">
<input id="needle-input" type="text" style="flex: 1; padding: 8px 12px; border: 1px solid #ccc; border-radius: 4px; font-family: monospace; font-size: 12px;" value="User reported client browser crash on service eventsourced-ai-web: Script error on url https://event-agent.k6n.net/#session=angry-koala-1" />
<button id="needle-btn" style="background: #10b981; color: white; border: none; padding: 8px 16px; border-radius: 4px; font-weight: bold; cursor: pointer;" onclick="classifyWithNeedle()">
Classify with Needle 3
</button>
</div>
<div id="needle-result" class="data-content" style="background: #ecfdf5; border: 1px solid #a7f3d0; min-height: 40px; color: #064e3b;">
Click "Classify with Needle 3" to test the sidecar...
</div>
</div>
</div>
<script>
async function classifyWithNeedle() {
const input = document.getElementById("needle-input").value;
const resultDiv = document.getElementById("needle-result");
const btn = document.getElementById("needle-btn");
if (!input.trim()) return;
btn.textContent = "Classifying...";
btn.disabled = true;
try {
const resp = await fetch("/api/needle/classify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: input }),
});
const data = await resp.json();
resultDiv.innerHTML = `<pre style="margin: 0; font-family: monospace;">${JSON.stringify(data, null, 2)}</pre>`;
} catch (err) {
resultDiv.textContent = "Error: " + err;
} finally {
btn.textContent = "Classify with Needle 3";
btn.disabled = false;
}
}
let ws;
let tracesCount = 0;
let metricsCount = 0;
let logsCount = 0;
function connect() {
ws = new WebSocket("ws://localhost:8080/ws");
ws.onopen = function () {
document.getElementById("status").className = "status connected";
document.getElementById("status").textContent = "Connected to server";
};
ws.onmessage = function (event) {
try {
const data = JSON.parse(event.data);
displayData(data);
} catch (e) {
console.error("Failed to parse data:", e);
}
};
ws.onclose = function () {
document.getElementById("status").className = "status disconnected";
document.getElementById("status").textContent =
"Disconnected from server - reconnecting...";
setTimeout(connect, 1000);
};
ws.onerror = function (error) {
console.error("WebSocket error:", error);
};
}
function displayData(data) {
let container;
let countElement;
if (data.resourceSpans) {
// Traces
container = document.getElementById("traces");
countElement = document.getElementById("traces-count");
tracesCount++;
countElement.textContent = tracesCount;
} else if (data.resourceMetrics) {
// Metrics
container = document.getElementById("metrics");
countElement = document.getElementById("metrics-count");
metricsCount++;
countElement.textContent = metricsCount;
} else if (data.resourceLogs) {
// Logs
container = document.getElementById("logs");
countElement = document.getElementById("logs-count");
logsCount++;
countElement.textContent = logsCount;
} else {
return; // Unknown data type
}
const timestamp = new Date().toLocaleTimeString();
const dataDiv = document.createElement("div");
dataDiv.innerHTML = `<strong>[${timestamp}]</strong> ${JSON.stringify(
data,
null,
2
)}<hr>`;
container.appendChild(dataDiv);
container.scrollTop = container.scrollHeight;
}
function clearData(type) {
const container = document.getElementById(type);
container.innerHTML = "";
const countElement = document.getElementById(type + "-count");
if (type === "traces") tracesCount = 0;
else if (type === "metrics") metricsCount = 0;
else if (type === "logs") logsCount = 0;
countElement.textContent = "0";
}
// Connect on page load
connect();
</script>
</body>
</html>