setModules([]); $event->setOsContext(null); $exceptions = array_map( [self::class, 'getAnonymizedSentryException'], $event->getExceptions() ); $event->setExceptions($exceptions); return $event; } /** * Anonymize Sentry Exception bag. */ private static function getAnonymizedSentryException(ExceptionDataBag $exceptionBag): ExceptionDataBag { $exceptionBag->setValue(self::getAnonymizedMessage($exceptionBag->getValue())); if ($stackTrace = $exceptionBag->getStacktrace()) { $exceptionBag->setStacktrace(self::getAnonymizedSentryStackTrace($stackTrace)); } return $exceptionBag; } /** * Anonymize sentry stacktrace. */ private static function getAnonymizedSentryStackTrace(Stacktrace $stackTrace): Stacktrace { $frames = $stackTrace->getFrames(); if (empty($frames)) { return $stackTrace; } $firstFrame = $frames[0]; $firstFrame->setPostContext([]); $firstFrame->setPreContext([]); $firstFrame->setContextLine(''); $firstFrame->setVars(self::getAnonymizedSentryParams($firstFrame->getVars())); return new Stacktrace([$firstFrame]); } /** * Get anonymized sentry params. * * @template T of mixed * * @param array $params * @return array */ private static function getAnonymizedSentryParams(array $params): array { foreach ($params as &$param) { $type = gettype($param); $param = match (true) { is_object($param) => $param, is_array($param) => sprintf('Array(%d)', count($param)), is_int($param), is_float($param) => ucfirst($type), is_string($param) => str_starts_with(trim(strtolower($param)), 'lambda') ? $param : sprintf('String(%d)', strlen($param)), default => ucfirst($type), }; } 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')) !== false) { $message = mb_strcut($message, 0, $sqlPos); } return $message; } /** * Anonymize standard PHP stack trace. */ public static function getAnonymizedStackTrace(\Throwable $e): string { $projectPath = realpath(app_path('../')) ?: ''; $traceStr = ''; foreach ($e->getTrace() as $stage => $stack) { $file = str_replace($projectPath, '', $stack['file'] ?? ''); $line = $stack['line'] ?? ''; $traceStr .= "#{$stage} {$file}({$line}): "; if (isset($stack['class'])) { $traceStr .= "{$stack['class']}->"; } $traceStr .= "{$stack['function']}("; $traceStr .= isset($stack['args']) ? self::handleArgs($stack['args']) : ''; $traceStr .= ")\n"; } $nextStage = count($e->getTrace()); return $traceStr . "#{$nextStage} {main}"; } /** * Handle Args. * * @param array $args */ public static function handleArgs(array $args): string { $formattedArgs = array_map(function ($arg) { $type = gettype($arg); return match (true) { is_object($arg) => sprintf('Object(%s)', $arg::class), is_array($arg) => sprintf('Array(%d)', count($arg)), is_string($arg) => sprintf('String(%d)', strlen((string) $arg)), default => $type, }; }, $args); return implode(',', $formattedArgs); } }