phase 2
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class Fingerprinter
|
||||
{
|
||||
/**
|
||||
* Compute a fingerprint hash for grouping events into issues.
|
||||
*
|
||||
* If the payload contains a custom `fingerprint` array (Sentry SDK standard),
|
||||
* that takes precedence. Otherwise we derive one from exception type, a
|
||||
* sanitized message, and the top stacktrace frames.
|
||||
*/
|
||||
public function compute(array $payload): string
|
||||
{
|
||||
if (! empty($payload['fingerprint']) && is_array($payload['fingerprint'])) {
|
||||
return $this->hash($payload['fingerprint']);
|
||||
}
|
||||
|
||||
$parts = [];
|
||||
|
||||
$exception = $this->extractPrimaryException($payload);
|
||||
|
||||
if ($exception) {
|
||||
$parts[] = $exception['type'] ?? '';
|
||||
$parts[] = $this->sanitizeMessage($exception['value'] ?? '');
|
||||
$parts = array_merge($parts, $this->topFrames($exception, 5));
|
||||
} else {
|
||||
$parts[] = $payload['message'] ?? $payload['logentry']['message'] ?? 'unknown';
|
||||
}
|
||||
|
||||
return $this->hash($parts);
|
||||
}
|
||||
|
||||
public function extractPrimaryException(array $payload): ?array
|
||||
{
|
||||
$exceptions = $payload['exception']['values'] ?? [];
|
||||
|
||||
return end($exceptions) ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip variable parts (numbers, UUIDs, hex strings) so that
|
||||
* "User 42 not found" and "User 99 not found" group together.
|
||||
*/
|
||||
private function sanitizeMessage(string $message): string
|
||||
{
|
||||
$message = preg_replace(
|
||||
'/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i',
|
||||
'{uuid}',
|
||||
$message,
|
||||
);
|
||||
|
||||
$message = preg_replace('/\b[0-9a-f]{24,}\b/i', '{hash}', $message);
|
||||
|
||||
$message = preg_replace('/\b\d+\b/', '{n}', $message);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return file+function strings from the top N stacktrace frames.
|
||||
*/
|
||||
private function topFrames(array $exception, int $limit): array
|
||||
{
|
||||
$frames = $exception['stacktrace']['frames'] ?? [];
|
||||
|
||||
$frames = array_reverse($frames);
|
||||
$frames = array_slice($frames, 0, $limit);
|
||||
|
||||
$parts = [];
|
||||
foreach ($frames as $frame) {
|
||||
$file = $frame['filename'] ?? $frame['abs_path'] ?? '';
|
||||
$function = $frame['function'] ?? '';
|
||||
$parts[] = "{$file}:{$function}";
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
|
||||
private function hash(array $parts): string
|
||||
{
|
||||
return md5(implode("\x00", $parts));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user