Skip to content

Commit f973dcf

Browse files
authored
infra: improve log explorer (#571)
# 🔍 Description - Add support for new versioning schemes to autodetect commit/tag. - Add past support. - Error logging.
1 parent b423a4f commit f973dcf

1 file changed

Lines changed: 182 additions & 13 deletions

File tree

website/static/logexplorer/index.html

Lines changed: 182 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,100 @@
439439
padding-right: 14px;
440440
}
441441

442+
/* Paste modal */
443+
.paste-overlay {
444+
position: fixed;
445+
inset: 0;
446+
background: rgba(0, 0, 0, 0.6);
447+
display: flex;
448+
align-items: center;
449+
justify-content: center;
450+
z-index: 100;
451+
}
452+
453+
.paste-modal {
454+
background: var(--surface);
455+
border: 1px solid var(--border);
456+
border-radius: 10px;
457+
padding: 20px;
458+
width: 640px;
459+
max-width: 90vw;
460+
max-height: 80vh;
461+
display: flex;
462+
flex-direction: column;
463+
gap: 12px;
464+
}
465+
466+
.paste-modal h3 {
467+
font-size: 15px;
468+
font-weight: 600;
469+
}
470+
471+
.paste-modal textarea {
472+
background: var(--bg);
473+
border: 1px solid var(--border);
474+
color: var(--text);
475+
border-radius: 6px;
476+
padding: 10px;
477+
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
478+
font-size: 12px;
479+
resize: vertical;
480+
min-height: 200px;
481+
flex: 1;
482+
outline: none;
483+
}
484+
485+
.paste-modal textarea:focus {
486+
border-color: var(--accent);
487+
}
488+
489+
.paste-modal-buttons {
490+
display: flex;
491+
gap: 8px;
492+
justify-content: flex-end;
493+
}
494+
495+
.paste-modal-buttons button {
496+
padding: 6px 16px;
497+
border-radius: 6px;
498+
font-size: 13px;
499+
font-weight: 600;
500+
cursor: pointer;
501+
border: 1px solid var(--border);
502+
}
503+
504+
.btn-cancel {
505+
background: var(--surface-hover);
506+
color: var(--text);
507+
}
508+
509+
.btn-load {
510+
background: var(--accent);
511+
color: #0d1117;
512+
border-color: var(--accent);
513+
}
514+
515+
.btn-load:hover {
516+
opacity: 0.85;
517+
}
518+
519+
.drop-zone-separator {
520+
margin-top: 8px;
521+
color: var(--text-muted);
522+
font-size: 13px;
523+
}
524+
525+
.paste-btn {
526+
background: var(--accent);
527+
color: #0d1117;
528+
border: none;
529+
border-radius: 6px;
530+
padding: 8px 18px;
531+
font-size: 13px;
532+
font-weight: 600;
533+
cursor: pointer;
534+
}
535+
442536
#minimap {
443537
position: absolute;
444538
right: 8px;
@@ -478,14 +572,28 @@
478572
checked></label>
479573
</div>
480574

481-
<input type="text" id="commit-input" placeholder="Commit SHA (enables source links)" disabled>
575+
<input type="text" id="commit-input" placeholder="Commit SHA or tag (enables source links)" disabled>
482576

483577
<span class="stats" id="stats"></span>
484578
</div>
485579

486580
<div class="drop-zone" id="drop-zone">
487581
<div class="drop-zone-icon">📄</div>
488582
<div>Drop a <strong>.jsonl</strong> log file here or click <strong>Open Log File</strong></div>
583+
<div class="drop-zone-separator">or</div>
584+
<button id="paste-btn" class="paste-btn">📋
585+
Paste from Clipboard</button>
586+
</div>
587+
588+
<div class="paste-overlay" id="paste-overlay" style="display:none">
589+
<div class="paste-modal" role="dialog" aria-modal="true" aria-labelledby="paste-modal-title">
590+
<h3 id="paste-modal-title">Paste Log Lines</h3>
591+
<textarea id="paste-textarea" placeholder="Paste JSONL log lines here…"></textarea>
592+
<div class="paste-modal-buttons">
593+
<button class="btn-cancel" id="paste-cancel">Cancel</button>
594+
<button class="btn-load" id="paste-load">Load</button>
595+
</div>
596+
</div>
489597
</div>
490598

