scratch – Blame information for rev 122
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
120 | office | 1 | <?php |
2 | |||
3 | /* |
||
4 | * This file is part of the Monolog package. |
||
5 | * |
||
6 | * (c) Jordi Boggiano <j.boggiano@seld.be> |
||
7 | * |
||
8 | * For the full copyright and license information, please view the LICENSE |
||
9 | * file that was distributed with this source code. |
||
10 | */ |
||
11 | |||
12 | namespace Monolog; |
||
13 | |||
14 | use Psr\Log\LoggerInterface; |
||
15 | use Psr\Log\LogLevel; |
||
16 | use Monolog\Handler\AbstractHandler; |
||
17 | |||
18 | /** |
||
19 | * Monolog error handler |
||
20 | * |
||
21 | * A facility to enable logging of runtime errors, exceptions and fatal errors. |
||
22 | * |
||
23 | * Quick setup: <code>ErrorHandler::register($logger);</code> |
||
24 | * |
||
25 | * @author Jordi Boggiano <j.boggiano@seld.be> |
||
26 | */ |
||
27 | class ErrorHandler |
||
28 | { |
||
29 | private $logger; |
||
30 | |||
31 | private $previousExceptionHandler; |
||
32 | private $uncaughtExceptionLevel; |
||
33 | |||
34 | private $previousErrorHandler; |
||
35 | private $errorLevelMap; |
||
36 | private $handleOnlyReportedErrors; |
||
37 | |||
38 | private $hasFatalErrorHandler; |
||
39 | private $fatalLevel; |
||
40 | private $reservedMemory; |
||
41 | private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); |
||
42 | |||
43 | public function __construct(LoggerInterface $logger) |
||
44 | { |
||
45 | $this->logger = $logger; |
||
46 | } |
||
47 | |||
48 | /** |
||
49 | * Registers a new ErrorHandler for a given Logger |
||
50 | * |
||
51 | * By default it will handle errors, exceptions and fatal errors |
||
52 | * |
||
53 | * @param LoggerInterface $logger |
||
54 | * @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling |
||
55 | * @param int|false $exceptionLevel a LogLevel::* constant, or false to disable exception handling |
||
56 | * @param int|false $fatalLevel a LogLevel::* constant, or false to disable fatal error handling |
||
57 | * @return ErrorHandler |
||
58 | */ |
||
59 | public static function register(LoggerInterface $logger, $errorLevelMap = array(), $exceptionLevel = null, $fatalLevel = null) |
||
60 | { |
||
61 | //Forces the autoloader to run for LogLevel. Fixes an autoload issue at compile-time on PHP5.3. See https://github.com/Seldaek/monolog/pull/929 |
||
62 | class_exists('\\Psr\\Log\\LogLevel', true); |
||
63 | |||
64 | $handler = new static($logger); |
||
65 | if ($errorLevelMap !== false) { |
||
66 | $handler->registerErrorHandler($errorLevelMap); |
||
67 | } |
||
68 | if ($exceptionLevel !== false) { |
||
69 | $handler->registerExceptionHandler($exceptionLevel); |
||
70 | } |
||
71 | if ($fatalLevel !== false) { |
||
72 | $handler->registerFatalHandler($fatalLevel); |
||
73 | } |
||
74 | |||
75 | return $handler; |
||
76 | } |
||
77 | |||
78 | public function registerExceptionHandler($level = null, $callPrevious = true) |
||
79 | { |
||
80 | $prev = set_exception_handler(array($this, 'handleException')); |
||
81 | $this->uncaughtExceptionLevel = $level; |
||
82 | if ($callPrevious && $prev) { |
||
83 | $this->previousExceptionHandler = $prev; |
||
84 | } |
||
85 | } |
||
86 | |||
87 | public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1, $handleOnlyReportedErrors = true) |
||
88 | { |
||
89 | $prev = set_error_handler(array($this, 'handleError'), $errorTypes); |
||
90 | $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); |
||
91 | if ($callPrevious) { |
||
92 | $this->previousErrorHandler = $prev ?: true; |
||
93 | } |
||
94 | |||
95 | $this->handleOnlyReportedErrors = $handleOnlyReportedErrors; |
||
96 | } |
||
97 | |||
98 | public function registerFatalHandler($level = null, $reservedMemorySize = 20) |
||
99 | { |
||
100 | register_shutdown_function(array($this, 'handleFatalError')); |
||
101 | |||
102 | $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); |
||
103 | $this->fatalLevel = $level; |
||
104 | $this->hasFatalErrorHandler = true; |
||
105 | } |
||
106 | |||
107 | protected function defaultErrorLevelMap() |
||
108 | { |
||
109 | return array( |
||
110 | E_ERROR => LogLevel::CRITICAL, |
||
111 | E_WARNING => LogLevel::WARNING, |
||
112 | E_PARSE => LogLevel::ALERT, |
||
113 | E_NOTICE => LogLevel::NOTICE, |
||
114 | E_CORE_ERROR => LogLevel::CRITICAL, |
||
115 | E_CORE_WARNING => LogLevel::WARNING, |
||
116 | E_COMPILE_ERROR => LogLevel::ALERT, |
||
117 | E_COMPILE_WARNING => LogLevel::WARNING, |
||
118 | E_USER_ERROR => LogLevel::ERROR, |
||
119 | E_USER_WARNING => LogLevel::WARNING, |
||
120 | E_USER_NOTICE => LogLevel::NOTICE, |
||
121 | E_STRICT => LogLevel::NOTICE, |
||
122 | E_RECOVERABLE_ERROR => LogLevel::ERROR, |
||
123 | E_DEPRECATED => LogLevel::NOTICE, |
||
124 | E_USER_DEPRECATED => LogLevel::NOTICE, |
||
125 | ); |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * @private |
||
130 | */ |
||
131 | public function handleException($e) |
||
132 | { |
||
133 | $this->logger->log( |
||
134 | $this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel, |
||
135 | sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), |
||
136 | array('exception' => $e) |
||
137 | ); |
||
138 | |||
139 | if ($this->previousExceptionHandler) { |
||
140 | call_user_func($this->previousExceptionHandler, $e); |
||
141 | } |
||
142 | |||
143 | exit(255); |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * @private |
||
148 | */ |
||
149 | public function handleError($code, $message, $file = '', $line = 0, $context = array()) |
||
150 | { |
||
151 | if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) { |
||
152 | return; |
||
153 | } |
||
154 | |||
155 | // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries |
||
156 | if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { |
||
157 | $level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; |
||
158 | $this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line)); |
||
159 | } |
||
160 | |||
161 | if ($this->previousErrorHandler === true) { |
||
162 | return false; |
||
163 | } elseif ($this->previousErrorHandler) { |
||
164 | return call_user_func($this->previousErrorHandler, $code, $message, $file, $line, $context); |
||
165 | } |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * @private |
||
170 | */ |
||
171 | public function handleFatalError() |
||
172 | { |
||
173 | $this->reservedMemory = null; |
||
174 | |||
175 | $lastError = error_get_last(); |
||
176 | if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) { |
||
177 | $this->logger->log( |
||
178 | $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel, |
||
179 | 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], |
||
180 | array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line']) |
||
181 | ); |
||
182 | |||
183 | if ($this->logger instanceof Logger) { |
||
184 | foreach ($this->logger->getHandlers() as $handler) { |
||
185 | if ($handler instanceof AbstractHandler) { |
||
186 | $handler->close(); |
||
187 | } |
||
188 | } |
||
189 | } |
||
190 | } |
||
191 | } |
||
192 | |||
193 | private static function codeToString($code) |
||
194 | { |
||
195 | switch ($code) { |
||
196 | case E_ERROR: |
||
197 | return 'E_ERROR'; |
||
198 | case E_WARNING: |
||
199 | return 'E_WARNING'; |
||
200 | case E_PARSE: |
||
201 | return 'E_PARSE'; |
||
202 | case E_NOTICE: |
||
203 | return 'E_NOTICE'; |
||
204 | case E_CORE_ERROR: |
||
205 | return 'E_CORE_ERROR'; |
||
206 | case E_CORE_WARNING: |
||
207 | return 'E_CORE_WARNING'; |
||
208 | case E_COMPILE_ERROR: |
||
209 | return 'E_COMPILE_ERROR'; |
||
210 | case E_COMPILE_WARNING: |
||
211 | return 'E_COMPILE_WARNING'; |
||
212 | case E_USER_ERROR: |
||
213 | return 'E_USER_ERROR'; |
||
214 | case E_USER_WARNING: |
||
215 | return 'E_USER_WARNING'; |
||
216 | case E_USER_NOTICE: |
||
217 | return 'E_USER_NOTICE'; |
||
218 | case E_STRICT: |
||
219 | return 'E_STRICT'; |
||
220 | case E_RECOVERABLE_ERROR: |
||
221 | return 'E_RECOVERABLE_ERROR'; |
||
222 | case E_DEPRECATED: |
||
223 | return 'E_DEPRECATED'; |
||
224 | case E_USER_DEPRECATED: |
||
225 | return 'E_USER_DEPRECATED'; |
||
226 | } |
||
227 | |||
228 | return 'Unknown PHP error'; |
||
229 | } |
||
230 | } |