aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/inc/Logger.php
blob: e48636c068986226bcb5c9acadf0c1f09e740d00 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
<?php

namespace dokuwiki;

use dokuwiki\Extension\Event;

/**
 * Log messages to a daily log file
 */
class Logger
{
    const LOG_ERROR = 'error';
    const LOG_DEPRECATED = 'deprecated';
    const LOG_DEBUG = 'debug';

    /** @var Logger[] */
    static protected $instances;

    /** @var string what kind of log is this */
    protected $facility;

    protected $isLogging = true;

    /**
     * Logger constructor.
     *
     * @param string $facility The type of log
     */
    protected function __construct($facility)
    {
        global $conf;
        $this->facility = $facility;

        // Should logging be disabled for this facility?
        $dontlog = explode(',', $conf['dontlog']);
        $dontlog = array_map('trim', $dontlog);
        if (in_array($facility, $dontlog)) $this->isLogging = false;
    }

    /**
     * Return a Logger instance for the given facility
     *
     * @param string $facility The type of log
     * @return Logger
     */
    static public function getInstance($facility = self::LOG_ERROR)
    {
        if (empty(self::$instances[$facility])) {
            self::$instances[$facility] = new Logger($facility);
        }
        return self::$instances[$facility];
    }

    /**
     * Convenience method to directly log to the error log
     *
     * @param string $message The log message
     * @param mixed $details Any details that should be added to the log entry
     * @param string $file A source filename if this is related to a source position
     * @param int $line A line number for the above file
     * @return bool has a log been written?
     */
    static public function error($message, $details = null, $file = '', $line = 0)
    {
        return self::getInstance(self::LOG_ERROR)->log(
            $message, $details, $file, $line
        );
    }

    /**
     * Convenience method to directly log to the debug log
     *
     * @param string $message The log message
     * @param mixed $details Any details that should be added to the log entry
     * @param string $file A source filename if this is related to a source position
     * @param int $line A line number for the above file
     * @return bool has a log been written?
     */
    static public function debug($message, $details = null, $file = '', $line = 0)
    {
        return self::getInstance(self::LOG_DEBUG)->log(
            $message, $details, $file, $line
        );
    }

    /**
     * Convenience method to directly log to the deprecation log
     *
     * @param string $message The log message
     * @param mixed $details Any details that should be added to the log entry
     * @param string $file A source filename if this is related to a source position
     * @param int $line A line number for the above file
     * @return bool has a log been written?
     */
    static public function deprecated($message, $details = null, $file = '', $line = 0)
    {
        return self::getInstance(self::LOG_DEPRECATED)->log(
            $message, $details, $file, $line
        );
    }

    /**
     * Log a message to the facility log
     *
     * @param string $message The log message
     * @param mixed $details Any details that should be added to the log entry
     * @param string $file A source filename if this is related to a source position
     * @param int $line A line number for the above file
     * @triggers LOGGER_DATA_FORMAT can be used to change the logged data or intercept it
     * @return bool has a log been written?
     */
    public function log($message, $details = null, $file = '', $line = 0)
    {
        global $EVENT_HANDLER;
        if (!$this->isLogging) return false;

        $datetime = time();
        $data = [
            'facility' => $this->facility,
            'datetime' => $datetime,
            'message' => $message,
            'details' => $details,
            'file' => $file,
            'line' => $line,
            'loglines' => [],
            'logfile' => $this->getLogfile($datetime),
        ];

        if ($EVENT_HANDLER !== null) {
            $event = new Event('LOGGER_DATA_FORMAT', $data);
            if ($event->advise_before()) {
                $data['loglines'] = $this->formatLogLines($data);
            }
            $event->advise_after();
        } else {
            // The event system is not yet available, to ensure the log isn't lost even on
            // fatal errors, the default action is executed
            $data['loglines'] = $this->formatLogLines($data);
        }

        // only log when any data available
        if (count($data['loglines'])) {
            return $this->writeLogLines($data['loglines'], $data['logfile']);
        } else {
            return false;
        }
    }

    /**
     * Formats the given data as loglines
     *
     * @param array $data Event data from LOGGER_DATA_FORMAT
     * @return string[] the lines to log
     */
    protected function formatLogLines($data)
    {
        extract($data);

        // details are logged indented
        if ($details) {
            if (!is_string($details)) {
                $details = json_encode($details, JSON_PRETTY_PRINT);
            }
            $details = explode("\n", $details);
            $loglines = array_map(function ($line) {
                return '  ' . $line;
            }, $details);
        } elseif ($details) {
            $loglines = [$details];
        } else {
            $loglines = [];
        }

        // datetime, fileline, message
        $logline = gmdate('Y-m-d H:i:s', $datetime) . "\t";
        if ($file) {
            $logline .= $file;
            if ($line) $logline .= "($line)";
        }
        $logline .= "\t" . $message;
        array_unshift($loglines, $logline);

        return $loglines;
    }

    /**
     * Construct the log file for the given day
     *
     * @param false|string|int $date Date to access, false for today
     * @return string
     */
    public function getLogfile($date = false)
    {
        global $conf;

        if($date !== null && !is_numeric($date)) {
            $date = strtotime($date);
        }
        if (!$date) $date = time();

        return $conf['logdir'] . '/' . $this->facility . '/' . date('Y-m-d', $date) . '.log';
    }

    /**
     * Write the given lines to today's facility log
     *
     * @param string[] $lines the raw lines to append to the log
     * @param string $logfile where to write to
     * @return bool true if the log was written
     */
    protected function writeLogLines($lines, $logfile)
    {
        return io_saveFile($logfile, join("\n", $lines) . "\n", true);
    }
}