491599
<div class="log-wrapper" id="log-wrapper">
@@ -513,6 +621,43 @@
513621
const commitInput = document.getElementById("commit-input");
514622
const statsEl = document.getElementById("stats");
515623
const levelFilters = document.getElementById("level-filters");
624+
const pasteOverlay = document.getElementById("paste-overlay");
625+
const pasteTextarea = document.getElementById("paste-textarea");
626+
627+
// --- Paste modal ---
628+
629+
const pasteBtn = document.getElementById("paste-btn");
630+
631+
function openPasteModal() {
632+
pasteOverlay.style.display = "flex";
633+
pasteTextarea.value = "";
634+
pasteTextarea.focus();
635+
}
636+
637+
function closePasteModal() {
638+
pasteOverlay.style.display = "none";
639+
pasteBtn.focus();
640+
}
641+
642+
pasteBtn.addEventListener("click", openPasteModal);
643+
644+
document.getElementById("paste-cancel").addEventListener("click", closePasteModal);
645+
646+
pasteOverlay.addEventListener("click", (e) => {
647+
if (e.target === pasteOverlay) closePasteModal();
648+
});
649+
650+
pasteOverlay.addEventListener("keydown", (e) => {
651+
if (e.key === "Escape") closePasteModal();
652+
});
653+
654+
document.getElementById("paste-load").addEventListener("click", () => {
655+
const text = pasteTextarea.value.trim();
656+
if (text) {
657+
closePasteModal();
658+
parseLog(text);
659+
}
660+
});
516661

517662
// --- File loading ---
518663

@@ -549,13 +694,20 @@
549694
function parseLog(text) {
550695
const lines = text.split("\n");
551696
allEntries = [];
697+
bookmarkedSet.clear();
552698
let parseErrors = 0;
553699

554-
for (const line of lines) {
555-
const trimmed = line.trim();
700+
for (let i = 0; i < lines.length; i++) {
701+
const trimmed = lines[i].trim();
556702
if (!trimmed) continue;
557703
try {
558704
const entry = JSON.parse(trimmed);
705+
// Validate expected fields
706+
if (!("level" in entry) && !("message" in entry) && !("timestamp" in entry)) {
707+
parseErrors++;
708+
console.warn(`Log parser: line ${i + 1} has no expected fields (level, message, timestamp):`, entry);
709+
continue;
710+
}
559711
// Normalize level to lowercase
560712
if (entry.level) entry.level = entry.level.toLowerCase();
561713
entry._idx = allEntries.length;
@@ -565,12 +717,22 @@
565717
}
566718
}
567719

568-
// Auto-detect commit hash from version message
720+
if (parseErrors > 0) {
721+
console.warn(`Log parser: ${parseErrors} line(s) could not be parsed as JSON`);
722+
}
723+
724+
// Auto-detect commit hash or tag from version message
569725
if (!commitInput.value.trim() && allEntries.length > 0) {
570726
const firstMsg = allEntries[0].message || "";
571-
const versionMatch = firstMsg.match(/Trident version:.*-v([0-9a-fA-F]+)/);
572-
if (versionMatch) {
573-
commitInput.value = versionMatch[1];
727+
const commitMatch = firstMsg.match(/Trident version:.*[.\-]v([0-9a-fA-F]+)/);
728+
if (commitMatch) {
729+
commitInput.value = commitMatch[1];
730+
} else {
731+
// Prod builds: MAJOR.MINOR.PATCH-RELEASE.DISTRO → use tag MAJOR.MINOR.PATCH
732+
const tagMatch = firstMsg.match(/Trident version:\s*(\d+\.\d+\.\d+)-\d+\.\w+/);
733+
if (tagMatch) {
734+
commitInput.value = tagMatch[1];
735+
}
574736
}
575737
}
576738

@@ -644,9 +806,12 @@
644806
// --- Rendering ---
645807

646808
function escapeHtml(str) {
647-
const div = document.createElement("div");
648-
div.textContent = str;
649-
return div.innerHTML;
809+
return String(str)
810+
.replace(/&/g, "&amp;")
811+
.replace(/</g, "&lt;")
812+
.replace(/>/g, "&gt;")
813+
.replace(/"/g, "&quot;")
814+
.replace(/'/g, "&#39;");
650815
}
651816

652817
function highlightText(escaped, query) {
@@ -658,6 +823,7 @@
658823

659824
function formatTimestamp(ts) {
660825
if (!ts) return "";
826+
661827
try {
662828
const d = new Date(ts);
663829
return d.toISOString().replace("T", " ").replace("Z", " UTC");
@@ -673,10 +839,13 @@
673839
}
674840

675841
function makeFileLink(file, line) {
676-
const commit = commitInput.value.trim();
842+
const ref = commitInput.value.trim();
677843
const text = escapeHtml(file + ":" + line);
678-
if (commit) {
679-
const url = `https://github.com/microsoft/trident/blob/${encodeURIComponent(commit)}/${encodeURIComponent(file)}#L${line}`;
844+
if (ref) {
845+
// If ref looks like a semver tag (e.g. "0.21.0"), prefix with "v"
846+
const gitRef = /^\d+\.\d+\.\d+$/.test(ref) ? "v" + ref : ref;
847+
const encodedFile = file.split("/").map(encodeURIComponent).join("/");
848+
const url = `https://github.com/microsoft/trident/blob/${encodeURIComponent(gitRef)}/${encodedFile}#L${line}`;
680849
return `<a href="${url}" target="_blank" rel="noopener">${text}</a>`;
681850
}
682851
return text;

0 commit comments

Comments
 (0)