Passed
Pull Request — master (#112)
by Christoph
02:13
created

SentryReporterAdapter::buildSentryContext()   A

Complexity

Conditions 5
Paths 16

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 15
c 1
b 0
f 0
nc 16
nop 1
dl 0
loc 26
rs 9.4555
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @author Christoph Wurst <[email protected]>
7
 *
8
 * @license GNU AGPL version 3 or any later version
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License as
12
 * published by the Free Software Foundation, either version 3 of the
13
 * License, or (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22
 *
23
 */
24
25
namespace OCA\Sentry\Reporter;
26
27
use Exception;
28
use OCA\Sentry\Helper\CredentialStoreHelper;
29
use OCP\Authentication\Exceptions\CredentialsUnavailableException;
30
use OCP\IConfig;
31
use OCP\ILogger;
32
use OCP\IUserSession;
33
use OCP\Support\CrashReport\ICollectBreadcrumbs;
34
use OCP\Support\CrashReport\IReporter;
35
use function Sentry\addBreadcrumb;
36
use Sentry\Breadcrumb;
37
use function Sentry\captureException;
38
use function Sentry\configureScope;
39
use Sentry\Severity;
40
use Sentry\State\Scope;
41
use Throwable;
42
43
class SentryReporterAdapter implements IReporter, ICollectBreadcrumbs {
44
45
	/** @var IUserSession */
46
	protected $userSession;
47
48
	/** @var CredentialStoreHelper */
49
	private $credentialStoreHelper;
50
51
	/** @var array mapping of log levels */
52
	protected $levels = [
53
		ILogger::DEBUG => Severity::DEBUG,
54
		ILogger::INFO => Severity::INFO,
55
		ILogger::WARN => Severity::WARNING,
56
		ILogger::ERROR => Severity::ERROR,
57
		ILogger::FATAL => Severity::FATAL,
58
	];
59
60
	/** @var int */
61
	protected $minimumLogLevel;
62
63
	public function __construct(IUserSession $userSession,
64
								IConfig $config,
65
								CredentialStoreHelper $credentialStoreHelper) {
66
		$this->userSession = $userSession;
67
		$this->minimumLogLevel = (int)$config->getSystemValue('sentry.minimum.log.level', ILogger::WARN);
68
		$this->credentialStoreHelper = $credentialStoreHelper;
69
	}
70
71
	/**
72
	 * Report an (unhandled) exception to Sentry
73
	 *
74
	 * @param Exception|Throwable $exception
75
	 * @param array $context
76
	 */
77
	public function report($exception, array $context = []) {
78
		if (isset($context['level'])
79
			&& $context['level'] < $this->minimumLogLevel) {
80
			// TODO: report as breadcrumb instead?
81
			return;
82
		}
83
84
		$this->setSentryScope($context);
85
86
		captureException($exception);
87
	}
88
89
	protected function setSentryScope(array $context): void {
90
		configureScope(function (Scope $scope) use ($context): void {
91
			if (isset($context['level'])) {
92
				$scope->setLevel(
93
					new Severity($this->levels[$context['level']])
94
				);
95
			}
96
			if (isset($context['app'])) {
97
				$scope->setExtra('app', $context['app']);
98
			}
99
100
			$user = $this->userSession->getUser();
101
			if ($user !== null) {
102
				// Try to obtain the login name as well
103
				try {
104
					$credentials = $this->credentialStoreHelper->getLoginCredentials();
105
					$username = $credentials->getLoginName();
106
				} catch (CredentialsUnavailableException $e) {
107
					$username = null;
108
				}
109
110
				$scope->setUser([
111
					'id' => $user->getUID(),
112
					'username' => $username,
113
				]);
114
			}
115
		});
116
	}
117
118
	public function collect(string $message, string $category, array $context = []) {
119
		$this->setSentryScope($context);
120
121
		$level = isset($context['level']);
122
		$sentryLevel = $this->levels[$level] ?? Breadcrumb::LEVEL_WARNING;
123
124
		addBreadcrumb(new Breadcrumb($sentryLevel, Breadcrumb::TYPE_ERROR, $category, $message));
125
	}
126
127
}
128