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

Subscribers

People subscribed via source and target branches