Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion root/app/www/public/ajax/logs.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
require '../loader.php';

if ($_POST['m'] == 'viewAppLog') {
getLog($_POST['log'], 1, true);
getLog($_POST['log'], 1, true, $_POST['activeTab'] ?: '#access');
}

if ($_POST['m'] == 'viewLog') {
Expand All @@ -30,3 +30,7 @@
if ($_POST['m'] == 'deleteLog') {
unlink($_POST['log']);
}

if ($_POST['m'] == 'refreshLogList') {
renderLogList();
}
57 changes: 52 additions & 5 deletions root/app/www/public/functions/logger.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,61 @@ function getLogs()
return $list;
}

function getLog($logfile, $page = 1, $app = false)
function renderLogList()
{
$logList = getLogs();
$index = 0;

foreach ($logList['system'] as $group => $groupLogs) {
?>
<h4 class="mt-2 fst-italic"><?= rtrim($group, '/') ?></h4><?php
foreach ($groupLogs as $log) {
$index++;
?>
<ul style="margin-bottom: 0px; padding-bottom: 0px;">
<li>
<i class="far fa-trash-alt text-danger" title="Delete access log" style="cursor:pointer;" onclick="deleteLog('<?= $log ?>')"></i>
<span id="app-log-<?= $index ?>" style="cursor:pointer;" onclick="viewLog('<?= $log ?>', <?= $index ?>)"><?= basename($log) ?> (<?= date('m/d', filemtime($log)) ?>)</span>
<div style="float: right;"><?= byteConversion(filesize($log)) ?></div>
</li>
</ul>
<?php
}
}

foreach ($logList as $app => $logs) {
if ($app == 'system') {
continue;
}

?>
<h4 class="mt-2"><?= $app ?></h4><?php
foreach ($logs as $log) {
$index++;
?>
<ul style="margin-bottom: 0px; padding-bottom: 0px;">
<li>
<i class="far fa-trash-alt text-danger" title="Delete access log" style="cursor:pointer;" onclick="deleteLog('<?= $log ?>')"></i>
<span id="app-log-<?= $index ?>" style="cursor:pointer;" onclick="viewLog('<?= $log ?>', <?= $index ?>)"><?= str_replace('access_', '', basename($log)) ?> (<?= date('m/d', filemtime($log)) ?>)</span>
<div style="float: right;"><?= byteConversion(filesize($log)) ?></div>
</li>
</ul>
<?php
}
}
}

