Passed
Push — master ( 65a502...564935 )
by Christoph
02:17 queued 11s
created

SentryReporterAdapter::reportMessage()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
eloc 9
c 2
b 0
f 0
nc 3
nop 2
dl 0
loc 14
rs 9.9666
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\IMessageReporter;
35
use function Sentry\addBreadcrumb;
36
use Sentry\Breadcrumb;
37
use function Sentry\captureException;
38
use function Sentry\captureMessage;
39
use function Sentry\configureScope;
40
use Sentry\Severity;
41
use Sentry\State\Scope;
42
use Throwable;
43
44
class SentryReporterAdapter implements IMessageReporter, ICollectBreadcrumbs {
45
46
	/** @var IUserSession */
47
	protected $userSession;
48
49
	/** @var CredentialStoreHelper */
50
	private $credentialStoreHelper;
51
52
	/** @var array mapping of log levels */
53
	protected $levels = [
54
		ILogger::DEBUG => Severity::DEBUG,
55
		ILogger::INFO => Severity::INFO,
56
		ILogger::WARN => Severity::WARNING,
57
		ILogger::ERROR => Severity::ERROR,
58
		ILogger::FATAL => Severity::FATAL,
59
	];
60
61
	/** @var int */
62
	protected $minimumLogLevel;
63
64
	public function __construct(IUserSession $userSession,
65
								IConfig $config,
66
								CredentialStoreHelper $credentialStoreHelper) {
67
		$this->userSession = $userSession;
68
		$this->minimumLogLevel = (int)$config->getSystemValue('sentry.minimum.log.level', ILogger::WARN);
69
		$this->credentialStoreHelper = $credentialStoreHelper;
70
	}
71
72
	/**
73
	 * Report an (unhandled) exception to Sentry
74
	 *
75
	 * @param Exception|Throwable $exception
76
	 * @param array $context
77
	 */
78
	public function report($exception, array $context = []) {
79
		if (isset($context['level'])
80
			&& $context['level'] < $this->minimumLogLevel) {
81
			// TODO: report as breadcrumb instead?
82
			return;
83
		}
84
85
		$this->setSentryScope($context);
86
87
		captureException($exception);
88
	}
89
90
	protected function setSentryScope(array $context): void {
91
		configureScope(function (Scope $scope) use ($context): void {
92
			if (isset($context['level'])) {
93
				$scope->setLevel(
94
					new Severity($this->levels[$context['level']])
95
				);
96
			}
97
			if (isset($context['app'])) {
98
				$scope->setExtra('app', $context['app']);
99
			}
100
101
			$user = $this->userSession->getUser();
102
			if ($user !== null) {
103
				// Try to obtain the login name as well
104
				try {
105
					$credentials = $this->credentialStoreHelper->getLoginCredentials();
106
					$username = $credentials->getLoginName();
107
				} catch (CredentialsUnavailableException $e) {
108
					$username = null;
109
				}
110
111
				$scope->setUser([
112
					'id' => $user->getUID(),
113
					'username' => $username,
114
				]);
115
			}
116
		});
117
	}
118
119
	public function collect(string $message, string $category, array $context = []) {
120
		if (isset($context['app'])) {
121
			$message = "[" . $context['app'] . "] " . $message;
122
		}
123
124
		$this->setSentryScope($context);
125
126
		$level = $context['level'] ?? ILogger::WARN;
127
		$sentryLevel = $this->levels[$level] ?? Breadcrumb::LEVEL_WARNING;
128
129
		addBreadcrumb(new Breadcrumb($sentryLevel, Breadcrumb::TYPE_ERROR, $category, $message));
130
	}
131
132
	/**
133
	 * Report a (error) message
134
	 *
135
	 * @param string $message
136
	 * @param array $context
137
	 */
138
	public function reportMessage(string $message, array $context = []): void {
139
		if (isset($context['level'])
140
			&& $context['level'] < $this->minimumLogLevel) {
141
			$this->collect($message, 'message', $context);
142
			return;
143
		}
144
145
		if (isset($context['app'])) {
146
			$message = "[" . $context['app'] . "] " . $message;
147
		}
148
149
		captureMessage(
150
			$message,
151
			new Severity($this->levels[$context['level'] ?? ILogger::WARN] ?? Severity::WARNING)
152
		);
153
	}
154
155
}
156