1<?php
2
3namespace dokuwiki;
4
5class Logger
6{
7    const LOG_ERROR = 'error';
8    const LOG_DEPRECATED = 'deprecated';
9    const LOG_DEBUG = 'debug';
10
11    /** @var Logger[] */
12    static protected $instances;
13
14    /** @var string what kind of log is this */
15    protected $facility;
16
17    protected $isLogging = true;
18
19    /**
20     * Logger constructor.
21     *
22     * @param string $facility The type of log
23     */
24    protected function __construct($facility)
25    {
26        global $conf;
27        $this->facility = $facility;
28
29        // Should logging be disabled for this facility?
30        $dontlog = explode(',', $conf['dontlog']);
31        $dontlog = array_map('trim', $dontlog);
32        if (in_array($facility, $dontlog)) $this->isLogging = false;
33    }
34
35    /**
36     * Return a Logger instance for the given facility
37     *
38     * @param string $facility The type of log
39     * @return Logger
40     */
41    static public function getInstance($facility = self::LOG_ERROR)
42    {
43        if (empty(self::$instances[$facility])) {
44            self::$instances[$facility] = new Logger($facility);
45        }
46        return self::$instances[$facility];
47    }
48
49    /**
50     * Convenience method to directly log to the error log
51     *
52     * @param string $message The log message
53     * @param mixed $details Any details that should be added to the log entry
54     * @param string $file A source filename if this is related to a source position
55     * @param int $line A line number for the above file
56     * @return bool has a log been written?
57     */
58    static public function error($message, $details = null, $file = '', $line = 0)
59    {
60        return self::getInstance(self::LOG_ERROR)->log(
61            $message, $details, $file, $line
62        );
63    }
64
65    /**
66     * Convenience method to directly log to the debug log
67     *
68     * @param string $message The log message
69     * @param mixed $details Any details that should be added to the log entry
70     * @param string $file A source filename if this is related to a source position
71     * @param int $line A line number for the above file
72     * @return bool has a log been written?
73     */
74    static public function debug($message, $details = null, $file = '', $line = 0)
75    {
76        return self::getInstance(self::LOG_DEBUG)->log(
77            $message, $details, $file, $line
78        );
79    }
80
81    /**
82     * Convenience method to directly log to the deprecation log
83     *
84     * @param string $message The log message
85     * @param mixed $details Any details that should be added to the log entry
86     * @param string $file A source filename if this is related to a source position
87     * @param int $line A line number for the above file
88     * @return bool has a log been written?
89     */
90    static public function deprecated($message, $details = null, $file = '', $line = 0)
91    {
92        return self::getInstance(self::LOG_DEPRECATED)->log(
93            $message, $details, $file, $line
94        );
95    }
96
97    /**
98     * Log a message to the facility log
99     *
100     * @param string $message The log message
101     * @param mixed $details Any details that should be added to the log entry
102     * @param string $file A source filename if this is related to a source position
103     * @param int $line A line number for the above file
104     * @return bool has a log been written?
105     */
106    public function log($message, $details = null, $file = '', $line = 0)
107    {
108        if(!$this->isLogging) return false;
109
110        // details are logged indented
111        if ($details) {
112            if (!is_string($details)) {
113                $details = json_encode($details, JSON_PRETTY_PRINT);
114            }
115            $details = explode("\n", $details);
116            $loglines = array_map(function ($line) {
117                return '  ' . $line;
118            }, $details);
119        } elseif ($details) {
120            $loglines = [$details];
121        } else {
122            $loglines = [];
123        }
124
125        // datetime, fileline, message
126        $logline = gmdate('Y-m-d H:i:s') . "\t";
127        if ($file) {
128            $logline .= $file;
129            if ($line) $logline .= "($line)";
130        }
131        $logline .= "\t" . $message;
132
133        array_unshift($loglines, $logline);
134        return $this->writeLogLines($loglines);
135    }
136
137    /**
138     * Construct the log file for the given day
139     *
140     * @param false|string|int $date Date to access, false for today
141     * @return string
142     */
143    public function getLogfile($date = false)
144    {
145        global $conf;
146
147        if ($date !== null) $date = strtotime($date);
148        if (!$date) $date = time();
149
150        return $conf['logdir'] . '/' . $this->facility . '/' . date('Y-m-d', $date) . '.log';
151    }
152
153    /**
154     * Write the given lines to today's facility log
155     *
156     * @param string[] $lines the raw lines to append to the log
157     * @return bool true if the log was written
158     */
159    protected function writeLogLines($lines)
160    {
161        $logfile = $this->getLogfile();
162        return io_saveFile($logfile, join("\n", $lines) . "\n", true);
163    }
164}
165