182 lines
5.6 KiB
PHP
182 lines
5.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Lib\Helpers;
|
|
|
|
use Sentry\Event;
|
|
use Sentry\ExceptionDataBag;
|
|
use Sentry\Frame;
|
|
use Sentry\Stacktrace;
|
|
use Throwable;
|
|
|
|
class ExceptionsHelper
|
|
{
|
|
/**
|
|
* Handle sentry event
|
|
*/
|
|
public static function sentryEventHandler(Event $event): ?Event
|
|
{
|
|
// Remove unwanted reports.
|
|
$event->setModules([]);
|
|
$event->setOsContext(null);
|
|
|
|
/** @var ExceptionDataBag[] */
|
|
$exceptionBag = [];
|
|
foreach ($event->getExceptions() as $exceptionDataBag) {
|
|
$exceptionBag[] = self::getAnonymizedSentryException($exceptionDataBag);
|
|
}
|
|
$event->setExceptions($exceptionBag);
|
|
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Anonimize Sentry Exception bag
|
|
*/
|
|
private static function getAnonymizedSentryException(
|
|
ExceptionDataBag $exceptionDataBag
|
|
): ExceptionDataBag {
|
|
$exceptionDataBag->setValue(self::getAnonymizedMessage($exceptionDataBag->getValue()));
|
|
if (($stackTrace = $exceptionDataBag->getStacktrace()) instanceof Stacktrace) {
|
|
$exceptionDataBag->setStacktrace(self::getAnonymizedSentryStackTrace($stackTrace));
|
|
}
|
|
|
|
return $exceptionDataBag;
|
|
}
|
|
|
|
/**
|
|
* Anonymise sentry stacktrace
|
|
*/
|
|
private static function getAnonymizedSentryStackTrace(Stacktrace $stackTrace): Stacktrace
|
|
{
|
|
/** @var Frame[] */
|
|
$frames = \array_reverse($stackTrace->getFrames(), true);
|
|
$i = \count($frames) - 1;
|
|
// * Remove all but keep first (index.php).
|
|
while ($i-- > 0) {
|
|
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
|
|
}
|
|
foreach ($frames as $index => $frame) {
|
|
$frame->setPostContext([]);
|
|
$frame->setPreContext([]);
|
|
$frame->setContextLine('');
|
|
$frame->setVars(self::getAnonymizedSentryParams($frame->getVars()));
|
|
$stackTrace->addFrame($frame);
|
|
if ($index === 0) {
|
|
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
|
|
}
|
|
}
|
|
|
|
return $stackTrace;
|
|
}
|
|
|
|
/**
|
|
* Get anonymized sentry params
|
|
*
|
|
* @param mixed[] $params
|
|
* @return string[]
|
|
*/
|
|
private static function getAnonymizedSentryParams(array $params): array
|
|
{
|
|
foreach ($params as &$param) {
|
|
$type = \strtolower(\gettype($param));
|
|
if (\is_string($param)) {
|
|
$type = \explode(' ', \trim(strtolower($param)))[0] ?? null;
|
|
}
|
|
switch (true) {
|
|
case $type === 'lambda':
|
|
case $type === 'object':
|
|
continue 2;
|
|
case $type === 'array' && \is_array($param):
|
|
$param = sprintf('Array(%d)', \count($param));
|
|
|
|
continue 2;
|
|
default:
|
|
if (\is_numeric($param)) {
|
|
$param = \ucfirst($type);
|
|
|
|
continue 2;
|
|
}
|
|
if (\is_string($param)) {
|
|
$param = sprintf('String(%d)', \strlen($param));
|
|
|
|
continue 2;
|
|
}
|
|
} // end switch
|
|
} // end foreach
|
|
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* Anonymize at least Sql queries
|
|
*/
|
|
public static function getAnonymizedMessage(string $message): string
|
|
{
|
|
$message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
|
|
if ($sqlPos = \strpos($message, 'to use near')) {
|
|
return \mb_strcut($message, 0, $sqlPos);
|
|
}
|
|
|
|
return $message;
|
|
}
|
|
|
|
/**
|
|
* Anonymize stack trace
|
|
*/
|
|
public static function getAnonymizedStackTrace(Throwable $e): string
|
|
{
|
|
$projectPath = \realpath(\sprintf('%s/../', \app_path()));
|
|
$traceStack = $e->getTrace();
|
|
$traceStr = '';
|
|
$maxStackStage = 0;
|
|
foreach ($traceStack as $stackStage => $stack) {
|
|
$maxStackStage = $stackStage;
|
|
$traceStr .= \sprintf(
|
|
'#%s %s(%d): ',
|
|
$stackStage,
|
|
str_replace($projectPath, '', $stack['file'] ?? ''),
|
|
$stack['line'] ?? ''
|
|
);
|
|
if (isset($stack['class'])) {
|
|
$traceStr .= "{$stack['class']}->{$stack['function']}(";
|
|
if (isset($stack['args'])) {
|
|
$traceStr .= self::handleArgs($stack);
|
|
}
|
|
$traceStr .= ")\n";
|
|
} else {
|
|
$traceStr .= "{$stack['function']}(";
|
|
if (isset($stack['args'])) {
|
|
$traceStr .= self::handleArgs($stack);
|
|
}
|
|
$traceStr .= ")\n";
|
|
}
|
|
} // end foreach
|
|
$maxStackStage++;
|
|
$traceStr .= "#$maxStackStage {main}";
|
|
|
|
return $traceStr;
|
|
}
|
|
|
|
/**
|
|
* Handle Args
|
|
*/
|
|
public static function handleArgs(array $stack): string
|
|
{
|
|
$traceArgs = '';
|
|
$argsCount = count($stack['args']);
|
|
for ($i = 0; $i < $argsCount; $i++) {
|
|
$type = \gettype($stack['args'][$i]);
|
|
match ($type) {
|
|
'object' => $traceArgs .= sprintf('Object(%s)', $stack['args'][$i]::class),
|
|
'array' => $traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i])),
|
|
'string' => $traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i])),
|
|
default => $traceArgs .= \gettype($stack['args'][$i]),
|
|
};
|
|
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
|
|
}
|
|
|
|
return $traceArgs;
|
|
}
|
|
}
|