(function () { 'use strict'; var state = { range: '15m', offset: 0, limit: 100, tail: false, tailTimer: null, tailCursor: null, tailPaused: false, lastCount: 0 }; var SEV_CLASS = { fatal: 'label-danger', crit: 'label-danger', critical: 'label-danger', error: 'label-danger', err: 'label-danger', warn: 'label-warning', warning: 'label-warning', info: 'label-info', debug: 'label-default', trace: 'label-default' }; function sevLabel(sev) { var cls = SEV_CLASS[(sev || '').toLowerCase()] || 'label-default'; return $('').addClass('label ' + cls).text(sev || '-'); } function timeParams() { var p = {}; if (state.range === 'custom') { var s = $('#since-input').val(), u = $('#until-input').val(); if (s) p.since = new Date(s).toISOString(); if (u) p.until = new Date(u).toISOString(); } else { p.since = state.range; } return p; } function filterParams() { var p = {}; var map = { namespace: '#f-namespace', app: '#f-app', host: '#f-host', pod: '#f-pod', container: '#f-container', severity: '#f-severity', stream: '#f-stream' }; Object.keys(map).forEach(function (k) { var v = $(map[k]).val(); if (v) p[k] = v; }); var q = $('#q').val().trim(); if (q) p.q = q; if ($('#sql-row').is(':visible')) { var sql = $('#sql').val().trim(); if (sql) p.sql = sql; } return p; } function setStatus(msg, isError) { $('#status-text').text(msg || '').toggleClass('text-danger', !!isError); } function renderRow(row, tbody) { var src = (row.namespace || '-') + ' / ' + (row.pod || '-'); var tr = $('').addClass('log-row') .append($('').addClass('col-ts').text(row.timestamp)) .append($('').addClass('col-src').text(src)) .append($('').addClass('col-sev').append(sevLabel(row.severity))) .append($('').addClass('msg').text(row.message)); tr.data('row', row); tbody.append(tr); } function kvTable(title, obj) { var keys = Object.keys(obj || {}); if (!keys.length) return null; var t = $('').addClass('table table-condensed detail-table'); t.append($('').append($('').addClass('detail-row').append(cell)); } function renderRows(rows, append) { var tbody = $('#log-body'); if (!append) tbody.empty(); rows.forEach(function (r) { renderRow(r, tbody); }); } function runQuery() { var params = $.extend({ limit: state.limit, offset: state.offset }, timeParams(), filterParams()); setStatus('loading...'); $.getJSON('/api/query', params) .done(function (res) { renderRows(res.rows, false); state.lastCount = res.count; setStatus(''); $('#result-info').text( res.count + ' rows (' + res.since + ' → ' + res.until + '), offset ' + res.offset); $('#prev-btn').prop('disabled', state.offset === 0); $('#next-btn').prop('disabled', res.count < state.limit); }) .fail(function (xhr) { setStatus((xhr.responseJSON && xhr.responseJSON.error) || 'query failed', true); }); } function tailTick() { if (state.tailPaused) return; var params = $.extend({}, filterParams()); if (state.tailCursor !== null) params.cursor = state.tailCursor; $.getJSON('/api/tail', params) .done(function (res) { state.tailCursor = res.cursor; if (res.rows.length) { renderRows(res.rows, true); // keep the DOM bounded during long tails var body = $('#log-body'); var extra = body.children('.log-row').length - 2000; if (extra > 0) body.children().slice(0, extra * 2).remove(); if (!state.tailPaused) { window.scrollTo(0, document.body.scrollHeight); } } setStatus('tailing (cursor ' + res.cursor + ')'); }) .fail(function (xhr) { setStatus((xhr.responseJSON && xhr.responseJSON.error) || 'tail failed', true); }); } function startTail() { state.tail = true; state.tailCursor = null; state.tailPaused = false; $('#log-body').empty(); $('#tail-toggle').addClass('active'); $('#pager button, #page-size').prop('disabled', true); state.tailTimer = setInterval(tailTick, 1000); tailTick(); } function stopTail() { state.tail = false; clearInterval(state.tailTimer); state.tailTimer = null; $('#tail-toggle').removeClass('active'); $('#pager button, #page-size').prop('disabled', false); setStatus(''); } function refresh() { if (state.tail) { stopTail(); startTail(); return; } state.offset = 0; runQuery(); } function loadFacets() { $.getJSON('/api/facets', timeParams()).done(function (res) { var fill = function (sel, items, placeholder) { var cur = $(sel).val(); var el = $(sel).empty().append($('
').text(title)); keys.sort().forEach(function (k) { t.append($('
').text(k)).append($('').text(obj[k]))); }); return t; } function toggleDetail(tr) { var next = tr.next(); if (next.hasClass('detail-row')) { next.remove(); return; } var row = tr.data('row'); var cell = $('').attr('colspan', 4); var meta = { host: row.host, source: row.source, namespace: row.namespace, pod: row.pod, container: row.container, stream: row.stream }; [kvTable('meta', meta), kvTable('labels', row.labels), kvTable('fields', row.fields)] .forEach(function (t) { if (t) cell.append(t); }); tr.after($('