Skip to content

Commit 420ddf3

Browse files
authored
Moved sentry logging to a monolog handler (2.4.8 compatibility) (#165)
1 parent 5e2b7e7 commit 420ddf3

File tree

10 files changed

+174
-81
lines changed

10 files changed

+174
-81
lines changed

.github/PULL_REQUEST_TEMPLATE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@
2222
**Checklist**
2323

2424
- [ ] I've ran `composer run codestyle`
25-
- [ ] I've ran `composer run phpstan`
25+
- [ ] I've ran `composer run analyse`

.github/workflows/analyse.yml

+9-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ on:
99
jobs:
1010
test:
1111
runs-on: ubuntu-latest
12-
name: analyse
12+
strategy:
13+
fail-fast: true
14+
matrix:
15+
php: [8.2, 8.3]
16+
stability: [prefer-lowest, prefer-stable]
17+
18+
name: PHPStan - P${{ matrix.php }} - ${{ matrix.stability }}
1319

1420
steps:
1521
- name: Checkout code
@@ -18,12 +24,12 @@ jobs:
1824
- name: Setup PHP
1925
uses: shivammathur/setup-php@v2
2026
with:
21-
php-version: 8.2
27+
php-version: ${{ matrix.php }}
2228
extensions: dom, curl, libxml, mbstring, zip, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
2329
coverage: none
2430

2531
- name: Install dependencies
26-
run: composer install --no-interaction
32+
run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
2733

2834
- name: Analyse
2935
run: composer run analyse

.github/workflows/php.yml renamed to .github/workflows/phpcs.yml

+9-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ on:
99
jobs:
1010
test:
1111
runs-on: ubuntu-latest
12-
name: PHPCS
12+
strategy:
13+
fail-fast: true
14+
matrix:
15+
php: [8.2, 8.3]
16+
stability: [prefer-lowest, prefer-stable]
17+
18+
name: PHPCS - P${{ matrix.php }} - ${{ matrix.stability }}
1319

1420
steps:
1521
- name: Checkout code
@@ -18,12 +24,12 @@ jobs:
1824
- name: Setup PHP
1925
uses: shivammathur/setup-php@v2
2026
with:
21-
php-version: 8.2
27+
php-version: ${{ matrix.php }}
2228
extensions: dom, curl, libxml, mbstring, zip, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
2329
coverage: none
2430

2531
- name: Install dependencies
26-
run: composer install --no-interaction
32+
run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
2733

2834
- name: Analyse
2935
run: composer run phpcs

Helper/Version.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class Version extends AbstractHelper
3636
public function __construct(
3737
private \Magento\Framework\App\State $appState,
3838
private \Magento\Framework\App\View\Deployment\Version\StorageInterface $versionStorage,
39-
DeploymentConfig $deploymentConfig = null
39+
?DeploymentConfig $deploymentConfig = null
4040
) {
4141
$this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->get(DeploymentConfig::class);
4242
}

Logger/Handler/Sentry.php

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
namespace JustBetter\Sentry\Logger\Handler;
4+
5+
use JustBetter\Sentry\Helper\Data;
6+
use JustBetter\Sentry\Model\SentryLog;
7+
use Magento\Framework\App\DeploymentConfig;
8+
use Monolog\Handler\AbstractHandler;
9+
use Monolog\Logger;
10+
use Monolog\LogRecord;
11+
12+
// TODO: Remove once V2 support is dropped.
13+
// phpcs:disable Generic.Classes.DuplicateClassName,PSR2.Classes.ClassDeclaration,PSR1.Classes.ClassDeclaration.MultipleClasses
14+
if (Logger::API < 3) {
15+
class Sentry extends AbstractHandler
16+
{
17+
/**
18+
* Construct.
19+
*
20+
* @param Data $sentryHelper
21+
* @param SentryLog $sentryLog
22+
* @param DeploymentConfig $deploymentConfig
23+
*/
24+
public function __construct(
25+
protected Data $sentryHelper,
26+
protected SentryLog $sentryLog,
27+
protected DeploymentConfig $deploymentConfig,
28+
) {
29+
parent::__construct();
30+
}
31+
32+
/**
33+
* @inheritDoc
34+
*/
35+
public function isHandling(array $record): bool
36+
{
37+
$config = $this->sentryHelper->collectModuleConfig();
38+
if ($config['log_level']) {
39+
$this->setLevel($config['log_level']);
40+
}
41+
42+
return parent::isHandling($record) && $this->deploymentConfig->isAvailable() && $this->sentryHelper->isActive();
43+
}
44+
45+
/**
46+
* @inheritDoc
47+
*/
48+
public function handle(array $record): bool
49+
{
50+
if (!$this->isHandling($record)) {
51+
return false;
52+
}
53+
54+
$this->sentryLog->send($record['message'], $record['level'], $record['context']);
55+
56+
return false;
57+
}
58+
}
59+
} else {
60+
class Sentry extends AbstractHandler
61+
{
62+
/**
63+
* Construct.
64+
*
65+
* @param Data $sentryHelper
66+
* @param SentryLog $sentryLog
67+
* @param DeploymentConfig $deploymentConfig
68+
*/
69+
public function __construct(
70+
protected Data $sentryHelper,
71+
protected SentryLog $sentryLog,
72+
protected DeploymentConfig $deploymentConfig,
73+
) {
74+
parent::__construct();
75+
}
76+
77+
/**
78+
* @inheritDoc
79+
*/
80+
public function isHandling(LogRecord $record): bool
81+
{
82+
$config = $this->sentryHelper->collectModuleConfig();
83+
if ($config['log_level']) {
84+
$this->setLevel($config['log_level']);
85+
}
86+
87+
return parent::isHandling($record) && $this->deploymentConfig->isAvailable() && $this->sentryHelper->isActive();
88+
}
89+
90+
/**
91+
* @inheritDoc
92+
*/
93+
public function handle(LogRecord $record): bool
94+
{
95+
if (!$this->isHandling($record)) {
96+
return false;
97+
}
98+
99+
$this->sentryLog->send($record['message'], $record['level'], $record['context']);
100+
101+
return false;
102+
}
103+
}
104+
}
105+
// phpcs:enable Generic.Classes.DuplicateClassName,PSR2.Classes.ClassDeclaration,PSR1.Classes.ClassDeclaration.MultipleClasses

Model/SentryLog.php

+32-10
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
use Magento\Framework\App\State;
99
use Magento\Framework\Exception\LocalizedException;
1010
use Magento\Framework\Exception\SessionException;
11-
use Magento\Framework\Logger\Monolog;
11+
use Sentry\EventHint;
12+
use Sentry\ExceptionMechanism;
13+
use Sentry\Stacktrace;
1214
use Sentry\State\Scope as SentryScope;
1315

14-
class SentryLog extends Monolog
16+
class SentryLog
1517
{
1618
/**
1719
* @var array
@@ -21,24 +23,17 @@ class SentryLog extends Monolog
2123
/**
2224
* SentryLog constructor.
2325
*
24-
* @param string $name
2526
* @param Data $data
2627
* @param Session $customerSession
2728
* @param State $appState
2829
* @param SentryInteraction $sentryInteraction
29-
* @param array $handlers
30-
* @param array $processors
3130
*/
3231
public function __construct(
33-
$name,
3432
protected Data $data,
3533
protected Session $customerSession,
3634
private State $appState,
3735
private SentryInteraction $sentryInteraction,
38-
array $handlers = [],
39-
array $processors = []
4036
) {
41-
parent::__construct($name, $handlers, $processors);
4237
}
4338

4439
/**
@@ -76,7 +71,11 @@ function (SentryScope $scope) use ($context, $customTags): void {
7671
if ($message instanceof \Throwable) {
7772
$lastEventId = \Sentry\captureException($message);
7873
} else {
79-
$lastEventId = \Sentry\captureMessage($message, \Sentry\Severity::fromError($logLevel));
74+
$lastEventId = \Sentry\captureMessage(
75+
$message,
76+
\Sentry\Severity::fromError($logLevel),
77+
$this->monologContextToSentryHint($context)
78+
);
8079
}
8180

8281
/// when using JS SDK you can use this for custom error page printing
@@ -89,6 +88,29 @@ function (SentryScope $scope) use ($context, $customTags): void {
8988
}
9089
}
9190

91+
/**
92+
* Turn the monolog context into a format Sentrys EventHint can deal with.
93+
*
94+
* @param array $context
95+
*
96+
* @return EventHint|null
97+
*/
98+
public function monologContextToSentryHint(array $context): ?EventHint
99+
{
100+
return EventHint::fromArray(
101+
[
102+
'exception' => ($context['exception'] ?? null) instanceof \Throwable ? $context['exception'] : null,
103+
'mechanism' => ($context['mechanism'] ?? null) instanceof ExceptionMechanism ? $context['mechanism'] : null,
104+
'stacktrace' => ($context['stacktrace'] ?? null) instanceof Stacktrace ? $context['stacktrace'] : null,
105+
'extra' => array_filter(
106+
$context,
107+
fn ($key) => !in_array($key, ['exception', 'mechanism', 'stacktrace']),
108+
ARRAY_FILTER_USE_KEY
109+
) ?: [],
110+
]
111+
);
112+
}
113+
92114
/**
93115
* Check if we can retrieve customer data.
94116
*

Plugin/MonologPlugin.php

100755100644
+12-37
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,31 @@
22

33
namespace JustBetter\Sentry\Plugin;
44

5-
use JustBetter\Sentry\Helper\Data;
6-
use JustBetter\Sentry\Model\SentryLog;
7-
use Magento\Framework\App\DeploymentConfig;
5+
use JustBetter\Sentry\Logger\Handler\Sentry;
86
use Magento\Framework\Logger\Monolog;
9-
use Monolog\DateTimeImmutable;
7+
use Monolog\LogRecord;
8+
use Monolog\Processor\ProcessorInterface;
109

1110
class MonologPlugin extends Monolog
1211
{
1312
/**
1413
* @psalm-param array<callable(array): array> $processors
1514
*
16-
* @param string $name The logging channel, a simple descriptive name that is attached to all log records
17-
* @param Data $sentryHelper
18-
* @param SentryLog $sentryLog
19-
* @param DeploymentConfig $deploymentConfig
20-
* @param \Monolog\Handler\HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc.
21-
* @param callable[] $processors Optional array of processors
15+
* @param string $name The logging channel, a simple descriptive name that is attached to all log records
16+
* @param Sentry $sentryHandler
17+
* @param \Monolog\Handler\HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc.
18+
* @param callable[] $processors Optional array of processors
19+
*
20+
* @phpstan-param array<(callable(LogRecord|array): LogRecord|array)|ProcessorInterface> $processors
2221
*/
2322
public function __construct(
2423
$name,
25-
protected Data $sentryHelper,
26-
protected SentryLog $sentryLog,
27-
protected DeploymentConfig $deploymentConfig,
24+
Sentry $sentryHandler,
2825
array $handlers = [],
2926
array $processors = []
3027
) {
31-
parent::__construct($name, $handlers, $processors);
32-
}
28+
$handlers['sentry'] = $sentryHandler;
3329

34-
/**
35-
* Adds a log record to Sentry.
36-
*
37-
* @param int $level The logging level
38-
* @param string $message The log message
39-
* @param array $context The log context
40-
* @param DateTimeImmutable $datetime Datetime of log
41-
*
42-
* @return bool Whether the record has been processed
43-
*/
44-
public function addRecord(
45-
int $level,
46-
string $message,
47-
array $context = [],
48-
DateTimeImmutable $datetime = null
49-
): bool {
50-
if ($this->deploymentConfig->isAvailable() && $this->sentryHelper->isActive()) {
51-
$this->sentryLog->send($message, $level, $context);
52-
}
53-
54-
// @phpstan-ignore argument.type
55-
return parent::addRecord($level, $message, $context, $datetime);
30+
parent::__construct($name, $handlers, $processors);
5631
}
5732
}

composer.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
"require": {
88
"php": ">=8.0",
99
"sentry/sdk": "^4.0",
10-
"monolog/monolog": ">=2.7.0",
10+
"monolog/monolog": ">=2.7.0|^3.0",
1111
"magento/framework": "*",
12+
"magento/module-csp": "*",
1213
"nyholm/psr7": "^1.2",
1314
"magento/module-config": "^101.2"
1415
},
@@ -51,7 +52,7 @@
5152
},
5253
"scripts": {
5354
"analyse": "vendor/bin/phpstan analyse --memory-limit='1G'",
54-
"phpcs": "vendor/bin/phpcs --colors --standard=vendor/magento/magento-coding-standard/Magento2 --exclude=Generic.Files.LineLength --report=full,summary,gitblame --extensions=php,phtml --ignore=./vendor ./",
55+
"phpcs": "vendor/bin/phpcs --colors --standard=vendor/magento/magento-coding-standard/Magento2 -s --exclude=Generic.Files.LineLength --report=full,summary,gitblame --extensions=php,phtml --ignore=./vendor ./",
5556
"phpcbf": "vendor/bin/phpcbf --colors --standard=vendor/magento/magento-coding-standard/Magento2 --exclude=Generic.Files.LineLength --extensions=php,phtml --ignore=./vendor ./ || exit 0",
5657
"codestyle": [
5758
"@phpcbf",

etc/di.xml

-23
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,6 @@
11
<?xml version="1.0"?>
22
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
33
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
4-
<type name="JustBetter\Sentry\Model\SentryLog">
5-
<arguments>
6-
<argument name="name" xsi:type="string">SentryLog</argument>
7-
<argument name="handlers" xsi:type="array">
8-
<item name="system" xsi:type="object">Magento\Framework\Logger\Handler\System</item>
9-
</argument>
10-
<argument name="processors" xsi:type="array"/>
11-
<argument name="customerSession" xsi:type="object">Magento\Customer\Model\Session\Proxy</argument>
12-
</arguments>
13-
</type>
14-
15-
<type name="JustBetter\Sentry\Plugin\MonologPlugin">
16-
<arguments>
17-
<argument name="name" xsi:type="string"></argument>
18-
<argument name="data" xsi:type="object">JustBetter\Sentry\Helper\Data\Proxy</argument>
19-
<argument name="sentryLog" xsi:type="object">JustBetter\Sentry\Model\SentryLog\Proxy</argument>
20-
<argument name="deploymentConfig" xsi:type="object">Magento\Framework\App\DeploymentConfig\Proxy</argument>
21-
<argument name="handlers" xsi:type="array"></argument>
22-
<argument name="processors" xsi:type="array"></argument>
23-
</arguments>
24-
</type>
25-
264
<!-- Cannot use plugin https://github.com/magento/magento2/issues/14950 -->
275
<preference for="Magento\Framework\Logger\Monolog" type="JustBetter\Sentry\Plugin\MonologPlugin"/>
286
<type name="Magento\Framework\AppInterface">
@@ -42,5 +20,4 @@
4220
</argument>
4321
</arguments>
4422
</type>
45-
4623
</config>

phpstan.neon

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ parameters:
66
excludePaths:
77
- vendor
88
- Test/*
9-
level: 5
9+
- Logger/Handler/Sentry.php
10+
level: 5

0 commit comments

Comments
 (0)