xref: /dokuwiki/lib/plugins/logviewer/admin.php (revision 775003a7dfc8824e655f040724abfb8255c6da03)
1<?php
2
3use dokuwiki\Logger;
4
5/**
6 * DokuWiki Plugin logviewer (Admin Component)
7 *
8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
9 * @author  Andreas Gohr <andi@splitbrain.org>
10 */
11class admin_plugin_logviewer extends DokuWiki_Admin_Plugin
12{
13    const MAX_READ_SIZE = 1048576; // 1 MB
14
15    protected $facilities;
16    protected $facility;
17    protected $date;
18
19    /** @inheritDoc */
20    public function forAdminOnly()
21    {
22        return true;
23    }
24
25    /** @inheritDoc */
26    public function handle()
27    {
28        global $INPUT;
29
30        $this->facilities = $this->getFacilities();
31        $this->facility = $INPUT->str('facility');
32        if (!in_array($this->facility, $this->facilities)) {
33            $this->facility = $this->facilities[0];
34        }
35
36        $this->date = $INPUT->str('date');
37        if (!preg_match('/^\d\d\d\d-\d\d-\d\d$/', $this->date)) {
38            $this->date = gmdate('Y-m-d');
39        }
40    }
41
42    /** @inheritDoc */
43    public function html()
44    {
45        echo '<div id="plugin__logviewer">';
46        echo $this->locale_xhtml('intro');
47        $this->displayTabs();
48        $this->displayLog();
49        echo '</div>';
50    }
51
52    /**
53     * Show the navigational tabs and date picker
54     */
55    protected function displayTabs()
56    {
57        global $ID;
58
59        $form = new dokuwiki\Form\Form(['method' => 'GET']);
60        $form->setHiddenField('do', 'admin');
61        $form->setHiddenField('page', 'logviewer');
62        $form->setHiddenField('facility', $this->facility);
63        $form->addTextInput('date', $this->getLang('date'))
64            ->attr('type', 'date')->val($this->date)->addClass('quickselect');
65        $form->addButton('submit', '>')->attr('type', 'submit');
66        echo $form->toHTML();
67
68        echo '<ul class="tabs">';
69        foreach ($this->facilities as $facility) {
70            echo '<li>';
71            if ($facility == $this->facility) {
72                echo '<strong>' . hsc($facility) . '</strong>';
73            } else {
74                $link = wl($ID,
75                    ['do' => 'admin', 'page' => 'logviewer', 'date' => $this->date, 'facility' => $facility]);
76                echo '<a href="' . $link . '">' . hsc($facility) . '</a>';
77            }
78            echo '</li>';
79        }
80        echo '</ul>';
81    }
82
83    /**
84     * Read and output the logfile contents
85     */
86    protected function displayLog()
87    {
88        $logfile = Logger::getInstance($this->facility)->getLogfile($this->date);
89        if (!file_exists($logfile)) {
90            echo $this->locale_xhtml('nolog');
91            return;
92        }
93
94        try {
95            $lines = $this->getLogLines($logfile);
96            $this->printLogLines($lines);
97        } catch (Exception $e) {
98            msg($e->getMessage(), -1);
99        }
100    }
101
102    /**
103     * Get the available logging facilities
104     *
105     * @return array
106     */
107    protected function getFacilities()
108    {
109        global $conf;
110
111        // default facilities first
112        $facilities = [
113            Logger::LOG_ERROR,
114            Logger::LOG_DEPRECATED,
115            Logger::LOG_DEBUG,
116        ];
117
118        // add all other dirs
119        $dirs = glob($conf['logdir'] . '/*', GLOB_ONLYDIR);
120        foreach ($dirs as $dir) {
121            $facilities[] = basename($dir);
122        }
123        $facilities = array_unique($facilities);
124
125        return $facilities;
126    }
127
128    /**
129     * Read the lines of the logfile and return them as array
130     *
131     * @param string $logfilePath
132     * @return array
133     * @throws Exception when reading fails
134     */
135    protected function getLogLines($logfilePath)
136    {
137        global $lang;
138        $size = filesize($logfilePath);
139        $fp = fopen($logfilePath, 'r');
140
141        if (!$fp) throw new Exception($lang['log_file_failed_to_open']);
142
143        if ($size < self::MAX_READ_SIZE) {
144            $toread = $size;
145        } else {
146            $toread = self::MAX_READ_SIZE;
147            fseek($fp, -$toread, SEEK_END);
148        }
149
150        $logData = fread($fp, $toread);
151        if (!$logData) throw new Exception($lang['log_file_failed_to_read']);
152
153        $lines = explode("\n", $logData);
154        unset($logData); // free memory early
155
156        if ($toread === self::MAX_READ_SIZE) {
157            array_shift($lines); // Discard the first line
158            while (!empty($lines) && (substr($lines[0], 0, 2) === '  ')) {
159                array_shift($lines); // Discard indented lines
160            }
161
162            // A message to inform users that previous lines are skipped
163            array_unshift($lines, "******\t" . "\t" . '[' . $lang['log_file_too_large'] . ']');
164        }
165
166        fclose($fp);
167        return $lines;
168    }
169
170    /**
171     * Get an array of log lines and print them using appropriate styles
172     *
173     * @param array $lines
174     */
175    protected function printLogLines($lines)
176    {
177        $numberOfLines = count($lines);
178
179        echo "<dl>";
180        for ($i = 0; $i < $numberOfLines; $i++) {
181            $line = $lines[$i];
182            if (substr($line, 0, 2) === '  ') {
183                // lines indented by two spaces are details, aggregate them
184                echo '<dd>';
185                while (substr($line, 0, 2) === '  ') {
186                    echo hsc(substr($line, 2)) . '<br />';
187                    $i++;
188                    $line = $lines[$i] ?? '';
189                }
190                echo '</dd>';
191                $i -= 1; // rewind the counter
192            } else {
193                // other lines are actual log lines in three parts
194                list($dt, $file, $msg) = sexplode("\t", $line, 3, '');
195                echo '<dt>';
196                echo '<span class="datetime">' . hsc($dt) . '</span>';
197                echo '<span class="log">';
198                echo '<span class="msg">' . hsc($msg) . '</span>';
199                echo '<span class="file">' . hsc($file) . '</span>';
200                echo '</span>';
201                echo '</dt>';
202            }
203        }
204        echo "</dl>";
205    }
206}
207