Menu="UNRAID-OS"
Title="System Log"
Icon="icon-log"
Tag="list"
---
<?php
/* Copyright 2005-2023, Lime Technology
 * Copyright 2012-2023, Bergware International.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */
?>
<?php
$zip    = htmlspecialchars(str_replace(' ', '_', strtolower($var['NAME'])));
$log    = '/var/log/syslog';
$prev   = '/boot/logs/syslog-previous';
$cfg    = '/boot/config/rsyslog.cfg';
$max    = 5000;
$select = [];
$logs   = [];
if (file_exists($cfg)) {
    $syslog = parse_ini_file($cfg);
    if (!empty($syslog['local_server']) && !empty($syslog['server_folder']) && $logs = glob($syslog['server_folder'].'/syslog-*.log', GLOB_NOSORT)) {
        natsort($logs);
    }
}
if (file_exists($prev)) {
    // add syslog-previous to front of logs array
    array_unshift($logs, $prev);
}
if (count($logs)) {
    // add syslog to front of logs array
    array_unshift($logs, $log);
    $select[] = "<select onchange='showLog(this.value)'>";
    foreach ($logs as $file) {
        $select[] = mk_option(1, $file, basename($file));
    }
    $select[] = "</select>";
}
$select = implode($select);
?>
<script>
var logfile = "<?=$log?>";

function zipfile(){
  var d = new Date();
  return "<?=$zip?>-"+logfile.split('/').reverse()[0].replace('.log','')+'-'+d.toISOString().substr(0,16).replace(/[-:]/g,'').replace('T','-')+'.zip';
}
function cleanUp(file) {
  if (document.hasFocus()) {
    $('input#download').val("_(Download)_").prop('disabled',false);
    $.post('/webGui/include/Download.php',{cmd:'delete',file:file});
  } else {
    setTimeout(function(){cleanUp(file);},2000);
  }
}
function syslog(file) {
  $('input#download').val("_(Downloading)_...").prop('disabled',true);
  $.post('/webGui/include/Download.php',{cmd:'save',source:logfile,file:file},function(zip) {
    location = zip;
    setTimeout(function(){cleanUp(file);},4000);
  });
}
function highlight(checked,line) {
  var o = checked ? '-' : '';
  var n = ($('span.text').css('display')=='none' && !checked) ? 'none' : '';
  switch (line) {
    case 'E': $('span.'+o+'error').css('display',n); $('span.error'+o).toggleClass('error -error error-'); break;
    case 'W': $('span.'+o+'warn').css('display',n); $('span.warn'+o).toggleClass('warn -warn warn-'); break;
    case 'S': $('span.'+o+'system').css('display',n); $('span.system'+o).toggleClass('system -system system-'); break;
    case 'A': $('span.'+o+'array').css('display',n); $('span.array'+o).toggleClass('array -array array-'); break;
    case 'L': $('span.'+o+'login').css('display',n); $('span.login'+o).toggleClass('login -login login-'); break;
    case 'N': $('span.text,span[class^="-"]').css('display',checked ? 'none':''); break;
  }
  $('span.label').show();
}
function toggle(checked) {
  highlight(checked,'E');
  highlight(checked,'W');
  highlight(checked,'S');
  highlight(checked,'A');
  highlight(checked,'L');
  $('span.label input[type=checkbox]').not('.ctrl').prop('checked',checked);
}

