├── .gitignore ├── composer.json ├── README.md ├── LICENSE └── src ├── SentryHelper.php ├── Target.php └── ErrorHandler.php /.gitignore: -------------------------------------------------------------------------------- 1 | /composer.lock 2 | /vendor -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e96/yii2-sentry", 3 | "description": "A Yii2 client for Sentry (http://getsentry.com)", 4 | "type": "yii2-extension", 5 | "keywords": ["log", "sentry", "raven", "yii2"], 6 | "minimum-stability": "dev", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "m8rge", 11 | "email": "to.merge@gmail.com", 12 | "homepage": "http://aputilov.ru" 13 | } 14 | ], 15 | "require": { 16 | "php": ">=5.4.0", 17 | "yiisoft/yii2": "*", 18 | "sentry/sentry": "~0.10" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "e96\\sentry\\": "src/" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Yii2 sentry client 2 | ================= 3 | 4 | ##Install 5 | ``` 6 | php composer.phar require e96/yii2-sentry 7 | ``` 8 | 9 | ## Usage 10 | In config file: 11 | 12 | ```php 13 | 'bootstrap' => ['log', 'raven'], 14 | 'components' => [ 15 | 'raven' => [ 16 | 'class' => 'e96\sentry\ErrorHandler', 17 | 'dsn' => '', // Sentry DSN 18 | ], 19 | 'log' => [ 20 | 'targets' => [ 21 | [ 22 | 'class' => 'e96\sentry\Target', 23 | 'levels' => ['error', 'warning'], 24 | 'dsn' => '', // Sentry DSN 25 | ] 26 | ], 27 | ], 28 | ] 29 | ``` 30 | You can provide additional information with exceptions: 31 | ```php 32 | SentryHelper::extraData($task->attributes); 33 | throw new Exception('unknown task type'); 34 | ``` 35 | 36 | Or just capture message with full stacktrace 37 | ```php 38 | try { 39 | throw new Exception('FAIL'); 40 | } catch (Exception $e) { 41 | SentryHelper::captureWithMessage('Fail to save model', $e); 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Putilov Andrey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/SentryHelper.php: -------------------------------------------------------------------------------- 1 | get('raven', false); 17 | if ($raven instanceof ErrorHandler) { 18 | $raven->client->extra_context($data); 19 | return true; 20 | } 21 | 22 | return false; 23 | } 24 | 25 | /** 26 | * @return bool 27 | */ 28 | public static function clearExtraData() 29 | { 30 | /** @var ErrorHandler $raven */ 31 | $raven = \Yii::$app->get('raven', false); 32 | if ($raven instanceof ErrorHandler) { 33 | $raven->client->context->clear(); 34 | return true; 35 | } 36 | 37 | return false; 38 | } 39 | 40 | /** 41 | * @param string $message 42 | * @param null|\Exception $previousException 43 | * @param string $level one of Raven_Client::* levels 44 | * @param string $exceptionClass 45 | * @return bool 46 | * @throws \yii\base\InvalidConfigException 47 | */ 48 | public static function captureWithMessage($message, $previousException = null, $level = \Raven_Client::ERROR, $exceptionClass = 'yii\base\Exception') 49 | { 50 | /** @var ErrorHandler $raven */ 51 | $raven = \Yii::$app->get('raven', false); 52 | if ($raven instanceof ErrorHandler) { 53 | $raven->client->captureException(new $exceptionClass($message, 0, $previousException), ['level' => $level]); 54 | return true; 55 | } 56 | 57 | return false; 58 | } 59 | 60 | /** 61 | * @param \Exception $exception 62 | * @param string $level one of Raven_Client::* levels 63 | * @return bool 64 | * @throws \yii\base\InvalidConfigException 65 | */ 66 | public static function captureException($exception, $level = \Raven_Client::ERROR) 67 | { 68 | /** @var ErrorHandler $raven */ 69 | $raven = \Yii::$app->get('raven', false); 70 | if ($raven instanceof ErrorHandler) { 71 | $raven->client->captureException($exception, ['level' => $level]); 72 | return true; 73 | } 74 | 75 | return false; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Target.php: -------------------------------------------------------------------------------- 1 | client = new \Raven_Client($this->dsn, $this->clientOptions); 32 | } 33 | 34 | protected function getContextMessage() 35 | { 36 | return ''; 37 | } 38 | 39 | /** 40 | * Filter all exceptions. They logged via ErrorHandler 41 | * @inheritdoc 42 | */ 43 | public static function filterMessages($messages, $levels = 0, $categories = [], $except = []) 44 | { 45 | $messages = parent::filterMessages($messages, $levels, $categories, $except); 46 | foreach ($messages as $i => $message) { 47 | $type = explode(':', $message[2]); 48 | // shutdown function not working in yii2 yet: https://github.com/yiisoft/yii2/issues/6637 49 | // allow fatal errors exceptions in log messages 50 | if (is_array($type) && 51 | sizeof($type) == 2 && 52 | $type[0] == 'yii\base\ErrorException' && 53 | ErrorException::isFatalError(['type' => $type[1]]) 54 | ) { 55 | continue; 56 | } 57 | if (strpos($message[0], 'exception \'') === 0) { 58 | unset($messages[$i]); 59 | } 60 | } 61 | 62 | return $messages; 63 | } 64 | 65 | /** 66 | * Exports log [[messages]] to a specific destination. 67 | */ 68 | public function export() 69 | { 70 | foreach ($this->messages as $message) { 71 | list($msg, $level, $category, $timestamp, $traces) = $message; 72 | 73 | $levelName = Logger::getLevelName($level); 74 | if (!in_array($levelName, ['error', 'warning', 'info'])) { 75 | $levelName = 'error'; 76 | } 77 | $data = [ 78 | 'timestamp' => gmdate('Y-m-d\TH:i:s\Z', $timestamp), 79 | 'level' => $levelName, 80 | 'tags' => ['category' => $category], 81 | 'message' => $msg, 82 | ]; 83 | if (!empty($traces)) { 84 | $data['sentry.interfaces.Stacktrace'] = [ 85 | 'frames' => Raven_Stacktrace::get_stack_info($traces), 86 | ]; 87 | } 88 | 89 | $this->client->capture($data, false); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /src/ErrorHandler.php: -------------------------------------------------------------------------------- 1 | client = new \Raven_Client($this->dsn, $this->clientOptions); 44 | 45 | $this->ravenErrorHandler = new \Raven_ErrorHandler($this->client); 46 | $this->ravenErrorHandler->registerErrorHandler(true); 47 | // shutdown function not working in yii2 yet: https://github.com/yiisoft/yii2/issues/6637 48 | //$this->ravenErrorHandler->registerShutdownFunction(); 49 | $this->oldExceptionHandler = set_exception_handler(array($this, 'handleYiiExceptions')); 50 | } 51 | 52 | /** 53 | * @param \Exception $e 54 | */ 55 | public function handleYiiExceptions($e) 56 | { 57 | restore_exception_handler(); 58 | 59 | if ($this->canLogException($e)) { 60 | $e->event_id = $this->client->getIdent($this->client->captureException($e)); 61 | } 62 | 63 | if ($this->oldExceptionHandler) { 64 | call_user_func($this->oldExceptionHandler, $e); 65 | } 66 | } 67 | 68 | /** 69 | * Filter exception and its previous exceptions for yii\base\ErrorException 70 | * Raven expects normal stacktrace, but yii\base\ErrorException may have xdebug_get_function_stack 71 | * @param \Exception $e 72 | * @return bool 73 | */ 74 | public function canLogException(&$e) 75 | { 76 | if (function_exists('xdebug_get_function_stack')) { 77 | if ($e instanceof ErrorException) { 78 | return false; 79 | } 80 | 81 | $selectedException = $e; 82 | while ($nestedException = $selectedException->getPrevious()) { 83 | if ($nestedException instanceof ErrorException) { 84 | $ref = new \ReflectionProperty('Exception', 'previous'); 85 | $ref->setAccessible(true); 86 | $ref->setValue($selectedException, null); 87 | return true; 88 | } 89 | $selectedException = $selectedException->getPrevious(); 90 | } 91 | } 92 | 93 | return true; 94 | } 95 | 96 | /** 97 | * @return \Raven_Client 98 | */ 99 | public function getClient() 100 | { 101 | return $this->client; 102 | } 103 | } --------------------------------------------------------------------------------