*/
class admin_plugin_logviewer extends AdminPlugin
{
protected const MAX_READ_SIZE = 1_048_576; // 1 MB
protected $facilities;
protected $facility;
protected $date;
/** @inheritDoc */
public function forAdminOnly()
{
return true;
}
/** @inheritDoc */
public function handle()
{
global $INPUT;
$this->facilities = $this->getFacilities();
$this->facility = $INPUT->str('facility');
if (!in_array($this->facility, $this->facilities)) {
$this->facility = $this->facilities[0];
}
$this->date = $INPUT->str('date');
if (!preg_match('/^\d\d\d\d-\d\d-\d\d$/', $this->date)) {
$this->date = date('Y-m-d');
}
}
/** @inheritDoc */
public function html()
{
echo '
';
echo $this->locale_xhtml('intro');
$this->displayTabs();
$this->displayLog();
echo '
';
}
/**
* Show the navigational tabs and date picker
*/
protected function displayTabs()
{
global $ID;
$form = new Form(['method' => 'GET']);
$form->setHiddenField('do', 'admin');
$form->setHiddenField('page', 'logviewer');
$form->setHiddenField('facility', $this->facility);
$form->addTextInput('date', $this->getLang('date'))
->attr('type', 'date')->val($this->date)->addClass('quickselect');
$form->addButton('submit', '>')->attr('type', 'submit');
echo $form->toHTML();
echo '';
foreach ($this->facilities as $facility) {
echo '- ';
if ($facility == $this->facility) {
echo '' . hsc($facility) . '';
} else {
$link = wl(
$ID,
['do' => 'admin', 'page' => 'logviewer', 'date' => $this->date, 'facility' => $facility]
);
echo '' . hsc($facility) . '';
}
echo '
';
}
echo '
';
}
/**
* Read and output the logfile contents
*/
protected function displayLog()
{
$logfile = Logger::getInstance($this->facility)->getLogfile($this->date);
if (!file_exists($logfile)) {
echo $this->locale_xhtml('nolog');
return;
}
try {
$lines = $this->getLogLines($logfile);
$this->printLogLines($lines);
} catch (Exception $e) {
msg($e->getMessage(), -1);
}
}
/**
* Get the available logging facilities
*
* @return array
*/
protected function getFacilities()
{
global $conf;
// default facilities first
$facilities = [
Logger::LOG_ERROR,
Logger::LOG_DEPRECATED,
Logger::LOG_DEBUG,
];
// add all other dirs
$dirs = glob($conf['logdir'] . '/*', GLOB_ONLYDIR);
foreach ($dirs as $dir) {
$facilities[] = basename($dir);
}
$facilities = array_unique($facilities);
return $facilities;
}
/**
* Read the lines of the logfile and return them as array
*
* @param string $logfilePath
* @return array
* @throws Exception when reading fails
*/
protected function getLogLines($logfilePath)
{
global $lang;
$size = filesize($logfilePath);
$fp = fopen($logfilePath, 'r');
if (!$fp) throw new Exception($lang['log_file_failed_to_open']);
try {
if ($size < self::MAX_READ_SIZE) {
$toread = $size;
} else {
$toread = self::MAX_READ_SIZE;
fseek($fp, -$toread, SEEK_END);
}
$logData = fread($fp, $toread);
if (!$logData) throw new Exception($lang['log_file_failed_to_read']);
$lines = explode("\n", $logData);
unset($logData); // free memory early
if ($size >= self::MAX_READ_SIZE) {
array_shift($lines); // Discard the first line
while ($lines !== [] && str_starts_with($lines[0], ' ')) {
array_shift($lines); // Discard indented lines
}
// A message to inform users that previous lines are skipped
array_unshift($lines, "******\t" . "\t" . '[' . $lang['log_file_too_large'] . ']');
}
} finally {
fclose($fp);
}
return $lines;
}
/**
* Get an array of log lines and print them using appropriate styles
*
* @param array $lines
*/
protected function printLogLines($lines)
{
$numberOfLines = count($lines);
echo "";
for ($i = 0; $i < $numberOfLines; $i++) {
$line = $lines[$i];
if (str_starts_with($line, ' ')) {
// lines indented by two spaces are details, aggregate them
echo '- ';
while (str_starts_with($line, ' ')) {
echo hsc(substr($line, 2)) . '
';
$i++;
$line = $lines[$i] ?? '';
}
echo ' ';
--$i; // rewind the counter
} else {
// other lines are actual log lines in three parts
[$dt, $file, $msg] = sexplode("\t", $line, 3, '');
echo '- ';
echo '' . hsc($dt) . '';
echo '';
echo '' . hsc($msg) . '';
echo '' . hsc($file) . '';
echo '';
echo '
';
}
}
echo "
";
}
}