xref: /dokuwiki/lib/plugins/logviewer/admin.php (revision 9c6974793ee2feadeac51bf08bf45b6d07275e8e)
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