Skip to content

Commit

Permalink
Added user context whenever possible (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
indykoning authored Nov 6, 2024
1 parent 866f561 commit e7015f5
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 32 deletions.
134 changes: 134 additions & 0 deletions Model/SentryInteraction.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,33 @@

// phpcs:disable Magento2.Functions.DiscouragedFunction

use Magento\Authorization\Model\UserContextInterface;
use Magento\Backend\Model\Auth\Session as AdminSession;
use Magento\Customer\Model\Session as CustomerSession;
use Magento\Framework\App\Area;
use Magento\Framework\App\State;
use Magento\Framework\Exception\LocalizedException;
use ReflectionClass;
use Sentry\State\Scope;

use function Sentry\captureException;
use function Sentry\configureScope;
use function Sentry\init;

class SentryInteraction
{
/**
* SentryInteraction constructor.
*
* @param UserContextInterface $userContext
* @param State $appState
*/
public function __construct(
private UserContextInterface $userContext,
private State $appState
) {
}

/**
* Initialize Sentry with passed config.
*
Expand All @@ -23,6 +45,118 @@ public function initialize($config)
init($config);
}

/**
* Check if we might be able to get the user data.
*/
private function canGetUserData()
{
try {
// @phpcs:ignore Generic.PHP.NoSilencedErrors
return in_array(@$this->appState->getAreaCode(), [Area::AREA_ADMINHTML, Area::AREA_FRONTEND]);
} catch (LocalizedException $ex) {
return false;
}
}

/**
* Attempt to get userdata from the current session.
*/
private function getSessionUserData()
{
if (!$this->canGetUserData()) {
return [];
}

$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$reflectionClass = new ReflectionClass($objectManager);
$sharedInstances = $reflectionClass->getProperty('_sharedInstances');
$sharedInstances->setAccessible(true);

if ($this->appState->getAreaCode() === Area::AREA_ADMINHTML) {
if (!array_key_exists(ltrim(AdminSession::class, '\\'), $sharedInstances->getValue($objectManager))) {
// Don't intitialise session if it has not already been started, this causes problems with dynamic resources.
return [];
}
$adminSession = $objectManager->get(AdminSession::class);

if ($adminSession->isLoggedIn()) {
return [
'id' => $adminSession->getUser()->getId(),
'email' => $adminSession->getUser()->getEmail(),
'user_type' => UserContextInterface::USER_TYPE_ADMIN,
];
}
}

if ($this->appState->getAreaCode() === Area::AREA_FRONTEND) {
if (!array_key_exists(ltrim(CustomerSession::class, '\\'), $sharedInstances->getValue($objectManager))) {
return [];
}
$customerSession = $objectManager->get(CustomerSession::class);

if ($customerSession->isLoggedIn()) {
return [
'id' => $customerSession->getCustomer()->getId(),
'email' => $customerSession->getCustomer()->getEmail(),
'website_id' => $customerSession->getCustomer()->getWebsiteId(),
'store_id' => $customerSession->getCustomer()->getStoreId(),
'user_type' => UserContextInterface::USER_TYPE_CUSTOMER,
];
}
}

return [];
}

/**
* Attempt to add the user context to the exception.
*/
public function addUserContext()
{
$userId = null;
$userType = null;
$userData = [];

\Magento\Framework\Profiler::start('SENTRY::add_user_context');

try {
$userId = $this->userContext->getUserId();
if ($userId) {
$userType = $this->userContext->getUserType();
}

if ($this->canGetUserData() && count($userData = $this->getSessionUserData())) {
$userId = $userData['id'] ?? $userId;
$userType = $userData['user_type'] ?? $userType;
unset($userData['user_type']);
}

if (!$userId) {
return;
}

configureScope(function (Scope $scope) use ($userType, $userId, $userData) {
$scope->setUser([
'id' => $userId,
...$userData,
'user_type' => match ($userType) {
UserContextInterface::USER_TYPE_INTEGRATION => 'integration',
UserContextInterface::USER_TYPE_ADMIN => 'admin',
UserContextInterface::USER_TYPE_CUSTOMER => 'customer',
UserContextInterface::USER_TYPE_GUEST => 'guest',
default => 'unknown'
},
]);
});
} catch (\Throwable $e) {
// User context could not be found or added.
\Magento\Framework\Profiler::stop('SENTRY::add_user_context');

return;
}
\Magento\Framework\Profiler::stop('SENTRY::add_user_context');
}

/**
* Capture passed exception.
*
Expand Down
42 changes: 10 additions & 32 deletions Model/SentryLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,20 @@ class SentryLog extends Monolog
/**
* SentryLog constructor.
*
* @param string $name
* @param Data $data
* @param Session $customerSession
* @param State $appState
* @param array $handlers
* @param array $processors
* @param string $name
* @param Data $data
* @param Session $customerSession
* @param State $appState
* @param SentryInteraction $sentryInteraction
* @param array $handlers
* @param array $processors
*/
public function __construct(
$name,
protected Data $data,
protected Session $customerSession,
private State $appState,
private SentryInteraction $sentryInteraction,
array $handlers = [],
array $processors = []
) {
Expand Down Expand Up @@ -63,13 +65,14 @@ public function send($message, $logLevel, $context = [])
\Sentry\configureScope(
function (SentryScope $scope) use ($context, $customTags): void {
$this->setTags($scope, $customTags);
$this->setUser($scope);
if (false === empty($context)) {
$scope->setContext('Custom context', $context);
}
}
);

$this->sentryInteraction->addUserContext();

if ($message instanceof \Throwable) {
$lastEventId = \Sentry\captureException($message);
} else {
Expand All @@ -86,31 +89,6 @@ function (SentryScope $scope) use ($context, $customTags): void {
}
}

/**
* Attempt to add user information based on customerSession.
*
* @param SentryScope $scope
*/
private function setUser(SentryScope $scope): void
{
try {
if (!$this->canGetCustomerData()
|| !$this->customerSession->isLoggedIn()) {
return;
}

$customerData = $this->customerSession->getCustomer();
$scope->setUser([
'id' => $customerData->getEntityId(),
'email' => $customerData->getEmail(),
'website_id' => $customerData->getWebsiteId(),
'store_id' => $customerData->getStoreId(),
]);
} catch (SessionException $e) {
return;
}
}

/**
* Check if we can retrieve customer data.
*
Expand Down
1 change: 1 addition & 0 deletions Plugin/GlobalExceptionCatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public function aroundLaunch(AppInterface $subject, callable $proceed)
} catch (\Throwable $ex) {
try {
if ($this->sentryHelper->shouldCaptureException($ex)) {
$this->sentryInteraction->addUserContext();
$this->sentryInteraction->captureException($ex);
}
} catch (\Throwable $bigProblem) {
Expand Down

0 comments on commit e7015f5

Please sign in to comment.