5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
6 <style type="text/css">
7 div#divlogs, div#diventry {
8 font-family: monospace;
10 background-color: #ffffff;
13 border-radius: 10px 10px 10px 10px;
14 border: 1px solid threedshadow;
25 background-color: #ededed;
32 border-right: 1px dotted lightgrey;
36 border-right: 1px dotted lightgrey;
43 td.message > a:link, td.message > a:visited {
44 text-decoration: none;
52 td.message-error > a:link, td.message-error > a:visited {
53 text-decoration: none;
56 td.message-highlight {
60 td.message-highlight > a:link, td.message-highlight > a:visited {
61 text-decoration: none;
64 td > a:hover, td > a:active {
65 text-decoration: underline;
68 table#tablelogs, table#tableentry {
69 border-collapse: collapse;
73 border-right: 1px dotted lightgrey;
85 - show red lines for reboots
86 - show contents of entries -->
91 <div id="virtualization"></div>
92 <div id="cutoff"></div>
93 <div id="machine"></div>
94 <div id="usage"></div>
95 <div id="showing"></div>
97 <div id="divlogs"><table id="tablelogs"></table></div>
99 <div id="diventry"><table id="tableentry"></table></div>
102 <input id="head" type="button" value="|<" onclick="entriesLoadHead();"/>
103 <input id="previous" type="button" value="<<" onclick="entriesLoadPrevious();"/>
104 <input id="next" type="button" value=">>" onclick="entriesLoadNext();"/>
105 <input id="tail" type="button" value=">|" onclick="entriesLoadTail();"/>
106
107 <input id="more" type="button" value="More" onclick="entriesMore();"/>
108 <input id="less" type="button" value="Less" onclick="entriesLess();"/>
111 <script type="text/javascript">
112 var first_cursor = null;
113 var last_cursor = null;
115 function setCookie(name, value, msec) {
117 d.setMilliseconds(d.getMilliseconds() + msec);
118 var v = escape(value) + "; expires=" + d.toUTCString();
119 document.cookie = name + "=" + value;
122 function getCookie(name) {
124 l = document.cookie.split(";");
127 j = l[i].indexOf("=");
128 x = l[i].substr(0, j);
129 y = l[i].substr(j+1);
136 function getNEntries() {
138 n = getCookie("n_entries");
144 function showNEntries(n) {
145 var showing = document.getElementById("showing");
146 showing.innerHTML = "Showing <b>" + n.toString() + "</b> entries.";
149 function setNEntries(n) {
155 setCookie("n_entries", n.toString(), 30*24*60*60*1000);
159 function machineLoad() {
160 var request = new XMLHttpRequest();
161 request.open("GET", "/machine");
162 request.onreadystatechange = machineOnResult;
163 request.setRequestHeader("Accept", "application/json");
167 function formatBytes(u) {
168 if (u >= 1024*1024*1024*1024)
169 return (u/1024/1024/1024/1024).toFixed(1) + " TiB";
170 else if (u >= 1024*1024*1024)
171 return (u/1024/1024/1024).toFixed(1) + " GiB";
172 else if (u >= 1024*1024)
173 return (u/1024/1024).toFixed(1) + " MiB";
175 return (u/1024).toFixed(1) + " KiB";
177 return u.toString() + " B";
180 function escapeHTML(s) {
181 return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
184 function machineOnResult(event) {
185 if ((event.currentTarget.readyState != 4) ||
186 (event.currentTarget.status != 200 && event.currentTarget.status != 0))
189 var d = JSON.parse(event.currentTarget.responseText);
191 var title = document.getElementById("title");
192 title.innerHTML = 'Journal of ' + d.hostname;
193 document.title = 'Journal of ' + d.hostname;
195 var machine = document.getElementById("machine");
196 machine.innerHTML = 'Machine ID is <b>' + d.machine_id + '</b>, current boot ID is <b>' + d.boot_id + '</b>.';
198 var cutoff = document.getElementById("cutoff");
199 var from = new Date(parseInt(d.cutoff_from_realtime) / 1000);
200 var to = new Date(parseInt(d.cutoff_to_realtime) / 1000);
201 cutoff.innerHTML = 'Journal begins at <b>' + from.toLocaleString() + '</b> and ends at <b>' + to.toLocaleString() + '</b>.';
203 var usage = document.getElementById("usage");
204 usage.innerHTML = 'Disk usage is <b>' + formatBytes(parseInt(d.usage)) + '</b>.';
206 var os = document.getElementById("os");
207 os.innerHTML = 'Operating system is <b>' + d.os_pretty_name + '</b>.';
209 var virtualization = document.getElementById("virtualization");
210 virtualization.innerHTML = d.virtualization == "bare" ? "Running on <b>bare metal</b>." : "Running on virtualization <b>" + d.virtualization + "</b>.";
213 function entriesLoad(range) {
214 var request = new XMLHttpRequest();
215 request.open("GET", "/entries");
216 request.onreadystatechange = entriesOnResult;
217 request.setRequestHeader("Accept", "application/json");
218 request.setRequestHeader("Range", "entries=" + range + ":" + getNEntries().toString());
222 function entriesLoadNext() {
223 if (last_cursor == null)
226 entriesLoad(last_cursor + ":1");
229 function entriesLoadPrevious() {
230 if (first_cursor == null)
233 entriesLoad(first_cursor + ":-" + getNEntries().toString());
236 function entriesLoadHead() {
240 function entriesLoadTail() {
241 entriesLoad(":-" + getNEntries().toString());
244 function entriesOnResult(event) {
246 if ((event.currentTarget.readyState != 4) ||
247 (event.currentTarget.status != 200 && event.currentTarget.status != 0))
250 var logs = document.getElementById("tablelogs");
256 var l = event.currentTarget.responseText.split('\n');
259 logs.innerHTML = '<tbody><tr><td colspan="3"><i>No further entries...</i></td></tr></tbody>';
270 var d = JSON.parse(l[i]);
271 if (d.MESSAGE == undefined || d.__CURSOR == undefined)
279 if (d.PRIORITY != undefined)
280 priority = parseInt(d.PRIORITY);
285 clazz = "message-error";
286 else if (priority <= 5)
287 clazz = "message-highlight";
291 buf += '<tr><td class="timestamp">';
293 if (d.__REALTIME_TIMESTAMP != undefined) {
294 var timestamp = new Date(parseInt(d.__REALTIME_TIMESTAMP) / 1000);
295 buf += timestamp.toLocaleString();
298 buf += '</td><td class="process">';
300 if (d.SYSLOG_IDENTIFIER != undefined)
301 buf += d.SYSLOG_IDENTIFIER;
302 else if (d._COMM != undefined)
305 if (d._PID != undefined)
306 buf += "[" + d._PID + "]";
307 else if (d.SYSLOG_PID != undefined)
308 buf += "[" + d.SYSLOG_PID + "]";
310 buf += '</td><td class="' + clazz + '"><a href="#entry" onclick="onMessageClick(\'' + lc + '\');">';
312 if (d.MESSAGE == null)
313 buf += "[blob data]";
314 else if (d.MESSAGE instanceof Array)
315 buf += "[" + formatBytes(d.MESSAGE.length) + " blob data]";
317 buf += escapeHTML(d.MESSAGE);
319 buf += '</a></td></tr>';
322 logs.innerHTML = '<tbody>' + buf + '</tbody>';
330 function entriesMore() {
331 setNEntries(getNEntries() + 10);
332 entriesLoad(first_cursor);
335 function entriesLess() {
336 setNEntries(getNEntries() - 10);
337 entriesLoad(first_cursor);
340 function onResultMessageClick(event) {
341 if ((event.currentTarget.readyState != 4) ||
342 (event.currentTarget.status != 200 && event.currentTarget.status != 0))
345 var d = JSON.parse(event.currentTarget.responseText);
347 document.getElementById("diventry").style.display = "block";
349 entry = document.getElementById("tableentry");
354 buf += '<tr><td class="field">' + key + '</td><td class="data">' + d[key] + '</td></tr>';
357 entry.innerHTML = '<tbody>' + buf + '</tbody>';
360 function onMessageClick(t) {
361 var request = new XMLHttpRequest();
362 request.open("GET", "/entries?discrete");
363 request.onreadystatechange = onResultMessageClick;
364 request.setRequestHeader("Accept", "application/json");
365 request.setRequestHeader("Range", "entries=" + t + ":0:1");
371 showNEntries(getNEntries());