function getLog($logfile, $page = 1, $app = false, $activeTab = '#access')
{
global $starr, $shell;

$page = $page <= 1 ? 1 : $page;
$start = $page == 1 ? 0 : $page * LOG_LINES_PER_PAGE - LOG_LINES_PER_PAGE;
$end = $start + LOG_LINES_PER_PAGE;

$accessActive = ($activeTab !== '#endpoints') ? 'active' : '';
$endpointsActive = ($activeTab === '#endpoints') ? 'active' : '';

$starr = $starr ?: new Starr();
list($logLines, $file) = explode(' ', $shell->exec('wc -l "' . $logfile . '"'));
$logLines = intval(trim($logLines));
Expand All @@ -141,11 +188,11 @@ function getLog($logfile, $page = 1, $app = false)
?>
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" data-bs-toggle="tab" href="#access" aria-selected="true" role="tab">Access log (<?= number_format($logLines) ?> lines)</a>
<a class="nav-link <?= $accessActive ?>" data-bs-toggle="tab" href="#access" aria-selected="<?= $accessActive ? 'true' : 'false' ?>" role="tab">Access log (<?= number_format($logLines) ?> lines)</a>
</li>
<?php if ($app) { ?>
<li class="nav-item" role="presentation">
<a class="nav-link" data-bs-toggle="tab" href="#endpoints" aria-selected="false" tabindex="-1" role="tab">Endpoint usage</a>
<a class="nav-link <?= $endpointsActive ?>" data-bs-toggle="tab" href="#endpoints" aria-selected="<?= $endpointsActive ? 'true' : 'false' ?>" role="tab">Endpoint usage</a>
</li>
<?php } ?>
<?php if (!$app) { ?>
Expand All @@ -169,7 +216,7 @@ function getLog($logfile, $page = 1, $app = false)
<?php } ?>
</ul>
<div id="myTabContent" class="tab-content">
<div class="tab-pane fade show active" id="access" role="tabpanel">
<div class="tab-pane fade <?= $accessActive ? 'show active' : '' ?>" id="access" role="tabpanel">
<table class="table table-no-squish table-bordered table-hover">
<?php
if ($lines) {
Expand Down Expand Up @@ -225,7 +272,7 @@ function getLog($logfile, $page = 1, $app = false)
$starrApp = $starr->getStarrInterfaceNameFromId($proxiedApp['starrAppDetails']['starr']);

?>
<div class="tab-pane fade" id="endpoints" role="tabpanel">
<div class="tab-pane fade <?= $endpointsActive ? 'show active' : '' ?>" id="endpoints" role="tabpanel">
<h4>Endpoint usage <span class="text-small">(<?= count($endpointUsage) ?> endpoint<?= count($endpointUsage) == 1 ? '' : 's' ?>)</span></h4>
<?php
foreach ($endpointUsage as $endpoint => $methods) {
Expand Down
164 changes: 141 additions & 23 deletions root/app/www/public/js/logs.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,98 @@
const LOG_REFRESH_INTERVAL = 3000;
const LOG_LIST_REFRESH_INTERVAL = 10000;

let currentLog = null;

//-- Polls tick() every interval ms, waits for the previous request to finish and pauses while the browser tab is hidden
function createPoller(interval, tick)
{
let timer = null;
let running = false;

function schedule()
{
clearTimeout(timer);
timer = running && !document.hidden ? setTimeout(run, interval) : null;
}

function run()
{
$.when(tick()).always(schedule);
}

document.addEventListener('visibilitychange', schedule);

return {
start: function () {
running = true;
schedule();
},
stop: function () {
running = false;
schedule();
},
toggle: function () {
running ? this.stop() : this.start();
return running;
}
};
}
// -------------------------------------------------------------------------------------------
function setRefreshButton(button, enabled)
{
$(button).find('i').toggleClass('fa-sync', enabled).toggleClass('fa-pause', !enabled);
$(button).attr('title', enabled ? 'Pause auto-refresh' : 'Resume auto-refresh');
}
// -------------------------------------------------------------------------------------------
function keepScroll(container, update)
{
const containerScroll = $(container).scrollTop();
const windowScroll = window.scrollY;

update();

$(container).scrollTop(containerScroll);
window.scrollTo(0, windowScroll);
}
// -------------------------------------------------------------------------------------------
const logPoller = createPoller(LOG_REFRESH_INTERVAL, function () {
if (!currentLog) {
return;
}

return $.post('ajax/logs.php', { m: 'viewLog', log: currentLog.log, page: currentLog.page, index: currentLog.index }, function (resultData) {
keepScroll('#log-viewer', function () {
$('#log-viewer').html(resultData);
});
});
});
// -------------------------------------------------------------------------------------------
const logListPoller = createPoller(LOG_LIST_REFRESH_INTERVAL, function () {
return $.post('ajax/logs.php', { m: 'refreshLogList' }, function (resultData) {
$('#log-sidebar').html(resultData);

if (currentLog) {
$('#app-log-' + currentLog.index).addClass('text-info');
}
});
});
// -------------------------------------------------------------------------------------------
let appLog = null;

const appLogPoller = createPoller(LOG_REFRESH_INTERVAL, function () {
if (!appLog || !$('#viewAppLog').is(':visible')) {
return;
}

const activeTab = $('#viewAppLog .nav-link.active').attr('href') || '#access';

return $.post('ajax/logs.php', { m: 'viewAppLog', key: appLog.key, log: appLog.log, activeTab: activeTab }, function (resultData) {
keepScroll('#viewAppLog .modal-body', function () {
$('#viewAppLog .modal-body').html(resultData);
});
});
});
// -------------------------------------------------------------------------------------------
function viewLog(log, index, page = 1)
{
loadingStart();
Expand All @@ -6,42 +101,59 @@ function viewLog(log, index, page = 1)
$('[id^=app-log-]').removeClass('text-info');
$('#app-log-' + index).addClass('text-info');

$.ajax({
type: 'POST',
url: 'ajax/logs.php',
data: '&m=viewLog&log=' + log + '&page=' + page + '&index=' + index,
success: function (resultData) {
$('#log-viewer').html(resultData);
loadingStop();
}
currentLog = { log: log, index: index, page: page };

$.post('ajax/logs.php', { m: 'viewLog', log: log, page: page, index: index }, function (resultData) {
$('#log-viewer').html(resultData);
loadingStop();
});
}
// -------------------------------------------------------------------------------------------
function toggleLogAutoRefresh()
{
const enabled = logPoller.toggle();

enabled ? logListPoller.start() : logListPoller.stop();
setRefreshButton('#auto-refresh-toggle', enabled);
$('#auto-refresh-status').text(enabled ? LOG_REFRESH_INTERVAL / 1000 + 's' : 'off');
}
// -------------------------------------------------------------------------------------------
function viewAppLog(log, key, appName)
{
loadingStart();

$.ajax({
type: 'POST',
url: 'ajax/logs.php',
data: '&m=viewAppLog&key=' + key + '&log=' + log,
success: function (resultData) {
dialogOpen({
id: 'viewAppLog',
title: 'Access log viewer: ' + appName + ' (filter: ' + key + ')',
size: 'xxl',
body: resultData,
onOpen: function() {
loadingStop();
}
});
}
appLog = { log: log, key: key };

$.post('ajax/logs.php', { m: 'viewAppLog', key: key, log: log }, function (resultData) {
dialogOpen({
id: 'viewAppLog',
title: 'Access log viewer: ' + appName + ' (filter: ' + key + ')' +
' <button id="app-log-refresh-toggle" class="btn btn-sm btn-outline-success border-light ms-3" onclick="toggleAppLogAutoRefresh()" title="Pause auto-refresh">' +
'<i class="fas fa-sync"></i></button>',
size: 'xxl',
body: resultData,
onOpen: function () {
loadingStop();
appLogPoller.start();
},
onClose: function () {
appLogPoller.stop();
appLog = null;
}
});
});
}
// -------------------------------------------------------------------------------------------
function toggleAppLogAutoRefresh()
{
setRefreshButton('#app-log-refresh-toggle', appLogPoller.toggle());
}
// -------------------------------------------------------------------------------------------
function deleteLog(log)
{
if (confirm('Are you sure you want to delete the log: ' + log + '?')) {
logPoller.stop();

$.ajax({
type: 'POST',
url: 'ajax/logs.php',
Expand All @@ -53,3 +165,9 @@ function deleteLog(log)
}
}
// -------------------------------------------------------------------------------------------
$(document).ready(function () {
if ($('#log-sidebar').length) {
logPoller.start();
logListPoller.start();
}
});
50 changes: 9 additions & 41 deletions root/app/www/public/pages/logs.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,51 +15,19 @@
exit('Invalid session, refresh the page');
}

$logList = getLogs();
?>

<div class="row p3">
<div class="col-sm-3">
<?php
$index = 0;
foreach ($logList['system'] as $group => $groupLogs) {
?>
<h4 class="mt-2 fst-italic"><?= rtrim($group, '/') ?></h4><?php
foreach ($groupLogs as $log) {
$index++;
?>
<ul style="margin-bottom: 0px; padding-bottom: 0px;">
<li>
<i class="far fa-trash-alt text-danger" title="Delete access log" style="cursor:pointer;" onclick="deleteLog('<?= $log ?>')"></i>
<span id="app-log-<?= $index ?>" style="cursor:pointer;" onclick="viewLog('<?= $log ?>', <?= $index ?>)"><?= basename($log) ?> (<?= date('m/d', filemtime($log)) ?>)</span>
<div style="float: right;"><?= byteConversion(filesize($log)) ?></div>
</li>
</ul>
<?php
}
}

foreach ($logList as $app => $logs) {
if ($app == 'system') {
continue;
}

?>
<h4 class="mt-2"><?= $app ?></h4><?php
foreach ($logs as $log) {
$index++;
?>
<ul style="margin-bottom: 0px; padding-bottom: 0px;">
<li>
<i class="far fa-trash-alt text-danger" title="Delete access log" style="cursor:pointer;" onclick="deleteLog('<?= $log ?>')"></i>
<span id="app-log-<?= $index ?>" style="cursor:pointer;" onclick="viewLog('<?= $log ?>', <?= $index ?>)"><?= str_replace('access_', '', basename($log)) ?> (<?= date('m/d', filemtime($log)) ?>)</span>
<div style="float: right;"><?= byteConversion(filesize($log)) ?></div>
</li>
</ul>
<?php
}
}
?>
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="text-muted small">Auto-refresh: <span id="auto-refresh-status">3s</span></span>
<button id="auto-refresh-toggle" class="btn btn-sm btn-outline-success border-light" onclick="toggleLogAutoRefresh()" title="Toggle auto-refresh">
<i class="fas fa-sync"></i>
</button>
</div>
<div id="log-sidebar">
<?php renderLogList(); ?>
</div>
</div>
<div class="col-sm-9">
<div id="log-viewer" class="mt-3"></div>
Expand Down