function showLog(log) {
	logfile = log;
	$('span.label input[type=checkbox]').prop('checked', true);
	$('span.label').each(function() {
		var type = $(this).attr('class').replace('label', '').replace(/-/g, '');
		$(this).removeClass().addClass(type + ' label');
	});
	timers.syslog = setTimeout(function() { $('div.spinner.fixed').show('slow'); }, 500);

	$.post('/webGui/include/Syslog.php', { log: log, max: $('#max').val() || <?=$max?> }, function(data) {
		clearTimeout(timers.syslog);
		let logContainer = document.querySelector("pre.up");
		if (!logContainer) return;  // Safety check

		$('pre.up').html(data);
		$('div.spinner.fixed').hide('slow');

		/* Ensuring reliable scroll behavior on Chrome */
		function scrollToBottom() {
			logContainer.style.scrollBehavior = 'smooth';
			logContainer.scrollTop = logContainer.scrollHeight;
		}

		/* 1. Delay with setTimeout */
		setTimeout(scrollToBottom, 50);

		/* 2. Force Chrome to reflow/repaint */
		logContainer.style.display = "none";
		setTimeout(function() {
			logContainer.style.display = "block";
			scrollToBottom();
		}, 10);

		/* 3. Use requestAnimationFrame */
		requestAnimationFrame(scrollToBottom);

		/* 4. Repeat scroll a few times as a backup (Fixes rare cases in Chrome) */
		setTimeout(scrollToBottom, 100);
		setTimeout(scrollToBottom, 500);
	});
}

$(function() {
  $('input#max').on('keydown',function(e) {
    if (e.keyCode === 13) {
      e.preventDefault();
      e.stopImmediatePropagation();
      showLog(logfile);
    }
  });
  showLog(logfile);

  <?if (_var($display,'resize')):?>
  fillAvailableHeight({
      targetElementSelector: '.js-fill-available-height',
      elementSelectorsForHeight: [
        '.js-syslog-controls',
        '.js-syslog-actions',
      ],
    });
  <?endif;?>
});
</script>

<div class="js-syslog-controls syslog-controls">
  <div class="flex flex-row justify-between items-center gap-4 flex-wrap w-full">
    <div class="flex flex-row items-center gap-4">
      <div class="lite label">
        <label class="flex flex-row items-center gap-2">
          <span class="flex-shrink-0">_(Log size)_:</span>
          <input type="number" id="max" value="" placeholder="<?=$max?>">
        </label>
      </div>

      <?if (!empty($select)):?>
      <div class="lite label">
        <label class="flex flex-row items-center gap-2">
          <span class="flex-shrink-0">_(Log file)_:</span>
          <?= $select ?>
        </label>
      </div>
      <?endif;?>
    </div>

    <div class="flex flex-wrap flex-row items-center gap-3">
      <div class="lite label">
        <label class="flex flex-row items-center gap-2">
          <input type="checkbox" class="ctrl" onclick="highlight(!this.checked, 'N')" checked>
          _(Text)_
        </label>
      </div>
      <span class="error label">
        <label class="flex flex-row items-center gap-2">
          <input type="checkbox" onclick="highlight(this.checked, 'E')" checked>
          _(Error)_
        </label>
      </span>
      <span class="warn label">
        <label class="flex flex-row items-center gap-2">
          <input type="checkbox" onclick="highlight(this.checked, 'W')" checked>
          _(Warning)_
        </label>
      </span>
      <span class="system label">
        <label class="flex flex-row items-center gap-2">
          <input type="checkbox" onclick="highlight(this.checked, 'S')" checked>
          _(System)_
        </label>
      </span>
      <span class="array label">
        <label class="flex flex-row items-center gap-2">
          <input type="checkbox" onclick="highlight(this.checked, 'A')" checked>
          _(Array)_
        </label>
      </span>
      <span class="login label">
        <label class="flex flex-row items-center gap-2">
          <input type="checkbox" onclick="highlight(this.checked, 'L')" checked>
          _(Login)_
        </label>
      </span>
      <span class="lite label">
        <label class="flex items-center gap-2">
          <input type="checkbox" class="ctrl" onclick="toggle(this.checked)" checked>
          _(Toggle All)_
        </label>
      </span>
    </div>
  </div>
</div>

<pre class="js-fill-available-height up"></pre>

<div class="js-syslog-actions flex flex-row items-center gap-2">
  <input type="button" id="download" value="_(Download)_" onclick="syslog(zipfile())">
  <input type="button" value="_(Refresh)_" onclick="showLog(logfile)">
  <input type="button" value="_(Done)_" onclick="done()">
</div>
