]>
Commit | Line | Data |
---|---|---|
bf5542f8 LP |
1 | <!DOCTYPE html> |
2 | <html> | |
3 | <head> | |
4 | <title>Journal</title> | |
5 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
6 | <style type="text/css"> | |
7 | div#logs { | |
8 | font-family: monospace; | |
9 | font-size: 8pt; | |
10 | background-color: #ffffff; | |
11 | padding: 1em; | |
12 | margin: 2em 0em; | |
13 | border-radius: 10px 10px 10px 10px; | |
14 | border: 1px solid threedshadow; | |
15 | white-space: nowrap; | |
16 | overflow-x: scroll; | |
17 | } | |
18 | body { | |
19 | background-color: #ededed; | |
20 | color: #313739; | |
21 | font: message-box; | |
22 | margin: 5em; | |
23 | } | |
24 | ||
25 | .log-error { | |
26 | color: red; | |
27 | font-weight: bold; | |
28 | } | |
29 | .log-highlight { | |
30 | font-weight: bold; | |
31 | } | |
32 | </style> | |
33 | </head> | |
34 | ||
35 | <body> | |
36 | <!-- TODO: | |
37 | ||
38 | - seek to back properly | |
39 | - handle seek before front properly | |
40 | - show red lines for reboots | |
41 | - show contents of entries --> | |
42 | ||
43 | <h1 id="title"></h1> | |
44 | ||
45 | <div id="os"></div> | |
46 | <div id="virtualization"></div> | |
47 | <div id="cutoff"></div> | |
48 | <div id="machine"></div> | |
49 | <div id="usage"></div> | |
50 | <div id="showing"></div> | |
51 | ||
52 | <div id="logs"></div> | |
53 | ||
54 | <form> | |
55 | <input id="head" type="button" value="|<" onclick="entriesLoadHead();"/> | |
56 | <input id="previous" type="button" value="<<" onclick="entriesLoadPrevious();"/> | |
57 | <input id="next" type="button" value=">>" onclick="entriesLoadNext();"/> | |
58 | <input id="tail" type="button" value=">|" onclick="entriesLoadTail();"/> | |
59 | | |
60 | <input id="more" type="button" value="More" onclick="entriesMore();"/> | |
61 | <input id="less" type="button" value="Less" onclick="entriesLess();"/> | |
62 | </form> | |
63 | ||
64 | <script type="text/javascript"> | |
65 | var first_cursor = null; | |
66 | var last_cursor = null; | |
67 | ||
68 | function setCookie(name, value, msec) { | |
69 | var d = new Date(); | |
70 | d.setMilliseconds(d.getMilliseconds() + msec); | |
71 | var v = escape(value) + "; expires=" + d.toUTCString(); | |
72 | document.cookie = name + "=" + value; | |
73 | } | |
74 | ||
75 | function getCookie(name) { | |
76 | var i, l; | |
77 | l = document.cookie.split(";"); | |
78 | for (i in l) { | |
79 | var x, y, j; | |
80 | j = l[i].indexOf("="); | |
81 | x = l[i].substr(0, j); | |
82 | y = l[i].substr(j+1); | |
83 | if (x == name) | |
84 | return unescape(y); | |
85 | } | |
86 | return null; | |
87 | } | |
88 | ||
89 | function getNEntries() { | |
90 | var n; | |
91 | n = getCookie("n_entries"); | |
92 | if (n == null) | |
93 | return 50; | |
94 | return parseInt(n); | |
95 | } | |
96 | ||
97 | function showNEntries(n) { | |
98 | var showing = document.getElementById("showing"); | |
99 | showing.innerHTML = "Showing <b>" + n.toString() + "</b> entries."; | |
100 | } | |
101 | ||
102 | function setNEntries(n) { | |
103 | if (n < 10) | |
104 | n = 10; | |
105 | else if (n > 1000) | |
106 | n = 1000; | |
107 | ||
108 | setCookie("n_entries", n.toString(), 30*24*60*60*1000); | |
109 | showNEntries(n); | |
110 | } | |
111 | ||
112 | function machineLoad() { | |
113 | var request = new XMLHttpRequest(); | |
114 | request.open("GET", "/machine"); | |
115 | request.onreadystatechange = machineOnResult; | |
116 | request.setRequestHeader("Accept", "application/json"); | |
117 | request.send(null); | |
118 | } | |
119 | ||
120 | function formatBytes(u) { | |
121 | if (u >= 1024*1024*1024*1024) | |
122 | return (u/1024/1024/1024/1024).toFixed(1) + " TiB"; | |
123 | else if (u >= 1024*1024*1024) | |
124 | return (u/1024/1024/1024).toFixed(1) + " GiB"; | |
125 | else if (u >= 1024*1024) | |
126 | return (u/1024/1024).toFixed(1) + " MiB"; | |
127 | else if (u >= 1024) | |
128 | return (u/1024).toFixed(1) + " KiB"; | |
129 | else | |
130 | return u.toString() + " B"; | |
131 | } | |
132 | ||
133 | function machineOnResult(event) { | |
134 | if ((event.currentTarget.readyState != 4) || | |
135 | (event.currentTarget.status != 200 && event.currentTarget.status != 0)) | |
136 | return; | |
137 | ||
138 | var d = JSON.parse(event.currentTarget.responseText); | |
139 | ||
140 | var title = document.getElementById("title"); | |
141 | title.innerHTML = 'Journal of ' + d.hostname; | |
142 | document.title = 'Journal of ' + d.hostname; | |
143 | ||
144 | var machine = document.getElementById("machine"); | |
145 | machine.innerHTML = 'Machine ID is <b>' + d.machine_id + '</b>, current boot ID is <b>' + d.boot_id + '</b>.'; | |
146 | ||
147 | var cutoff = document.getElementById("cutoff"); | |
148 | var from = new Date(parseInt(d.cutoff_from_realtime) / 1000); | |
149 | var to = new Date(parseInt(d.cutoff_to_realtime) / 1000); | |
150 | cutoff.innerHTML = 'Journal begins at <b>' + from.toLocaleString() + '</b> and ends at <b>' + to.toLocaleString() + '</b>.'; | |
151 | ||
152 | var usage = document.getElementById("usage"); | |
153 | usage.innerHTML = 'Disk usage is <b>' + formatBytes(parseInt(d.usage)) + '</b>.'; | |
154 | ||
155 | var os = document.getElementById("os"); | |
156 | os.innerHTML = 'Operating system is <b>' + d.os_pretty_name + '</b>.'; | |
157 | ||
158 | var virtualization = document.getElementById("virtualization"); | |
159 | virtualization.innerHTML = d.virtualization == "bare" ? "Running on <b>bare metal</b>." : "Running on virtualization <b>" + d.virtualization + "</b>."; | |
160 | } | |
161 | ||
162 | function entriesLoad(range) { | |
163 | var request = new XMLHttpRequest(); | |
164 | request.open("GET", "/entries"); | |
165 | request.onreadystatechange = entriesOnResult; | |
166 | request.setRequestHeader("Accept", "application/json"); | |
167 | request.setRequestHeader("Range", "entries=" + range + ":" + getNEntries().toString()); | |
168 | request.send(null); | |
169 | } | |
170 | ||
171 | function entriesLoadNext() { | |
172 | if (last_cursor == null) | |
173 | entriesLoad(""); | |
174 | else | |
175 | entriesLoad(last_cursor + ":1"); | |
176 | } | |
177 | ||
178 | function entriesLoadPrevious() { | |
179 | if (first_cursor == null) | |
180 | entriesLoad(""); | |
181 | else | |
182 | entriesLoad(first_cursor + ":-" + getNEntries().toString()); | |
183 | } | |
184 | ||
185 | function entriesLoadHead() { | |
186 | entriesLoad(""); | |
187 | } | |
188 | ||
189 | function entriesLoadTail() { | |
190 | entriesLoad(":-" + getNEntries().toString()); | |
191 | } | |
192 | ||
193 | function entriesOnResult(event) { | |
194 | ||
195 | if ((event.currentTarget.readyState != 4) || | |
196 | (event.currentTarget.status != 200 && event.currentTarget.status != 0)) | |
197 | return; | |
198 | ||
199 | var logs = document.getElementById("logs"); | |
200 | logs.innerHTML = ""; | |
201 | ||
202 | var lc = null; | |
203 | var fc = null; | |
204 | ||
205 | var i; | |
206 | var l = event.currentTarget.responseText.split('\n'); | |
207 | ||
208 | if (l.length <= 1) { | |
209 | logs.innerHTML = "<i>No further entries...</i>"; | |
210 | return; | |
211 | } | |
212 | ||
213 | for (i in l) { | |
214 | ||
215 | if (l[i] == '') | |
216 | continue; | |
217 | ||
218 | var d = JSON.parse(l[i]); | |
219 | if (d.MESSAGE == undefined || d.__CURSOR == undefined) | |
220 | continue; | |
221 | ||
222 | if (fc == null) | |
223 | fc = d.__CURSOR; | |
224 | lc = d.__CURSOR; | |
225 | ||
226 | var priority; | |
227 | if (d.PRIORITY != undefined) | |
228 | priority = parseInt(d.PRIORITY); | |
229 | else | |
230 | priority = 6; | |
231 | ||
232 | if (priority <= 3) | |
233 | clazz = "log-error"; | |
234 | else if (priority <= 5) | |
235 | clazz = "log-highlight"; | |
236 | else | |
237 | clazz = "log-normal"; | |
238 | ||
239 | var line = '<div class="' + clazz + '">'; | |
240 | ||
241 | if (d.SYSLOG_IDENTIFIER != undefined) | |
242 | line += d.SYSLOG_IDENTIFIER; | |
243 | else if (d._COMM != undefined) | |
244 | line += d._COMM; | |
245 | ||
246 | if (d._PID != undefined) | |
247 | line += "[" + d._PID + "]"; | |
248 | else if (d.SYSLOG_PID != undefined) | |
249 | line += "[" + d.SYSLOG_PID + "]"; | |
250 | ||
251 | if (d.MESSAGE == null) | |
252 | line += ": [blob data]</div>"; | |
253 | else if (d.MESSAGE instanceof Array) | |
254 | line += ": [" + formatBytes(d.MESSAGE.length) + " blob data]</div>"; | |
255 | else | |
256 | line += ": " + d.MESSAGE + "</div>"; | |
257 | ||
258 | logs.innerHTML += line; | |
259 | } | |
260 | ||
261 | if (fc != null) | |
262 | first_cursor = fc; | |
263 | if (lc != null) | |
264 | last_cursor = lc; | |
265 | } | |
266 | ||
267 | function entriesMore() { | |
268 | setNEntries(getNEntries() + 10); | |
269 | entriesLoad(""); | |
270 | } | |
271 | ||
272 | function entriesLess() { | |
273 | setNEntries(getNEntries() - 10); | |
274 | entriesLoad(""); | |
275 | } | |
276 | ||
277 | machineLoad(); | |
278 | entriesLoad(""); | |
279 | showNEntries(getNEntries()); | |
280 | </script> | |
281 | </body> | |
282 | </html> |