├── .gitignore ├── tests ├── .gitignore ├── config │ └── params.php ├── unit │ ├── bootstrap.php │ ├── config.php │ └── SentryTargetTest.php ├── unit.suite.yml ├── _support │ ├── Helper │ │ └── Unit.php │ ├── E2eTester.php │ └── UnitTester.php ├── bootstrap.php ├── models │ └── User.php ├── sentry-fill └── commands │ └── SentryController.php ├── codeception.yml ├── .travis.yml ├── composer.json ├── LICENSE.md ├── CHANGELOG.md ├── README.md └── src └── SentryTarget.php /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | *.iml 3 | /vendor 4 | codecept.phar 5 | composer.lock 6 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | _output 2 | _support/_generated 3 | config/params-local.php 4 | -------------------------------------------------------------------------------- /tests/config/params.php: -------------------------------------------------------------------------------- 1 | null 5 | ]; 6 | -------------------------------------------------------------------------------- /tests/unit/bootstrap.php: -------------------------------------------------------------------------------- 1 | 'sentry-tests', 5 | 'class' => 'yii\console\Application', 6 | 'basePath' => \Yii::getAlias('@tests'), 7 | 'runtimePath' => \Yii::getAlias('@tests/_output'), 8 | 'bootstrap' => [], 9 | ]; 10 | -------------------------------------------------------------------------------- /codeception.yml: -------------------------------------------------------------------------------- 1 | actor: Tester 2 | bootstrap: bootstrap.php 3 | paths: 4 | tests: tests 5 | log: tests/_output 6 | data: tests/_data 7 | support: tests/_support 8 | envs: tests/_envs 9 | settings: 10 | colors: true 11 | memory_limit: 1024M 12 | extensions: 13 | enabled: 14 | - Codeception\Extension\RunFailed 15 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | $id]); 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public static function findIdentityByAccessToken($token, $type = null) 37 | { 38 | return new self(); 39 | } 40 | 41 | /** 42 | * {@inheritdoc} 43 | */ 44 | public function getId() 45 | { 46 | return $this->id; 47 | } 48 | 49 | /** 50 | * {@inheritdoc} 51 | */ 52 | public function getAuthKey() 53 | { 54 | return '123'; 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function validateAuthKey($authKey) 61 | { 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tests/sentry-fill: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | 'sentry-tests', 23 | 'basePath' => Yii::getAlias('@tests'), 24 | 'runtimePath' => Yii::getAlias('@tests/_output'), 25 | 'controllerNamespace' => 'tests\commands', 26 | 'bootstrap' => ['log'], 27 | 'params' => $params, 28 | 'components' => [ 29 | 'request' => [ 30 | 'cookieValidationKey' => '123', 31 | ], 32 | 'log' => [ 33 | 'targets' => [ 34 | [ 35 | 'class' => \yii\log\FileTarget::class, 36 | ], 37 | [ 38 | 'class' => \notamedia\sentry\SentryTarget::class, 39 | 'dsn' => $params['sentryDsn'], 40 | ], 41 | ], 42 | ], 43 | 'user' => [ 44 | 'class' => \yii\web\User::class, 45 | 'identityClass' => \tests\models\User::class, 46 | 'enableSession' => false, 47 | ], 48 | ], 49 | ]); 50 | 51 | $application->runAction('sentry/fill'); 52 | 53 | echo 'All messages sent.'; 54 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change log 2 | 3 | ## 1.6.0-beta - 2020-10-22 4 | ### Changed 5 | * PHP updated to 7.2 6 | * Sentry/SDK updated to 3.0 7 | 8 | ## 1.5.1 - 2020-08-24 9 | ### Fixed 10 | * Fix double sending of events to Sentry. 11 | * Fix event exclusion ("except" parameter). 12 | 13 | ## 1.5.0 - 2020-07-07 14 | 1.5.0 released 15 | 16 | ## 1.5.0-beta - 2020-05-18 17 | ### Fixed 18 | * Fix message level (debug, info, warning, error) translating to sentry. 19 | * Fix message scope. For now every message has own scope and not affect to other. 20 | * Fix adding additional data (extra context, user data) for exception messages. 21 | ### Changed 22 | * Sentry init will be invoking at application start, and not before log export started. 23 | ### Added 24 | * Log user ID and IP, if available. 25 | * Added ability to add own context data for messages scope. 26 | 27 | ## 1.4.2 - 2020-01-21 28 | ### Fixed 29 | * Array export fix if text not contains message key. 30 | 31 | ## 1.4.1 - 2019-11-06 32 | ### Fixed 33 | * Fix passing client options to sentry. 34 | * Fix error with undefined index. 35 | * Fix bug with sets extra values. 36 | 37 | ## 1.4.0 - 2019-09-27 38 | ### Changed 39 | Used sentry/sdk 2.0 package. 40 | 41 | ### Fixed 42 | Fixed error with `extraCallback` property. 43 | 44 | ## 1.3.0 - 2017-08-24 45 | ### Changed 46 | * Exception handling refactoring. 47 | * Unsupported HHMV. 48 | 49 | ## 1.2.1 - 2017-01-28 50 | ### Added 51 | * Unit tests. 52 | * Change log. 53 | 54 | ## 1.2.0 - 2016-11-30 55 | ### Added 56 | * Added supporting tags in messages. 57 | 58 | ## 1.1.2 - 2016-10-03 59 | ### Fixed 60 | * Checking context on instance of `\Throwable`. 61 | * New URL of the Sentry website. 62 | 63 | ## 1.1.1 - 2016-09-05 64 | ### Changed 65 | * New name of the Sentry composer package. 66 | 67 | ## 1.1.0 - 2016-04-08 68 | ### Added 69 | * `extraCallback` property in configuration for modify extra's data. 70 | 71 | ## 1.0.0 - 2016-01-04 72 | Hello, World! 73 | -------------------------------------------------------------------------------- /tests/commands/SentryController.php: -------------------------------------------------------------------------------- 1 | log->setLogger(Yii::getLogger()); 22 | 23 | foreach ($this->logsProvider() as $log) { 24 | Yii::$app->user->logout(); 25 | if (isset($log['user'])) { 26 | Yii::$app->user->login($log['user']); 27 | } 28 | $_SERVER['REMOTE_ADDR'] = $log['ip'] ?? null; 29 | 30 | \Sentry\configureScope(function (\Sentry\State\Scope $scope) use ($log) { 31 | $scope->setUser([ 32 | // configureScope modify global scope, so we revert changes from previous log message 33 | 'username' => isset($log['user']) ? $log['user']->username : null, 34 | 'email' => isset($log['user']) ? $log['user']->email : null, 35 | ], true); 36 | }); 37 | 38 | Yii::getLogger()->log($log['message'], $log['level'], $log['category']); 39 | // We need to final flush logs for change ip and user on fly 40 | Yii::getLogger()->flush(true); 41 | } 42 | } 43 | 44 | protected function logsProvider() 45 | { 46 | return [ 47 | [ 48 | 'level' => Logger::LEVEL_ERROR, 49 | 'message' => [ 50 | 'msg' => new RuntimeException('Connection error', 999, new \Exception), 51 | 'extra' => 'Hello, World!', 52 | 'tags' => ['db-name' => 'bulling'], 53 | ], 54 | 'category' => 'dbms', 55 | ], 56 | [ 57 | 'level' => Logger::LEVEL_ERROR, 58 | 'message' => new RuntimeException('Oops... This is exception.', 999, new \Exception), 59 | 'category' => 'exceptions', 60 | 'user' => new User([ 61 | 'id' => 47, 62 | 'username' => 'Agent 47', 63 | 'email' => '47@agency.com', 64 | ]), 65 | 'ip' => '127.0.0.42', 66 | ], 67 | [ 68 | 'level' => Logger::LEVEL_INFO, 69 | 'message' => [ 70 | 'msg' => 'Message from bulling service', 71 | 'extra' => 'Hello, World!', 72 | 'tags' => ['currency' => 'RUB'], 73 | ], 74 | 'category' => 'monitoring', 75 | 'user' => new User([ 76 | 'id' => 543, 77 | 'username' => 'John', 78 | 'email' => 'hello@example.com', 79 | ]), 80 | 'ip' => '2607:f0d0:1002:51::4', 81 | ], 82 | [ 83 | 'level' => Logger::LEVEL_WARNING, 84 | 'message' => 'Invalid request', 85 | 'category' => 'UI', 86 | ], 87 | [ 88 | 'level' => null, 89 | 'message' => [1, 2, 3], 90 | 'category' => null, 91 | ], 92 | [ 93 | 'level' => '', 94 | 'message' => ['one' => 'value 1', 'two' => 'value 2'], 95 | 'category' => null, 96 | ], 97 | ]; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Sentry](https://sentry.io) logger for Yii2 2 | 3 | [![Build Status](https://travis-ci.org/notamedia/yii2-sentry.svg)](https://travis-ci.org/notamedia/yii2-sentry) 4 | [![Latest Stable Version](https://poser.pugx.org/notamedia/yii2-sentry/v/stable)](https://packagist.org/packages/notamedia/yii2-sentry) 5 | [![Total Downloads](https://poser.pugx.org/notamedia/yii2-sentry/downloads)](https://packagist.org/packages/notamedia/yii2-sentry) 6 | [![License](https://poser.pugx.org/notamedia/yii2-sentry/license)](https://packagist.org/packages/notamedia/yii2-sentry) 7 | 8 | ## Installation 9 | 10 | ```bash 11 | composer require notamedia/yii2-sentry 12 | ``` 13 | 14 | Add target class in the application config: 15 | 16 | ```php 17 | return [ 18 | 'components' => [ 19 | 'log' => [ 20 | 'traceLevel' => YII_DEBUG ? 3 : 0, 21 | 'targets' => [ 22 | [ 23 | 'class' => 'notamedia\sentry\SentryTarget', 24 | 'dsn' => 'http://2682ybvhbs347:235vvgy465346@sentry.io/1', 25 | 'levels' => ['error', 'warning'], 26 | // Write the context information (the default is true): 27 | 'context' => true, 28 | // Additional options for `Sentry\init`: 29 | 'clientOptions' => ['release' => 'my-project-name@2.3.12'] 30 | ], 31 | ], 32 | ], 33 | ], 34 | ]; 35 | ``` 36 | 37 | ## Usage 38 | 39 | Writing simple message: 40 | 41 | ```php 42 | \Yii::error('message', 'category'); 43 | ``` 44 | 45 | Writing messages with extra data: 46 | 47 | ```php 48 | \Yii::warning([ 49 | 'msg' => 'message', 50 | 'extra' => 'value', 51 | ], 'category'); 52 | ``` 53 | 54 | ### Extra callback 55 | 56 | `extraCallback` property can modify extra's data as callable function: 57 | 58 | ```php 59 | 'targets' => [ 60 | [ 61 | 'class' => 'notamedia\sentry\SentryTarget', 62 | 'dsn' => 'http://2682ybvhbs347:235vvgy465346@sentry.io/1', 63 | 'levels' => ['error', 'warning'], 64 | 'context' => true, // Write the context information. The default is true. 65 | 'extraCallback' => function ($message, $extra) { 66 | // some manipulation with data 67 | $extra['some_data'] = \Yii::$app->someComponent->someMethod(); 68 | return $extra; 69 | } 70 | ], 71 | ], 72 | ``` 73 | 74 | ### Tags 75 | 76 | Writing messages with additional tags. If need to add additional tags for event, add `tags` key in message. Tags are various key/value pairs that get assigned to an event, and can later be used as a breakdown or quick access to finding related events. 77 | 78 | Example: 79 | 80 | ```php 81 | \Yii::warning([ 82 | 'msg' => 'message', 83 | 'extra' => 'value', 84 | 'tags' => [ 85 | 'extraTagKey' => 'extraTagValue', 86 | ] 87 | ], 'category'); 88 | ``` 89 | 90 | More about tags see https://docs.sentry.io/learn/context/#tagging-events 91 | 92 | ### Additional context 93 | 94 | You can add additional context (such as user information, fingerprint, etc) by calling `\Sentry\configureScope()` before logging. 95 | For example in main configuration on `beforeAction` event (real place will dependant on your project): 96 | ```php 97 | return [ 98 | // ... 99 | 'on beforeAction' => function (\yii\base\ActionEvent $event) { 100 | /** @var \yii\web\User $user */ 101 | $user = Yii::$app->has('user', true) ? Yii::$app->get('user', false) : null; 102 | if ($user && ($identity = $user->getIdentity(false))) { 103 | \Sentry\configureScope(function (\Sentry\State\Scope $scope) use ($identity) { 104 | $scope->setUser([ 105 | // User ID and IP will be added by logger automatically 106 | 'username' => $identity->username, 107 | 'email' => $identity->email, 108 | ]); 109 | }); 110 | } 111 | 112 | return $event->isValid; 113 | }, 114 | // ... 115 | ]; 116 | ``` 117 | 118 | ## Log levels 119 | 120 | Yii2 log levels converts to Sentry levels: 121 | 122 | ``` 123 | \yii\log\Logger::LEVEL_ERROR => 'error', 124 | \yii\log\Logger::LEVEL_WARNING => 'warning', 125 | \yii\log\Logger::LEVEL_INFO => 'info', 126 | \yii\log\Logger::LEVEL_TRACE => 'debug', 127 | \yii\log\Logger::LEVEL_PROFILE_BEGIN => 'debug', 128 | \yii\log\Logger::LEVEL_PROFILE_END => 'debug', 129 | ``` 130 | -------------------------------------------------------------------------------- /tests/unit/SentryTargetTest.php: -------------------------------------------------------------------------------- 1 | getMethod('getContextMessage'); 37 | $method->setAccessible(true); 38 | 39 | $sentryTarget = new SentryTarget(); 40 | $result = $method->invokeArgs($sentryTarget, []); 41 | 42 | $this->assertEmpty($result); 43 | } 44 | 45 | public function testExceptionPassing() 46 | { 47 | $sentryTarget = $this->getConfiguredSentryTarget(); 48 | 49 | $logData = [ 50 | 'message' => 'This exception was caught, but still needs to be reported', 51 | 'exception' => new RuntimeException('Package loss detected'), 52 | 'something_extra' => ['foo' => 'bar'], 53 | ]; 54 | 55 | $messageWasSent = false; 56 | 57 | $client = $this->createMock(ClientInterface::class); 58 | $client->expects($this->once()) 59 | ->method('captureEvent') 60 | ->willReturnCallback(function (Event $event, ?EventHint $hint = null, ?Scope $scope = null) use ($logData, &$messageWasSent): ?EventId { 61 | $messageWasSent = true; 62 | $this->assertSame($logData['exception'], $hint->exception); 63 | $this->assertSame($logData['message'], $event->getMessage()); 64 | 65 | return EventId::generate(); 66 | }); 67 | 68 | SentrySdk::getCurrentHub()->bindClient($client); 69 | 70 | $sentryTarget->collect([[$logData, Logger::LEVEL_INFO, 'application', 1481513561.197593, []]], true); 71 | $this->assertTrue($messageWasSent); 72 | } 73 | 74 | public function messageDataProvider() 75 | { 76 | $msg = 'A message'; 77 | 78 | yield [$msg, $msg]; 79 | 80 | yield [$msg, ['msg' => $msg]]; 81 | 82 | yield [$msg, ['message' => $msg]]; 83 | 84 | yield [$msg, ['message' => $msg, 'msg' => 'Ignored']]; 85 | } 86 | 87 | /** 88 | * @dataProvider messageDataProvider 89 | */ 90 | public function testMessageConverting($expectedMessageText, $loggedMessage) 91 | { 92 | $sentryTarget = $this->getConfiguredSentryTarget(); 93 | $messageWasSent = false; 94 | 95 | $client = $this->createMock(ClientInterface::class); 96 | $client->expects($this->once()) 97 | ->method('captureEvent') 98 | ->willReturnCallback(function (Event $event, ?EventHint $hint = null, ?Scope $scope = null) use ($expectedMessageText, &$messageWasSent): ?EventId { 99 | $messageWasSent = true; 100 | $this->assertSame($expectedMessageText, $event->getMessage()); 101 | 102 | return EventId::generate(); 103 | }); 104 | 105 | SentrySdk::getCurrentHub()->bindClient($client); 106 | 107 | $sentryTarget->collect([[$loggedMessage, Logger::LEVEL_INFO, 'application', 1481513561.197593, []]], true); 108 | $this->assertTrue($messageWasSent); 109 | } 110 | 111 | /** 112 | * Testing method getLevelName() 113 | * - returns level name for each logger level 114 | * @see SentryTarget::getLevelName 115 | */ 116 | public function testGetLevelName() 117 | { 118 | //valid level names 119 | $levelNames = [ 120 | 'info', 121 | 'error', 122 | 'warning', 123 | 'debug', 124 | ]; 125 | 126 | $loggerClass = new ReflectionClass(Logger::className()); 127 | $loggerLevelConstants = $loggerClass->getConstants(); 128 | foreach ($loggerLevelConstants as $constant => $value) { 129 | if (strpos($constant, 'LEVEL_') === 0) { 130 | $level = SentryTarget::getLevelName($value); 131 | $this->assertNotEmpty($level); 132 | $this->assertTrue(in_array($level, $levelNames), sprintf('Level "%s" is incorrect', $level)); 133 | } 134 | } 135 | 136 | //check default level name 137 | $this->assertEquals('error', SentryTarget::getLevelName('')); 138 | $this->assertEquals('error', SentryTarget::getLevelName('somerandomstring' . uniqid())); 139 | } 140 | 141 | /** 142 | * Testing method collect() 143 | * - assigns messages to Target property 144 | * - creates Sentry object 145 | * @see SentryTarget::collect 146 | */ 147 | public function testCollect() 148 | { 149 | $sentryTarget = $this->getConfiguredSentryTarget(); 150 | 151 | $sentryTarget->collect($this->messages, false); 152 | $this->assertEquals(count($this->messages), count($sentryTarget->messages)); 153 | } 154 | 155 | /** 156 | * Testing method export() 157 | * - Sentry::capture is called on collect([...], true) 158 | * - messages stack is cleaned on collect([...], true) 159 | * - Sentry::capture is called on export() 160 | * @see SentryTarget::export 161 | */ 162 | public function testExport() 163 | { 164 | $sentryTarget = $this->getConfiguredSentryTarget(); 165 | 166 | //test calling client and clearing messages on final collect 167 | $sentryTarget->collect($this->messages, true); 168 | $this->assertEmpty($sentryTarget->messages); 169 | 170 | //add messages and test simple export() method 171 | $sentryTarget->collect($this->messages, false); 172 | $sentryTarget->export(); 173 | $this->assertEquals(count($this->messages), count($sentryTarget->messages)); 174 | } 175 | 176 | /** 177 | * Returns configured SentryTarget object 178 | * 179 | * @return SentryTarget 180 | * @throws \yii\base\InvalidConfigException 181 | */ 182 | protected function getConfiguredSentryTarget() 183 | { 184 | $sentryTarget = new SentryTarget(); 185 | $sentryTarget->exportInterval = 100; 186 | $sentryTarget->setLevels(Logger::LEVEL_INFO); 187 | return $sentryTarget; 188 | } 189 | 190 | /** 191 | * Returns reflected 'client' property 192 | * 193 | * @param SentryTarget $sentryTarget 194 | * @return \ReflectionProperty 195 | */ 196 | protected function getAccessibleClientProperty(SentryTarget $sentryTarget) 197 | { 198 | $sentryTargetClass = new ReflectionClass("\Sentry\Client"); 199 | $clientProperty = $sentryTargetClass->getProperty('transport'); 200 | $clientProperty->setAccessible(true); 201 | 202 | return $clientProperty; 203 | } 204 | 205 | /** 206 | * Compatible version of creating mock method 207 | * 208 | * @param string $className 209 | * @return \PHPUnit_Framework_MockObject_MockObject 210 | */ 211 | protected function getMockCompatible($className) 212 | { 213 | return method_exists($this, 'createMock') ? 214 | self::createMock($className) : $this->getMock($className); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/SentryTarget.php: -------------------------------------------------------------------------------- 1 | $this->dsn], $this->clientOptions); 59 | $builder = ClientBuilder::create($userOptions); 60 | 61 | $options = $builder->getOptions(); 62 | $options->setIntegrations(static function (array $integrations) { 63 | // Remove the default error and fatal exception listeners to let us handle those 64 | return array_filter($integrations, static function (IntegrationInterface $integration): bool { 65 | if ($integration instanceof ErrorListenerIntegration) { 66 | return false; 67 | } 68 | if ($integration instanceof ExceptionListenerIntegration) { 69 | return false; 70 | } 71 | if ($integration instanceof FatalErrorListenerIntegration) { 72 | return false; 73 | } 74 | 75 | return true; 76 | }); 77 | }); 78 | 79 | SentrySdk::init()->bindClient($builder->getClient()); 80 | } 81 | 82 | /** 83 | * @inheritdoc 84 | */ 85 | protected function getContextMessage() 86 | { 87 | return ''; 88 | } 89 | 90 | /** 91 | * @inheritdoc 92 | */ 93 | public function export() 94 | { 95 | foreach ($this->messages as $message) { 96 | [$text, $level, $category] = $message; 97 | 98 | $data = [ 99 | 'message' => '', 100 | 'tags' => ['category' => $category], 101 | 'extra' => [], 102 | 'userData' => [], 103 | ]; 104 | 105 | $request = Yii::$app->getRequest(); 106 | if ($request instanceof Request && $request->getUserIP()) { 107 | $data['userData']['ip_address'] = $request->getUserIP(); 108 | } 109 | 110 | try { 111 | /** @var User $user */ 112 | $user = Yii::$app->has('user', true) ? Yii::$app->get('user', false) : null; 113 | if ($user && ($identity = $user->getIdentity(false))) { 114 | $data['userData']['id'] = $identity->getId(); 115 | } 116 | } catch (Throwable $e) {} 117 | 118 | \Sentry\withScope(function (Scope $scope) use ($text, $level, $data) { 119 | if (is_array($text)) { 120 | if (isset($text['msg'])) { 121 | $data['message'] = (string)$text['msg']; 122 | unset($text['msg']); 123 | } 124 | if (isset($text['message'])) { 125 | $data['message'] = (string)$text['message']; 126 | unset($text['message']); 127 | } 128 | 129 | if (isset($text['tags'])) { 130 | $data['tags'] = ArrayHelper::merge($data['tags'], $text['tags']); 131 | unset($text['tags']); 132 | } 133 | 134 | if (isset($text['exception']) && $text['exception'] instanceof Throwable) { 135 | $data['exception'] = $text['exception']; 136 | unset($text['exception']); 137 | } 138 | 139 | $data['extra'] = $text; 140 | } else { 141 | $data['message'] = (string) $text; 142 | } 143 | 144 | if ($this->context) { 145 | $data['extra']['context'] = parent::getContextMessage(); 146 | } 147 | 148 | $data = $this->runExtraCallback($text, $data); 149 | 150 | $scope->setUser($data['userData']); 151 | foreach ($data['extra'] as $key => $value) { 152 | $scope->setExtra((string) $key, $value); 153 | } 154 | foreach ($data['tags'] as $key => $value) { 155 | if ($value) { 156 | $scope->setTag($key, $value); 157 | } 158 | } 159 | 160 | if ($text instanceof Throwable) { 161 | \Sentry\captureException($text); 162 | } else { 163 | $event = Event::createEvent(); 164 | $event->setMessage($data['message']); 165 | $event->setLevel($this->getLogLevel($level)); 166 | 167 | \Sentry\captureEvent($event, EventHint::fromArray(array_filter([ 168 | 'exception' => $data['exception'] ?? null, 169 | ]))); 170 | } 171 | }); 172 | } 173 | } 174 | 175 | /** 176 | * Calls the extra callback if it exists 177 | * 178 | * @param mixed $text 179 | * @param array $data 180 | * 181 | * @return array 182 | */ 183 | public function runExtraCallback($text, $data) 184 | { 185 | if (is_callable($this->extraCallback)) { 186 | $data['extra'] = call_user_func($this->extraCallback, $text, $data['extra'] ?? []); 187 | } 188 | 189 | return $data; 190 | } 191 | 192 | /** 193 | * Returns the text display of the specified level for the Sentry. 194 | * 195 | * @deprecated Deprecated from 1.5, will remove in 2.0 196 | * 197 | * @param int $level The message level, e.g. [[LEVEL_ERROR]], [[LEVEL_WARNING]]. 198 | * 199 | * @return string 200 | */ 201 | public static function getLevelName($level) 202 | { 203 | static $levels = [ 204 | Logger::LEVEL_ERROR => 'error', 205 | Logger::LEVEL_WARNING => 'warning', 206 | Logger::LEVEL_INFO => 'info', 207 | Logger::LEVEL_TRACE => 'debug', 208 | Logger::LEVEL_PROFILE_BEGIN => 'debug', 209 | Logger::LEVEL_PROFILE_END => 'debug', 210 | ]; 211 | 212 | return $levels[$level] ?? 'error'; 213 | } 214 | 215 | /** 216 | * Translates Yii2 log levels to Sentry Severity. 217 | * 218 | * @param int $level 219 | * 220 | * @return Severity 221 | */ 222 | protected function getLogLevel($level): Severity 223 | { 224 | switch ($level) { 225 | case Logger::LEVEL_PROFILE: 226 | case Logger::LEVEL_PROFILE_BEGIN: 227 | case Logger::LEVEL_PROFILE_END: 228 | case Logger::LEVEL_TRACE: 229 | return Severity::debug(); 230 | case Logger::LEVEL_WARNING: 231 | return Severity::warning(); 232 | case Logger::LEVEL_ERROR: 233 | return Severity::error(); 234 | case Logger::LEVEL_INFO: 235 | default: 236 | return Severity::info(); 237 | } 238 | } 239 | } 240 | --------------------------------------------------------------------------------