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

Proposed by Skia
Status: Merged
Merged at revision: 56ec9a7d433cf0e8a4b5b3383dd0f72637c9bb23
Proposed branch: ~hyask/autopkgtest-cloud:skia/logs_viewer
Merge into: autopkgtest-cloud:master
Diff against target: 284 lines (+267/-1)
2 files modified
charms/focal/autopkgtest-web/webcontrol/static/logs-viewer.user.js (+263/-0)
charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html (+4/-1)
Reviewer Review Type Date Requested Status
Tim Andersson Approve
Review via email: mp+463108@code.launchpad.net

Description of the change

Adding a log viewer as userscript.

To post a comment you must log in.
Revision history for this message
Tim Andersson (andersson123) wrote :

LGTM if CI passes :)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/charms/focal/autopkgtest-web/webcontrol/static/logs-viewer.user.js b/charms/focal/autopkgtest-web/webcontrol/static/logs-viewer.user.js
2new file mode 100644
3index 0000000..27c9349
4--- /dev/null
5+++ b/charms/focal/autopkgtest-web/webcontrol/static/logs-viewer.user.js
6@@ -0,0 +1,263 @@
7+// ==UserScript==
8+// @name Autopkgtest Logs Enhancer
9+// @namespace http://tampermonkey.net/
10+// @version 2024-03-25
11+// @description Does some processing to beautify the tests logs on https://autopkgtest.ubuntu.com
12+// @author Corentin -Cajuteq- Jacquet, Point Vermeil, https://pointvermeil.fr/
13+// @match objectstorage.prodstack5.canonical.com/*
14+// @match autopkgtest.ubuntu.com/*
15+// @icon https://www.google.com/s2/favicons?sz=64&domain=mozilla.org
16+// @grant GM_addStyle
17+// ==/UserScript==
18+
19+(function() {
20+ 'use strict';
21+
22+ addEventListener("load", main)
23+
24+ function main() {
25+ add_surrounding();
26+ }
27+
28+ function add_surrounding() {
29+ const css = `
30+.button-bar {
31+ position: sticky;
32+ top: 0;
33+ background: #222;
34+}
35+
36+.button-bar button {
37+ margin: 0.5em;
38+}
39+
40+/* -----------------------------------------------------------------
41+ * Log viewer
42+ * ----------------------------------------------------------------- */
43+.log-view .log-divider {
44+ background-color: #2e3436;
45+}
46+
47+.log-view .log-divider a {
48+ font-family: monospace;
49+ font-size: smaller;
50+ color: #66ff66;
51+ font-weight: bold;
52+ padding-left: 50px;
53+}
54+
55+.log-view .log-divider-fail a {
56+ color: #ff6666;
57+}
58+
59+.log-view .log-divider .log-open {
60+ display: none;
61+}
62+
63+.log-view .line-number {
64+ float: left;
65+ width: 50px;
66+ font-family: monospace;
67+ font-size: smaller;
68+ text-align: right;
69+ color: #eeeeec;
70+ user-select: none;
71+}
72+
73+.line-number a,
74+.line-number a:visited {
75+ padding-right: 0.5em;
76+ display: block;
77+ color: #eeeeec;
78+}
79+.line-number a:hover {
80+ background-color: #ffc;
81+ color: #888a85;
82+}
83+
84+.log-view .log-line {
85+ margin-left: 50px;
86+ padding-left: 0.5em;
87+ padding-right: 0.5em;
88+ font-family: monospace;
89+ font-size: smaller;
90+ background-color: #2e3436;
91+ color: #eeeeec;
92+ white-space: pre-wrap;
93+}
94+
95+.log-section > .line-number:first-child + .log-line,
96+.log-section > .line-number:first-child {
97+ padding-top: 0.5em;
98+}
99+
100+.log-section .log-line:last-child {
101+ padding-bottom: 0.5em;
102+}
103+
104+.log-view {
105+ border: 1px solid #040000;
106+}
107+
108+.log-section {
109+ background-color: #555753;
110+}
111+
112+.log-view .log-nav {
113+ float: right;
114+ margin-right: 1em;
115+}
116+
117+#logview-index .section-index-group {
118+ margin-top: 1em;
119+}
120+
121+#logview-index a:hover {
122+ background-color: #ffc;
123+}
124+ `
125+ GM_addStyle(css)
126+ const rawlog = document.getElementsByTagName("pre")[0]
127+ const newDiv = document.createElement("div");
128+ newDiv.className = "log-view";
129+
130+ const buttonBar = document.createElement("div");
131+ buttonBar.className = "button-bar";
132+
133+ const foldAllButton = document.createElement("button");
134+ foldAllButton.innerText = "fold all"
135+ foldAllButton.addEventListener('click', () => {
136+ var sections = document.getElementsByClassName("log-section")
137+ for (var section of sections) {
138+ section.style.display = "none";
139+ }
140+ var sectionsArrowLeft = document.getElementsByClassName("log-open")
141+ for (var sectionArrowLeft of sectionsArrowLeft) {
142+ sectionArrowLeft.style.display = "inherit";
143+ }
144+ var sectionsArrowDown = document.getElementsByClassName("log-close")
145+ for (var sectionArrowDown of sectionsArrowDown) {
146+ sectionArrowDown.style.display = "none";
147+ }
148+ })
149+ buttonBar.appendChild(foldAllButton)
150+ const unfoldAllButton = document.createElement("button");
151+ unfoldAllButton.innerText = "unfold all"
152+ unfoldAllButton.addEventListener('click', () => {
153+ var sections = document.getElementsByClassName("log-section")
154+ for (var section of sections) {
155+ section.style.display = "inherit";
156+ }
157+ var sectionsArrowLeft = document.getElementsByClassName("log-open")
158+ for (var sectionArrowLeft of sectionsArrowLeft) {
159+ sectionArrowLeft.style.display = "none";
160+ }
161+ var sectionsArrowDown = document.getElementsByClassName("log-close")
162+ for (var sectionArrowDown of sectionsArrowDown) {
163+ sectionArrowDown.style.display = "inherit";
164+ }
165+ })
166+ buttonBar.appendChild(unfoldAllButton)
167+ newDiv.appendChild(buttonBar)
168+
169+
170+ const linedLog = rawlog.innerText.split("\n");
171+
172+ function toggleLogSection(section) {
173+ var arrows = section.getElementsByClassName("log-toggle")
174+ if (arrows[0].style.display == "none") {
175+ arrows[0].style.display = "inline";
176+ arrows[1].style.display = "none";
177+ section.parentElement.nextSibling.style.display = "none";
178+
179+ } else {
180+ arrows[0].style.display = "none";
181+ arrows[1].style.display = "inline";
182+ section.parentElement.nextSibling.style.display = "inherit";
183+
184+ }
185+ };
186+
187+
188+ var logSection
189+ var logDivider
190+
191+ var logSectionNb = 0;
192+ function createSection(text){
193+ logSectionNb+=1;
194+ logDivider = document.createElement("div");
195+ logDivider.className = "log-divider log-divider-";
196+ newDiv.appendChild(logDivider);
197+
198+ const dividerAnchor = document.createElement("a");
199+ dividerAnchor.href = "#S"+logSectionNb;
200+ dividerAnchor.onclick = function() {return toggleLogSection(this)}
201+ const dividerArrowLeft = document.createElement("span");
202+ dividerArrowLeft.className = "log-toggle log-open"
203+ dividerArrowLeft.style.display = "none";
204+ const dividerArrowDown = document.createElement("span");
205+ dividerArrowDown.className = "log-toggle log-close"
206+ dividerArrowDown.style.display = "inline";
207+ const dividerArrowLeftContent = document.createTextNode(" ▸ ");
208+ dividerArrowLeft.appendChild(dividerArrowLeftContent)
209+ const dividerArrowDownContent = document.createTextNode(" ▾ ");
210+ dividerArrowDown.appendChild(dividerArrowDownContent)
211+ const dividerName = document.createElement("span");
212+ dividerName.className = "log-section-name"
213+ const dividerContent = document.createTextNode(text);
214+ dividerName.appendChild(dividerContent)
215+
216+ dividerAnchor.appendChild(dividerArrowLeft)
217+ dividerAnchor.appendChild(dividerArrowDown)
218+ dividerAnchor.appendChild(dividerName)
219+ logDivider.appendChild(dividerAnchor);
220+
221+ logSection = document.createElement("div");
222+ logSection.className = "log-section";
223+ newDiv.appendChild(logSection);
224+ };
225+
226+ for (const line in linedLog) {
227+ switch (true) {
228+ case /autopkgtest.*: starting date/.test(linedLog[line]):
229+ createSection("start run");
230+ break;
231+ case /autopkgtest.*: @@@@@@@@@@@@@@@@@@@@ test bed setup/.test(linedLog[line]):
232+ createSection("test bed setup");
233+ break;
234+ case /autopkgtest.*: @@@@@@@@@@@@@@@@@@@@ apt-source/.test(linedLog[line]):
235+ createSection("apt-source");
236+ break;
237+ case /autopkgtest.*: test(.*)testbed/.test(linedLog[line]):
238+ var group = linedLog[line].match(/autopkgtest.*: test(.*)testbed/)
239+ createSection(" test " + group[1] + " preparing testbed");
240+ break;
241+ case /autopkgtest.*: @@@@@@@@@@@@@@@@@@@@ summary/.test(linedLog[line]):
242+ createSection("summary");
243+ break;
244+ default:
245+ break;
246+ }
247+
248+ const lineNumber = document.createElement("span");
249+ lineNumber.id = line;
250+ lineNumber.className = "line-number";
251+ const lineAnchor = document.createElement("a");
252+ lineAnchor.href = "#"+line;
253+ const lineNb = document.createTextNode(line);
254+ lineAnchor.appendChild(lineNb)
255+ lineNumber.appendChild(lineAnchor);
256+ logSection.appendChild(lineNumber);
257+
258+ const logLine = document.createElement("div");
259+ logLine.className = "log-line";
260+ const newContent = document.createTextNode(linedLog[line]);
261+ logLine.appendChild(newContent);
262+
263+ logSection.appendChild(logLine);
264+ }
265+
266+ document.body.insertBefore(newDiv, rawlog);
267+ rawlog.remove();
268+ }
269+})();
270diff --git a/charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html b/charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html
271index 05a3c2e..fadff6d 100644
272--- a/charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html
273+++ b/charms/focal/autopkgtest-web/webcontrol/templates/browse-results.html
274@@ -60,6 +60,9 @@
275 </td>
276 </tr>
277 {% endfor %}
278-
279 </table>
280+
281+ <p>
282+ 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>.
283+ </p>
284 {% endblock %}

Subscribers

People subscribed via source and target branches