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