1<?php 2 3use dokuwiki\plugin\sentry\Event; 4 5/** 6 * DokuWiki Plugin sentry (Action Component) 7 * 8 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9 * @author Andreas Gohr, Michael Große <dokuwiki@cosmocode.de> 10 */ 11class action_plugin_sentry_errors extends DokuWiki_Action_Plugin 12{ 13 14 protected $lastHandledError; 15 16 /** 17 * Registers a callback function for a given event 18 * 19 * @param Doku_Event_Handler $controller DokuWiki's event controller object 20 * 21 * @return void 22 */ 23 public function register(Doku_Event_Handler $controller) 24 { 25 if (!$this->getConf('dsn')) { 26 return; 27 } 28 29 // catch all exceptions 30 set_exception_handler([$this, 'exceptionHandler']); 31 // log non fatal errors 32 set_error_handler([$this, 'errorHandler']); 33 // log fatal errors 34 register_shutdown_function([$this, 'fatalHandler']); 35 36 // retry to send pending events 37 $controller->register_hook('INDEXER_TASKS_RUN', 'AFTER', $this, 'handle_indexer'); 38 39 // log deprecated function use 40 $controller->register_hook('INFO_DEPRECATION_LOG', 'AFTER', $this, 'handle_deprecation'); 41 } 42 43 /** 44 * Send pending tasks on indexer run 45 * 46 * Called for event: INDEXER_TASKS_RUN 47 * 48 * @param Doku_Event $event event object by reference 49 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 50 * handler was registered] 51 * 52 * @return void 53 */ 54 public function handle_indexer(Doku_Event $event, $param) 55 { 56 /** @var helper_plugin_sentry $helper */ 57 $helper = plugin_load('helper', 'sentry'); 58 $events = $helper->getPendingEventIDs(); 59 if (!count($events)) { 60 return; 61 } 62 63 $event->preventDefault(); 64 $event->stopPropagation(); 65 66 foreach ($events as $eid) { 67 $event = $helper->loadEvent($eid); 68 if ($event === null) { 69 continue; 70 } 71 if ($helper->sendEvent($event)) { 72 $helper->deleteEvent($eid); 73 } 74 } 75 } 76 77 /** 78 * Send deprecated function use to Sentry 79 * 80 * Called for event: INFO_DEPRECATION_LOG 81 * 82 * @param Doku_Event $event event object by reference 83 * @param mixed $param [the parameters passed as fifth argument to register_hook() when this 84 * handler was registered] 85 * 86 * @return void 87 */ 88 public function handle_deprecation(Doku_Event $event, $param) 89 { 90 /** @var helper_plugin_sentry $helper */ 91 $helper = plugin_load('helper', 'sentry'); 92 93 $msg = $event->data['called'] . ' is deprecated.'; 94 if (!empty($event->data['alternative'])) { 95 $msg .= ' Use ' . $event->data['alternative'] . ' instead.'; 96 } 97 98 $data = [ 99 'exception' => [ 100 'values' => [ 101 [ 102 'type' => 'Deprecated', 103 'value' => $msg, 104 'stacktrace' => [ 105 'frames' => Event::backTraceFrames($event->data['trace']), 106 ], 107 ], 108 ], 109 ], 110 'level' => Event::LVL_WARN, 111 ]; 112 113 $event = new Event($data); 114 $helper->logEvent($event); 115 } 116 117 /** 118 * Log errors that killed the application 119 */ 120 public function fatalHandler() 121 { 122 $error = error_get_last(); 123 if ($error === null) { 124 return; 125 } 126 // was this error already processed in error handler? ignore it 127 if ($error == $this->lastHandledError) { 128 return; 129 } 130 131 /** @var helper_plugin_sentry $helper */ 132 $helper = plugin_load('helper', 'sentry'); 133 $event = Event::fromError($error); 134 $helper->logEvent($event); 135 } 136 137 /** 138 * Send exceptions to sentry 139 * 140 * @param \Throwable|\Exception $e 141 */ 142 public function exceptionHandler($e) 143 { 144 /** @var helper_plugin_sentry $helper */ 145 $helper = plugin_load('helper', 'sentry'); 146 $helper->logException($e); 147 echo $helper->formatException($e); 148 } 149 150 /** 151 * Error handler to log old school warnings, notices, etc 152 * 153 * @param int $type 154 * @param string $message 155 * @param string $file 156 * @param int $line 157 * @return false we always let the default handler continue 158 */ 159 public function errorHandler($type, $message, $file, $line) 160 { 161 $error = compact('type', 'message', 'file', 'line'); 162 $this->lastHandledError = $error; 163 164 // error_reporting = 0 -> error was supressed, never handle it 165 if (error_reporting() === 0) { 166 return false; 167 } 168 169 /** @var helper_plugin_sentry $helper */ 170 $helper = plugin_load('helper', 'sentry'); 171 172 // Check if this error code is wanted for sentry logging 173 if (!($helper->errorReporting() & $type)) { 174 return false; 175 } 176 177 // add backtrace 178 $error['trace'] = debug_backtrace(); 179 array_shift($error['trace']); 180 181 // log it 182 $event = Event::fromError($error); 183 $helper->logEvent($event); 184 185 return false; 186 } 187 188} 189 190