← Back to team overview

canonical-ubuntu-qa team mailing list archive

[Merge] ~hyask/autopkgtest-cloud:skia/logs_viewer into autopkgtest-cloud:master

 

Skia has proposed merging ~hyask/autopkgtest-cloud:skia/logs_viewer into autopkgtest-cloud:master.

Requested reviews:
  Canonical's Ubuntu QA (canonical-ubuntu-qa)

For more details, see:
https://code.launchpad.net/~hyask/autopkgtest-cloud/+git/autopkgtest-cloud/+merge/463108

Adding a log viewer as userscript.
-- 
Your team Canonical's Ubuntu QA is requested to review the proposed merge of ~hyask/autopkgtest-cloud:skia/logs_viewer into autopkgtest-cloud:master.
diff --git a/charms/focal/autopkgtest-web/webcontrol/static/logs-viewer.user.js b/charms/focal/autopkgtest-web/webcontrol/static/logs-viewer.user.js
new file mode 100644
index 0000000..27c9349
--- /dev/null
+++ b/charms/focal/autopkgtest-web/webcontrol/static/logs-viewer.user.js
@@ -0,0 +1,263 @@
+// ==UserScript==
+// @name         Autopkgtest Logs Enhancer
+// @namespace    http://tampermonkey.net/
+// @version      2024-03-25
+// @description  Does some processing to beautify the tests logs on https://autopkgtest.ubuntu.com
+// @author       Corentin -Cajuteq- Jacquet, Point Vermeil, https://pointvermeil.fr/
+// @match        objectstorage.prodstack5.canonical.com/*
+// @match        autopkgtest.ubuntu.com/*
+// @icon         https://www.google.com/s2/favicons?sz=64&domain=mozilla.org
+// @grant        GM_addStyle
+// ==/UserScript==
+
+(function() {
+    'use strict';
+
+    addEventListener("load", main)
+
+    function main() {
+        add_surrounding();
+    }
+
+    function add_surrounding() {
+        const css = `
+.button-bar {
+    position: sticky;
+    top: 0;
+    background: #222;
+}
+
+.button-bar button {
+    margin: 0.5em;
+}
+
+/* -----------------------------------------------------------------
+ * Log viewer
+ * ----------------------------------------------------------------- */
+.log-view .log-divider {
+  background-color: #2e3436;
+}
+
+.log-view .log-divider a {
+  font-family: monospace;
+  font-size: smaller;
+  color: #66ff66;
+  font-weight: bold;
+  padding-left: 50px;
+}
+
+.log-view .log-divider-fail a {
+  color: #ff6666;
+}
+
+.log-view .log-divider .log-open {
+  display: none;
+}
+
+.log-view .line-number {
+  float: left;
+  width: 50px;
+  font-family: monospace;
+  font-size: smaller;
+  text-align: right;
+  color: #eeeeec;
+  user-select: none;
+}
+
+.line-number a,
+.line-number a:visited {
+  padding-right: 0.5em;
+  display: block;
+  color: #eeeeec;
+}
+.line-number a:hover {
+  background-color: #ffc;
+  color: #888a85;
+}
+
+.log-view .log-line {
+  margin-left: 50px;
+  padding-left: 0.5em;
+  padding-right: 0.5em;
+  font-family: monospace;
+  font-size: smaller;
+  background-color: #2e3436;
+  color: #eeeeec;
+  white-space: pre-wrap;
+}
+
+.log-section > .line-number:first-child + .log-line,
+.log-section > .line-number:first-child {
+  padding-top: 0.5em;
+}
+
+.log-section .log-line:last-child {
+  padding-bottom: 0.5em;
+}
+
+.log-view {
+  border: 1px solid #040000;
+}
+
+.log-section {
+  background-color: #555753;
+}
+
+.log-view .log-nav {
+  float: right;
+  margin-right: 1em;
+}
+
+#logview-index .section-index-group {
+  margin-top: 1em;
+}
+
+#logview-index a:hover {
+  background-color: #ffc;
+}
+    `
+        GM_addStyle(css)
+        const rawlog = document.getElementsByTagName("pre")[0]
+        const newDiv = document.createElement("div");
+        newDiv.className = "log-view";
+
+        const buttonBar = document.createElement("div");
+        buttonBar.className = "button-bar";
+
+        const foldAllButton = document.createElement("button");
+        foldAllButton.innerText = "fold all"
+        foldAllButton.addEventListener('click', () => {
+            var sections = document.getElementsByClassName("log-section")
+            for (var section of sections) {
+                section.style.display = "none";
+            }
+            var sectionsArrowLeft = document.getElementsByClassName("log-open")
+            for (var sectionArrowLeft of sectionsArrowLeft) {
+                sectionArrowLeft.style.display = "inherit";
+            }
+            var sectionsArrowDown = document.getElementsByClassName("log-close")
+            for (var sectionArrowDown of sectionsArrowDown) {
+                sectionArrowDown.style.display = "none";
+            }
+        })
+        buttonBar.appendChild(foldAllButton)
+        const unfoldAllButton = document.createElement("button");
+        unfoldAllButton.innerText = "unfold all"
+        unfoldAllButton.addEventListener('click', () => {
+            var sections = document.getElementsByClassName("log-section")
+            for (var section of sections) {
+                section.style.display = "inherit";
+            }
+            var sectionsArrowLeft = document.getElementsByClassName("log-open")
+            for (var sectionArrowLeft of sectionsArrowLeft) {
+                sectionArrowLeft.style.display = "none";
+            }
+            var sectionsArrowDown = document.getElementsByClassName("log-close")
+            for (var sectionArrowDown of sectionsArrowDown) {
+                sectionArrowDown.style.display = "inherit";
+            }
+        })
+        buttonBar.appendChild(unfoldAllButton)
+        newDiv.appendChild(buttonBar)
+
+
+        const linedLog = rawlog.innerText.split("\n");
+
+        function toggleLogSection(section) {
+            var arrows = section.getElementsByClassName("log-toggle")
+            if (arrows[0].style.display == "none") {
+                arrows[0].style.display = "inline";
+                arrows[1].style.display = "none";
+                section.parentElement.nextSibling.style.display = "none";
+
+            } else {
+                arrows[0].style.display = "none";
+                arrows[1].style.display = "inline";
+                section.parentElement.nextSibling.style.display = "inherit";
+
+            }
+        };
+
+
+        var logSection
+        var logDivider
+
+        var logSectionNb = 0;
+        function createSection(text){
+            logSectionNb+=1;
+            logDivider = document.createElement("div");
+            logDivider.className = "log-divider log-divider-";
+            newDiv.appendChild(logDivider);
+
+            const dividerAnchor = document.createElement("a");
+            dividerAnchor.href = "#S"+logSectionNb;
+            dividerAnchor.onclick = function() {return toggleLogSection(this)}
+            const dividerArrowLeft = document.createElement("span");
+            dividerArrowLeft.className = "log-toggle log-open"
+            dividerArrowLeft.style.display = "none";
+            const dividerArrowDown = document.createElement("span");
+            dividerArrowDown.className = "log-toggle log-close"
+            dividerArrowDown.style.display = "inline";
+            const dividerArrowLeftContent = document.createTextNode(" ▸ ");
+            dividerArrowLeft.appendChild(dividerArrowLeftContent)
+            const dividerArrowDownContent = document.createTextNode(" ▾ ");
+            dividerArrowDown.appendChild(dividerArrowDownContent)
+            const dividerName = document.createElement("span");
+            dividerName.className = "log-section-name"
+            const dividerContent = document.createTextNode(text);
+            dividerName.appendChild(dividerContent)
+
+            dividerAnchor.appendChild(dividerArrowLeft)
+            dividerAnchor.appendChild(dividerArrowDown)
+            dividerAnchor.appendChild(dividerName)
+            logDivider.appendChild(dividerAnchor);
+
+            logSection = document.createElement("div");
+            logSection.className = "log-section";
+            newDiv.appendChild(logSection);
+        };
+
+        for (const line in linedLog) {
+            switch (true) {
+                case /autopkgtest.*: starting date/.test(linedLog[line]):
+                    createSection("start run");
+                    break;
+                case /autopkgtest.*: @@@@@@@@@@@@@@@@@@@@ test bed setup/.test(linedLog[line]):
+                    createSection("test bed setup");
+                    break;
+                case /autopkgtest.*: @@@@@@@@@@@@@@@@@@@@ apt-source/.test(linedLog[line]):
+                    createSection("apt-source");
+                    break;
+                case /autopkgtest.*: test(.*)testbed/.test(linedLog[line]):
+                    var group = linedLog[line].match(/autopkgtest.*: test(.*)testbed/)
+                    createSection(" test " + group[1] + " preparing testbed");
+                    break;
+                case /autopkgtest.*: @@@@@@@@@@@@@@@@@@@@ summary/.test(linedLog[line]):
+                    createSection("summary");
+                    break;
+                default:
+                    break;
+            }
+
+            const lineNumber = document.createElement("span");
+            lineNumber.id = line;
+            lineNumber.className = "line-number";
+            const lineAnchor = document.createElement("a");
+            lineAnchor.href = "#"+line;
+            const lineNb = document.createTextNode(line);
+            lineAnchor.appendChild(lineNb)
+            lineNumber.appendChild(lineAnchor);
+            logSection.appendChild(lineNumber);
+
+            const logLine = document.createElement("div");
+            logLine.className = "log-line";
+            const newContent = document.createTextNode(linedLog[line]);
+            logLine.appendChild(newContent);
+
+            logSection.appendChild(logLine);
+        }
+
+        document.body.insertBefore(newDiv, rawlog);
+        rawlog.remove();
+    }
+})();
diff --git a/charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html b/charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html
index 05a3c2e..fadff6d 100644
--- a/charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html
+++ b/charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html
@@ -60,6 +60,9 @@
       </td>
     </tr>
   {% endfor %}
-
   </table>
+
+  <p>
+    To ease the browsing of logs, you can use <a href="{{ url_for('static', filename='logs-viewer.user.js') }}">this userscript</a> with any extension supporting that, like <a href="https://www.tampermonkey.net/";>TamperMonkey</a>.
+  </p>
 {% endblock %}