xref: /dokuwiki/lib/plugins/logviewer/admin.php (revision d4059ee791f33aa71e3fde699e6922080850a6e5)
170cc2cbfSAndreas Gohr<?php
270cc2cbfSAndreas Gohr
38553d24dSAndreas Gohruse dokuwiki\Extension\AdminPlugin;
454cc7aa4SAndreas Gohruse dokuwiki\Form\Form;
570cc2cbfSAndreas Gohruse dokuwiki\Logger;
670cc2cbfSAndreas Gohr
770cc2cbfSAndreas Gohr/**
870cc2cbfSAndreas Gohr * DokuWiki Plugin logviewer (Admin Component)
970cc2cbfSAndreas Gohr *
1070cc2cbfSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
1170cc2cbfSAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
1270cc2cbfSAndreas Gohr */
138553d24dSAndreas Gohrclass admin_plugin_logviewer extends AdminPlugin
1470cc2cbfSAndreas Gohr{
15bf9be0e3SAndreas Gohr    protected const MAX_READ_SIZE = 1_048_576; // 1 MB
1670cc2cbfSAndreas Gohr
1770cc2cbfSAndreas Gohr    protected $facilities;
1870cc2cbfSAndreas Gohr    protected $facility;
1970cc2cbfSAndreas Gohr    protected $date;
2070cc2cbfSAndreas Gohr
2170cc2cbfSAndreas Gohr    /** @inheritDoc */
2270cc2cbfSAndreas Gohr    public function forAdminOnly()
2370cc2cbfSAndreas Gohr    {
2470cc2cbfSAndreas Gohr        return true;
2570cc2cbfSAndreas Gohr    }
2670cc2cbfSAndreas Gohr
2770cc2cbfSAndreas Gohr    /** @inheritDoc */
2870cc2cbfSAndreas Gohr    public function handle()
2970cc2cbfSAndreas Gohr    {
3070cc2cbfSAndreas Gohr        global $INPUT;
3170cc2cbfSAndreas Gohr
3270cc2cbfSAndreas Gohr        $this->facilities = $this->getFacilities();
3370cc2cbfSAndreas Gohr        $this->facility = $INPUT->str('facility');
3470cc2cbfSAndreas Gohr        if (!in_array($this->facility, $this->facilities)) {
3570cc2cbfSAndreas Gohr            $this->facility = $this->facilities[0];
3670cc2cbfSAndreas Gohr        }
3770cc2cbfSAndreas Gohr
3870cc2cbfSAndreas Gohr        $this->date = $INPUT->str('date');
3970cc2cbfSAndreas Gohr        if (!preg_match('/^\d\d\d\d-\d\d-\d\d$/', $this->date)) {
40*d4059ee7Shauk92            $this->date = date('Y-m-d');
4170cc2cbfSAndreas Gohr        }
4270cc2cbfSAndreas Gohr    }
4370cc2cbfSAndreas Gohr
4470cc2cbfSAndreas Gohr    /** @inheritDoc */
4570cc2cbfSAndreas Gohr    public function html()
4670cc2cbfSAndreas Gohr    {
4770cc2cbfSAndreas Gohr        echo '<div id="plugin__logviewer">';
4870cc2cbfSAndreas Gohr        echo $this->locale_xhtml('intro');
4970cc2cbfSAndreas Gohr        $this->displayTabs();
5070cc2cbfSAndreas Gohr        $this->displayLog();
5170cc2cbfSAndreas Gohr        echo '</div>';
5270cc2cbfSAndreas Gohr    }
5370cc2cbfSAndreas Gohr
5470cc2cbfSAndreas Gohr    /**
5570cc2cbfSAndreas Gohr     * Show the navigational tabs and date picker
5670cc2cbfSAndreas Gohr     */
5770cc2cbfSAndreas Gohr    protected function displayTabs()
5870cc2cbfSAndreas Gohr    {
5970cc2cbfSAndreas Gohr        global $ID;
6070cc2cbfSAndreas Gohr
6154cc7aa4SAndreas Gohr        $form = new Form(['method' => 'GET']);
6270cc2cbfSAndreas Gohr        $form->setHiddenField('do', 'admin');
6370cc2cbfSAndreas Gohr        $form->setHiddenField('page', 'logviewer');
6470cc2cbfSAndreas Gohr        $form->setHiddenField('facility', $this->facility);
65bc45a28eSAndreas Gohr        $form->addTextInput('date', $this->getLang('date'))
66bc45a28eSAndreas Gohr            ->attr('type', 'date')->val($this->date)->addClass('quickselect');
6770cc2cbfSAndreas Gohr        $form->addButton('submit', '>')->attr('type', 'submit');
6870cc2cbfSAndreas Gohr        echo $form->toHTML();
6970cc2cbfSAndreas Gohr
7070cc2cbfSAndreas Gohr        echo '<ul class="tabs">';
7170cc2cbfSAndreas Gohr        foreach ($this->facilities as $facility) {
7270cc2cbfSAndreas Gohr            echo '<li>';
7370cc2cbfSAndreas Gohr            if ($facility == $this->facility) {
7470cc2cbfSAndreas Gohr                echo '<strong>' . hsc($facility) . '</strong>';
7570cc2cbfSAndreas Gohr            } else {
76dccd6b2bSAndreas Gohr                $link = wl(
77dccd6b2bSAndreas Gohr                    $ID,
78dccd6b2bSAndreas Gohr                    ['do' => 'admin', 'page' => 'logviewer', 'date' => $this->date, 'facility' => $facility]
79dccd6b2bSAndreas Gohr                );
8070cc2cbfSAndreas Gohr                echo '<a href="' . $link . '">' . hsc($facility) . '</a>';
8170cc2cbfSAndreas Gohr            }
8270cc2cbfSAndreas Gohr            echo '</li>';
8370cc2cbfSAndreas Gohr        }
8470cc2cbfSAndreas Gohr        echo '</ul>';
8570cc2cbfSAndreas Gohr    }
8670cc2cbfSAndreas Gohr
8770cc2cbfSAndreas Gohr    /**
88775003a7SHamid     * Read and output the logfile contents
8970cc2cbfSAndreas Gohr     */
9070cc2cbfSAndreas Gohr    protected function displayLog()
9170cc2cbfSAndreas Gohr    {
9270cc2cbfSAndreas Gohr        $logfile = Logger::getInstance($this->facility)->getLogfile($this->date);
9370cc2cbfSAndreas Gohr        if (!file_exists($logfile)) {
9470cc2cbfSAndreas Gohr            echo $this->locale_xhtml('nolog');
9570cc2cbfSAndreas Gohr            return;
9670cc2cbfSAndreas Gohr        }
9770cc2cbfSAndreas Gohr
98775003a7SHamid        try {
99775003a7SHamid            $lines = $this->getLogLines($logfile);
100775003a7SHamid            $this->printLogLines($lines);
101775003a7SHamid        } catch (Exception $e) {
102775003a7SHamid            msg($e->getMessage(), -1);
103775003a7SHamid        }
104775003a7SHamid    }
10570cc2cbfSAndreas Gohr
106775003a7SHamid    /**
107775003a7SHamid     * Get the available logging facilities
108775003a7SHamid     *
109775003a7SHamid     * @return array
110775003a7SHamid     */
111775003a7SHamid    protected function getFacilities()
112775003a7SHamid    {
113775003a7SHamid        global $conf;
114775003a7SHamid
115775003a7SHamid        // default facilities first
116775003a7SHamid        $facilities = [
117775003a7SHamid            Logger::LOG_ERROR,
118775003a7SHamid            Logger::LOG_DEPRECATED,
119775003a7SHamid            Logger::LOG_DEBUG,
120775003a7SHamid        ];
121775003a7SHamid
122775003a7SHamid        // add all other dirs
123775003a7SHamid        $dirs = glob($conf['logdir'] . '/*', GLOB_ONLYDIR);
124775003a7SHamid        foreach ($dirs as $dir) {
125775003a7SHamid            $facilities[] = basename($dir);
126775003a7SHamid        }
127775003a7SHamid        $facilities = array_unique($facilities);
128775003a7SHamid
129775003a7SHamid        return $facilities;
130775003a7SHamid    }
131775003a7SHamid
132775003a7SHamid    /**
133775003a7SHamid     * Read the lines of the logfile and return them as array
134775003a7SHamid     *
135775003a7SHamid     * @param string $logfilePath
136775003a7SHamid     * @return array
137775003a7SHamid     * @throws Exception when reading fails
138775003a7SHamid     */
139775003a7SHamid    protected function getLogLines($logfilePath)
140775003a7SHamid    {
141775003a7SHamid        global $lang;
142775003a7SHamid        $size = filesize($logfilePath);
143775003a7SHamid        $fp = fopen($logfilePath, 'r');
144775003a7SHamid
145775003a7SHamid        if (!$fp) throw new Exception($lang['log_file_failed_to_open']);
146775003a7SHamid
1479c697479Sfiwswe        try {
148775003a7SHamid            if ($size < self::MAX_READ_SIZE) {
149775003a7SHamid                $toread = $size;
150775003a7SHamid            } else {
151775003a7SHamid                $toread = self::MAX_READ_SIZE;
152775003a7SHamid                fseek($fp, -$toread, SEEK_END);
153775003a7SHamid            }
154775003a7SHamid
155775003a7SHamid            $logData = fread($fp, $toread);
156775003a7SHamid            if (!$logData) throw new Exception($lang['log_file_failed_to_read']);
157775003a7SHamid
158775003a7SHamid            $lines = explode("\n", $logData);
159775003a7SHamid            unset($logData); // free memory early
160775003a7SHamid
1611eb39399Sfiwswe            if ($size >= self::MAX_READ_SIZE) {
162775003a7SHamid                array_shift($lines); // Discard the first line
1631b2deed9Sfiwswe                while ($lines !== [] && str_starts_with($lines[0], '  ')) {
164775003a7SHamid                    array_shift($lines); // Discard indented lines
165775003a7SHamid                }
166775003a7SHamid
167775003a7SHamid                // A message to inform users that previous lines are skipped
168775003a7SHamid                array_unshift($lines, "******\t" . "\t" . '[' . $lang['log_file_too_large'] . ']');
169775003a7SHamid            }
1709c697479Sfiwswe        } finally {
171775003a7SHamid            fclose($fp);
1729c697479Sfiwswe        }
1739c697479Sfiwswe
174775003a7SHamid        return $lines;
175775003a7SHamid    }
176775003a7SHamid
177775003a7SHamid    /**
178775003a7SHamid     * Get an array of log lines and print them using appropriate styles
179775003a7SHamid     *
180775003a7SHamid     * @param array $lines
181775003a7SHamid     */
182775003a7SHamid    protected function printLogLines($lines)
183775003a7SHamid    {
184775003a7SHamid        $numberOfLines = count($lines);
185775003a7SHamid
186775003a7SHamid        echo "<dl>";
187775003a7SHamid        for ($i = 0; $i < $numberOfLines; $i++) {
188775003a7SHamid            $line = $lines[$i];
1891b2deed9Sfiwswe            if (str_starts_with($line, '  ')) {
19070cc2cbfSAndreas Gohr                // lines indented by two spaces are details, aggregate them
19170cc2cbfSAndreas Gohr                echo '<dd>';
1921b2deed9Sfiwswe                while (str_starts_with($line, '  ')) {
19370cc2cbfSAndreas Gohr                    echo hsc(substr($line, 2)) . '<br />';
194586feb6eSAndreas Gohr                    $i++;
195586feb6eSAndreas Gohr                    $line = $lines[$i] ?? '';
19670cc2cbfSAndreas Gohr                }
19770cc2cbfSAndreas Gohr                echo '</dd>';
19854cc7aa4SAndreas Gohr                --$i; // rewind the counter
19970cc2cbfSAndreas Gohr            } else {
20070cc2cbfSAndreas Gohr                // other lines are actual log lines in three parts
20154cc7aa4SAndreas Gohr                [$dt, $file, $msg] = sexplode("\t", $line, 3, '');
20270cc2cbfSAndreas Gohr                echo '<dt>';
20370cc2cbfSAndreas Gohr                echo '<span class="datetime">' . hsc($dt) . '</span>';
20470cc2cbfSAndreas Gohr                echo '<span class="log">';
20570cc2cbfSAndreas Gohr                echo '<span class="msg">' . hsc($msg) . '</span>';
20670cc2cbfSAndreas Gohr                echo '<span class="file">' . hsc($file) . '</span>';
20770cc2cbfSAndreas Gohr                echo '</span>';
20870cc2cbfSAndreas Gohr                echo '</dt>';
20970cc2cbfSAndreas Gohr            }
21070cc2cbfSAndreas Gohr        }
211775003a7SHamid        echo "</dl>";
21270cc2cbfSAndreas Gohr    }
21370cc2cbfSAndreas Gohr}
214