diff options
Diffstat (limited to 'web/log.js')
-rw-r--r-- | web/log.js | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/web/log.js b/web/log.js new file mode 100644 index 0000000..36fd553 --- /dev/null +++ b/web/log.js @@ -0,0 +1,192 @@ +/* Copyright (c) 2012: Daniel Richman. License: GNU GPL 3 */ +/* Additional features: Priyesh Patel */ + +(function () { + +var dataelem = "#data"; +var pausetoggle = "#pause"; +var scrollelems = ["html", "body"]; + +var url = "/var/log/compute-tools/container.log"; +var fix_rn = true; +var load = 10 * 1024; /* 10KB */ +var poll = 1000; /* 1s */ + +var kill = false; +var loading = false; +var pause = false; +var reverse = true; +var log_data = ""; +var log_file_size = 0; + +/* :-( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt */ +function parseInt2(value) { + if(!(/^[0-9]+$/.test(value))) throw "Invalid integer " + value; + var v = Number(value); + if (isNaN(v)) throw "Invalid integer " + value; + return v; +} + +function get_log() { + if (kill | loading) return; + loading = true; + + var range; + var first_load; + var must_get_206; + if (log_file_size === 0) { + /* Get the last 'load' bytes */ + range = "-" + load.toString(); + first_load = true; + must_get_206 = false; + } else { + /* Get the (log_file_size - 1)th byte, onwards. */ + range = (log_file_size - 1).toString() + "-"; + first_load = false; + must_get_206 = log_file_size > 1; + } + + /* The "log_file_size - 1" deliberately reloads the last byte, which we already + * have. This is to prevent a 416 "Range unsatisfiable" error: a response + * of length 1 tells us that the file hasn't changed yet. A 416 shows that + * the file has been trucnated */ + + $.ajax(url, { + dataType: "text", + cache: false, + headers: {Range: "bytes=" + range}, + success: function (data, s, xhr) { + loading = false; + + var content_size; + + if (xhr.status === 206) { + var c_r = xhr.getResponseHeader("Content-Range"); + if (!c_r) + throw "Server did not respond with a Content-Range"; + + log_file_size = parseInt2(c_r.split("/")[1]); + content_size = parseInt2(xhr.getResponseHeader("Content-Length")); + } else if (xhr.status === 200) { + if (must_get_206) + throw "Expected 206 Partial Content"; + + content_size = log_file_size = + parseInt2(xhr.getResponseHeader("Content-Length")); + } else { + throw "Unexpected status " + xhr.status; + } + + if (first_load && data.length > load) + throw "Server's response was too long"; + + var added = false; + + if (first_load) { + /* Clip leading part-line if not the whole file */ + if (content_size < log_file_size) { + var start = data.indexOf("\n"); + log_data = data.substring(start + 1); + } else { + log_data = data; + } + + added = true; + } else { + /* Drop the first byte (see above) */ + log_data += data.substring(1); + + if (log_data.length > load) { + var start = log_data.indexOf("\n", log_data.length - load); + log_data = log_data.substring(start + 1); + } + + if (data.length > 1) + added = true; + } + + if (added) + show_log(added); + setTimeout(get_log, poll); + }, + error: function (xhr, s, t) { + loading = false; + + if (xhr.status === 416 || xhr.status == 404) { + /* 416: Requested range not satisfiable: log was truncated. */ + /* 404: Retry soon, I guess */ + + log_file_size = 0; + log_data = ""; + show_log(); + + setTimeout(get_log, poll); + } else { + throw "Unknown AJAX Error (status " + xhr.status + ")"; + } + } + }); +} + +function scroll(where) { + for (var i = 0; i < scrollelems.length; i++) { + var s = $(scrollelems[i]); + if (where === -1) + s.scrollTop(s.height()); + else + s.scrollTop(where); + } +} + +function show_log() { + if (pause) return; + + var t = log_data; + + if (reverse) { + var t_a = t.split(/\n/g); + t_a.reverse(); + if (t_a[0] == "") + t_a.shift(); + t = t_a.join("\n"); + } + + if (fix_rn) + t = t.replace(/\n/g, "\r\n"); + + $(dataelem).text(t); + if (!reverse) + scroll(-1); +} + +function error(what) { + kill = true; + + $(dataelem).text("An error occured :-(.\r\n" + + "Reloading may help; no promises.\r\n" + + what); + scroll(0); + + return false; +} + +$(document).ready(function () { + window.onerror = error; + + /* If URL is /logtail/?noreverse display in chronological order */ + var hash = location.search.replace(/^\?/, ""); + if (hash == "noreverse") + reverse = false; + + /* Add pause toggle */ + $(pausetoggle).click(function (e) { + pause = !pause; + $(pausetoggle).text(pause ? "Unpause" : "Pause"); + show_log(); + e.preventDefault(); + }); + + get_log(); +}); + +})(); |