170cc2cbfSAndreas Gohr<?php 270cc2cbfSAndreas Gohr 370cc2cbfSAndreas Gohruse dokuwiki\Logger; 470cc2cbfSAndreas Gohr 570cc2cbfSAndreas Gohr/** 670cc2cbfSAndreas Gohr * DokuWiki Plugin logviewer (Admin Component) 770cc2cbfSAndreas Gohr * 870cc2cbfSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 970cc2cbfSAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 1070cc2cbfSAndreas Gohr */ 1170cc2cbfSAndreas Gohrclass admin_plugin_logviewer extends DokuWiki_Admin_Plugin 1270cc2cbfSAndreas Gohr{ 13775003a7SHamid const MAX_READ_SIZE = 1048576; // 1 MB 1470cc2cbfSAndreas Gohr 1570cc2cbfSAndreas Gohr protected $facilities; 1670cc2cbfSAndreas Gohr protected $facility; 1770cc2cbfSAndreas Gohr protected $date; 1870cc2cbfSAndreas Gohr 1970cc2cbfSAndreas Gohr /** @inheritDoc */ 2070cc2cbfSAndreas Gohr public function forAdminOnly() 2170cc2cbfSAndreas Gohr { 2270cc2cbfSAndreas Gohr return true; 2370cc2cbfSAndreas Gohr } 2470cc2cbfSAndreas Gohr 2570cc2cbfSAndreas Gohr /** @inheritDoc */ 2670cc2cbfSAndreas Gohr public function handle() 2770cc2cbfSAndreas Gohr { 2870cc2cbfSAndreas Gohr global $INPUT; 2970cc2cbfSAndreas Gohr 3070cc2cbfSAndreas Gohr $this->facilities = $this->getFacilities(); 3170cc2cbfSAndreas Gohr $this->facility = $INPUT->str('facility'); 3270cc2cbfSAndreas Gohr if (!in_array($this->facility, $this->facilities)) { 3370cc2cbfSAndreas Gohr $this->facility = $this->facilities[0]; 3470cc2cbfSAndreas Gohr } 3570cc2cbfSAndreas Gohr 3670cc2cbfSAndreas Gohr $this->date = $INPUT->str('date'); 3770cc2cbfSAndreas Gohr if (!preg_match('/^\d\d\d\d-\d\d-\d\d$/', $this->date)) { 3870cc2cbfSAndreas Gohr $this->date = gmdate('Y-m-d'); 3970cc2cbfSAndreas Gohr } 4070cc2cbfSAndreas Gohr } 4170cc2cbfSAndreas Gohr 4270cc2cbfSAndreas Gohr /** @inheritDoc */ 4370cc2cbfSAndreas Gohr public function html() 4470cc2cbfSAndreas Gohr { 4570cc2cbfSAndreas Gohr echo '<div id="plugin__logviewer">'; 4670cc2cbfSAndreas Gohr echo $this->locale_xhtml('intro'); 4770cc2cbfSAndreas Gohr $this->displayTabs(); 4870cc2cbfSAndreas Gohr $this->displayLog(); 4970cc2cbfSAndreas Gohr echo '</div>'; 5070cc2cbfSAndreas Gohr } 5170cc2cbfSAndreas Gohr 5270cc2cbfSAndreas Gohr /** 5370cc2cbfSAndreas Gohr * Show the navigational tabs and date picker 5470cc2cbfSAndreas Gohr */ 5570cc2cbfSAndreas Gohr protected function displayTabs() 5670cc2cbfSAndreas Gohr { 5770cc2cbfSAndreas Gohr global $ID; 5870cc2cbfSAndreas Gohr 5970cc2cbfSAndreas Gohr $form = new dokuwiki\Form\Form(['method' => 'GET']); 6070cc2cbfSAndreas Gohr $form->setHiddenField('do', 'admin'); 6170cc2cbfSAndreas Gohr $form->setHiddenField('page', 'logviewer'); 6270cc2cbfSAndreas Gohr $form->setHiddenField('facility', $this->facility); 63bc45a28eSAndreas Gohr $form->addTextInput('date', $this->getLang('date')) 64bc45a28eSAndreas Gohr ->attr('type', 'date')->val($this->date)->addClass('quickselect'); 6570cc2cbfSAndreas Gohr $form->addButton('submit', '>')->attr('type', 'submit'); 6670cc2cbfSAndreas Gohr echo $form->toHTML(); 6770cc2cbfSAndreas Gohr 6870cc2cbfSAndreas Gohr echo '<ul class="tabs">'; 6970cc2cbfSAndreas Gohr foreach ($this->facilities as $facility) { 7070cc2cbfSAndreas Gohr echo '<li>'; 7170cc2cbfSAndreas Gohr if ($facility == $this->facility) { 7270cc2cbfSAndreas Gohr echo '<strong>' . hsc($facility) . '</strong>'; 7370cc2cbfSAndreas Gohr } else { 7470cc2cbfSAndreas Gohr $link = wl($ID, 7570cc2cbfSAndreas Gohr ['do' => 'admin', 'page' => 'logviewer', 'date' => $this->date, 'facility' => $facility]); 7670cc2cbfSAndreas Gohr echo '<a href="' . $link . '">' . hsc($facility) . '</a>'; 7770cc2cbfSAndreas Gohr } 7870cc2cbfSAndreas Gohr echo '</li>'; 7970cc2cbfSAndreas Gohr } 8070cc2cbfSAndreas Gohr echo '</ul>'; 8170cc2cbfSAndreas Gohr } 8270cc2cbfSAndreas Gohr 8370cc2cbfSAndreas Gohr /** 84775003a7SHamid * Read and output the logfile contents 8570cc2cbfSAndreas Gohr */ 8670cc2cbfSAndreas Gohr protected function displayLog() 8770cc2cbfSAndreas Gohr { 8870cc2cbfSAndreas Gohr $logfile = Logger::getInstance($this->facility)->getLogfile($this->date); 8970cc2cbfSAndreas Gohr if (!file_exists($logfile)) { 9070cc2cbfSAndreas Gohr echo $this->locale_xhtml('nolog'); 9170cc2cbfSAndreas Gohr return; 9270cc2cbfSAndreas Gohr } 9370cc2cbfSAndreas Gohr 94775003a7SHamid try { 95775003a7SHamid $lines = $this->getLogLines($logfile); 96775003a7SHamid $this->printLogLines($lines); 97775003a7SHamid } catch (Exception $e) { 98775003a7SHamid msg($e->getMessage(), -1); 99775003a7SHamid } 100775003a7SHamid } 10170cc2cbfSAndreas Gohr 102775003a7SHamid /** 103775003a7SHamid * Get the available logging facilities 104775003a7SHamid * 105775003a7SHamid * @return array 106775003a7SHamid */ 107775003a7SHamid protected function getFacilities() 108775003a7SHamid { 109775003a7SHamid global $conf; 110775003a7SHamid 111775003a7SHamid // default facilities first 112775003a7SHamid $facilities = [ 113775003a7SHamid Logger::LOG_ERROR, 114775003a7SHamid Logger::LOG_DEPRECATED, 115775003a7SHamid Logger::LOG_DEBUG, 116775003a7SHamid ]; 117775003a7SHamid 118775003a7SHamid // add all other dirs 119775003a7SHamid $dirs = glob($conf['logdir'] . '/*', GLOB_ONLYDIR); 120775003a7SHamid foreach ($dirs as $dir) { 121775003a7SHamid $facilities[] = basename($dir); 122775003a7SHamid } 123775003a7SHamid $facilities = array_unique($facilities); 124775003a7SHamid 125775003a7SHamid return $facilities; 126775003a7SHamid } 127775003a7SHamid 128775003a7SHamid /** 129775003a7SHamid * Read the lines of the logfile and return them as array 130775003a7SHamid * 131775003a7SHamid * @param string $logfilePath 132775003a7SHamid * @return array 133775003a7SHamid * @throws Exception when reading fails 134775003a7SHamid */ 135775003a7SHamid protected function getLogLines($logfilePath) 136775003a7SHamid { 137775003a7SHamid global $lang; 138775003a7SHamid $size = filesize($logfilePath); 139775003a7SHamid $fp = fopen($logfilePath, 'r'); 140775003a7SHamid 141775003a7SHamid if (!$fp) throw new Exception($lang['log_file_failed_to_open']); 142775003a7SHamid 143*9c697479Sfiwswe try { 144775003a7SHamid if ($size < self::MAX_READ_SIZE) { 145775003a7SHamid $toread = $size; 146775003a7SHamid } else { 147775003a7SHamid $toread = self::MAX_READ_SIZE; 148775003a7SHamid fseek($fp, -$toread, SEEK_END); 149775003a7SHamid } 150775003a7SHamid 151775003a7SHamid $logData = fread($fp, $toread); 152775003a7SHamid if (!$logData) throw new Exception($lang['log_file_failed_to_read']); 153775003a7SHamid 154775003a7SHamid $lines = explode("\n", $logData); 155775003a7SHamid unset($logData); // free memory early 156775003a7SHamid 157e7794be6Sfiwswe if ($size > self::MAX_READ_SIZE) { 158775003a7SHamid array_shift($lines); // Discard the first line 159775003a7SHamid while (!empty($lines) && (substr($lines[0], 0, 2) === ' ')) { 160775003a7SHamid array_shift($lines); // Discard indented lines 161775003a7SHamid } 162775003a7SHamid 163775003a7SHamid // A message to inform users that previous lines are skipped 164775003a7SHamid array_unshift($lines, "******\t" . "\t" . '[' . $lang['log_file_too_large'] . ']'); 165775003a7SHamid } 166*9c697479Sfiwswe } finally { 167775003a7SHamid fclose($fp); 168*9c697479Sfiwswe } 169*9c697479Sfiwswe 170775003a7SHamid return $lines; 171775003a7SHamid } 172775003a7SHamid 173775003a7SHamid /** 174775003a7SHamid * Get an array of log lines and print them using appropriate styles 175775003a7SHamid * 176775003a7SHamid * @param array $lines 177775003a7SHamid */ 178775003a7SHamid protected function printLogLines($lines) 179775003a7SHamid { 180775003a7SHamid $numberOfLines = count($lines); 181775003a7SHamid 182775003a7SHamid echo "<dl>"; 183775003a7SHamid for ($i = 0; $i < $numberOfLines; $i++) { 184775003a7SHamid $line = $lines[$i]; 185428df306Sfiwswe if (substr($line, 0, 2) === ' ') { 18670cc2cbfSAndreas Gohr // lines indented by two spaces are details, aggregate them 18770cc2cbfSAndreas Gohr echo '<dd>'; 188428df306Sfiwswe while (substr($line, 0, 2) === ' ') { 18970cc2cbfSAndreas Gohr echo hsc(substr($line, 2)) . '<br />'; 190586feb6eSAndreas Gohr $i++; 191586feb6eSAndreas Gohr $line = $lines[$i] ?? ''; 19270cc2cbfSAndreas Gohr } 19370cc2cbfSAndreas Gohr echo '</dd>'; 194b9a4556dSAndreas Gohr $i -= 1; // rewind the counter 19570cc2cbfSAndreas Gohr } else { 19670cc2cbfSAndreas Gohr // other lines are actual log lines in three parts 197ec34bb30SAndreas Gohr list($dt, $file, $msg) = sexplode("\t", $line, 3, ''); 19870cc2cbfSAndreas Gohr echo '<dt>'; 19970cc2cbfSAndreas Gohr echo '<span class="datetime">' . hsc($dt) . '</span>'; 20070cc2cbfSAndreas Gohr echo '<span class="log">'; 20170cc2cbfSAndreas Gohr echo '<span class="msg">' . hsc($msg) . '</span>'; 20270cc2cbfSAndreas Gohr echo '<span class="file">' . hsc($file) . '</span>'; 20370cc2cbfSAndreas Gohr echo '</span>'; 20470cc2cbfSAndreas Gohr echo '</dt>'; 20570cc2cbfSAndreas Gohr } 20670cc2cbfSAndreas Gohr } 207775003a7SHamid echo "</dl>"; 20870cc2cbfSAndreas Gohr } 20970cc2cbfSAndreas Gohr} 210