getMessage(), exception: $throwable::class, file: $throwable->getFile(), line: $throwable->getLine(), trace: $throwable->getTraceAsString(), context: $context, environment: $environment ?? 'production', serverInfo: self::getServerInfo() ); } /** * Create from manual report */ public static function create( string $level, string $message, array $context = [], ?Throwable $exception = null, ?string $environment = null ): self { return new self( id: self::generateId(), timestamp: new DateTimeImmutable(), level: $level, message: $message, exception: $exception?->class ?? 'N/A', file: $exception?->getFile() ?? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'] ?? 'unknown', line: $exception?->getLine() ?? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['line'] ?? 0, trace: $exception?->getTraceAsString() ?? self::getCurrentTrace(), context: $context, environment: $environment ?? 'production', serverInfo: self::getServerInfo() ); } /** * Add user context */ public function withUser(string $userId, ?string $sessionId = null): self { return new self( id: $this->id, timestamp: $this->timestamp, level: $this->level, message: $this->message, exception: $this->exception, file: $this->file, line: $this->line, trace: $this->trace, context: $this->context, userId: $userId, sessionId: $sessionId ?? $this->sessionId, requestId: $this->requestId, userAgent: $this->userAgent, ipAddress: $this->ipAddress, route: $this->route, method: $this->method, requestData: $this->requestData, executionTime: $this->executionTime, memoryUsage: $this->memoryUsage, tags: $this->tags, breadcrumbs: $this->breadcrumbs, release: $this->release, environment: $this->environment, serverInfo: $this->serverInfo, customData: $this->customData ); } /** * Add HTTP request context */ public function withRequest( string $method, string $route, ?string $requestId = null, ?string $userAgent = null, ?string $ipAddress = null, ?array $requestData = null ): self { return new self( id: $this->id, timestamp: $this->timestamp, level: $this->level, message: $this->message, exception: $this->exception, file: $this->file, line: $this->line, trace: $this->trace, context: $this->context, userId: $this->userId, sessionId: $this->sessionId, requestId: $requestId, userAgent: $userAgent, ipAddress: $ipAddress, route: $route, method: $method, requestData: $requestData, executionTime: $this->executionTime, memoryUsage: $this->memoryUsage, tags: $this->tags, breadcrumbs: $this->breadcrumbs, release: $this->release, environment: $this->environment, serverInfo: $this->serverInfo, customData: $this->customData ); } /** * Add performance context */ public function withPerformance(Duration $executionTime, int $memoryUsage): self { return new self( id: $this->id, timestamp: $this->timestamp, level: $this->level, message: $this->message, exception: $this->exception, file: $this->file, line: $this->line, trace: $this->trace, context: $this->context, userId: $this->userId, sessionId: $this->sessionId, requestId: $this->requestId, userAgent: $this->userAgent, ipAddress: $this->ipAddress, route: $this->route, method: $this->method, requestData: $this->requestData, executionTime: $executionTime, memoryUsage: $memoryUsage, tags: $this->tags, breadcrumbs: $this->breadcrumbs, release: $this->release, environment: $this->environment, serverInfo: $this->serverInfo, customData: $this->customData ); } /** * Add tags */ public function withTags(array $tags): self { return new self( id: $this->id, timestamp: $this->timestamp, level: $this->level, message: $this->message, exception: $this->exception, file: $this->file, line: $this->line, trace: $this->trace, context: $this->context, userId: $this->userId, sessionId: $this->sessionId, requestId: $this->requestId, userAgent: $this->userAgent, ipAddress: $this->ipAddress, route: $this->route, method: $this->method, requestData: $this->requestData, executionTime: $this->executionTime, memoryUsage: $this->memoryUsage, tags: array_unique(array_merge($this->tags, $tags)), breadcrumbs: $this->breadcrumbs, release: $this->release, environment: $this->environment, serverInfo: $this->serverInfo, customData: $this->customData ); } /** * Add breadcrumbs */ public function withBreadcrumbs(array $breadcrumbs): self { return new self( id: $this->id, timestamp: $this->timestamp, level: $this->level, message: $this->message, exception: $this->exception, file: $this->file, line: $this->line, trace: $this->trace, context: $this->context, userId: $this->userId, sessionId: $this->sessionId, requestId: $this->requestId, userAgent: $this->userAgent, ipAddress: $this->ipAddress, route: $this->route, method: $this->method, requestData: $this->requestData, executionTime: $this->executionTime, memoryUsage: $this->memoryUsage, tags: $this->tags, breadcrumbs: array_merge($this->breadcrumbs, $breadcrumbs), release: $this->release, environment: $this->environment, serverInfo: $this->serverInfo, customData: $this->customData ); } /** * Add custom data */ public function withCustomData(array $customData): self { return new self( id: $this->id, timestamp: $this->timestamp, level: $this->level, message: $this->message, exception: $this->exception, file: $this->file, line: $this->line, trace: $this->trace, context: $this->context, userId: $this->userId, sessionId: $this->sessionId, requestId: $this->requestId, userAgent: $this->userAgent, ipAddress: $this->ipAddress, route: $this->route, method: $this->method, requestData: $this->requestData, executionTime: $this->executionTime, memoryUsage: $this->memoryUsage, tags: $this->tags, breadcrumbs: $this->breadcrumbs, release: $this->release, environment: $this->environment, serverInfo: $this->serverInfo, customData: array_merge($this->customData, $customData) ); } /** * Get severity level as integer */ public function getSeverityLevel(): int { return match (strtolower($this->level)) { 'emergency' => 0, 'alert' => 1, 'critical' => 2, 'error' => 3, 'warning' => 4, 'notice' => 5, 'info' => 6, 'debug' => 7, default => 3, }; } /** * Check if error is critical */ public function isCritical(): bool { return $this->getSeverityLevel() <= 2; } /** * Get fingerprint for grouping similar errors */ public function getFingerprint(): string { $key = $this->exception . '|' . $this->file . '|' . $this->line; return hash('xxh64', $key); } /** * Convert to array for storage/transmission */ public function toArray(): array { return [ 'id' => $this->id, 'timestamp' => $this->timestamp->format('c'), 'level' => $this->level, 'message' => $this->message, 'exception' => $this->exception, 'file' => $this->file, 'line' => $this->line, 'trace' => $this->trace, 'context' => $this->context, 'user_id' => $this->userId, 'session_id' => $this->sessionId, 'request_id' => $this->requestId, 'user_agent' => $this->userAgent, 'ip_address' => $this->ipAddress, 'route' => $this->route, 'method' => $this->method, 'request_data' => $this->requestData, 'execution_time' => $this->executionTime?->toMilliseconds(), 'memory_usage' => $this->memoryUsage, 'tags' => $this->tags, 'breadcrumbs' => $this->breadcrumbs, 'release' => $this->release, 'environment' => $this->environment, 'server_info' => $this->serverInfo, 'custom_data' => $this->customData, 'fingerprint' => $this->getFingerprint(), 'severity_level' => $this->getSeverityLevel(), ]; } private static function generateId(): string { return bin2hex(random_bytes(16)); } private static function getCurrentTrace(): string { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); array_shift($trace); // Remove current method $result = []; foreach ($trace as $frame) { $file = $frame['file'] ?? 'unknown'; $line = $frame['line'] ?? 0; $function = $frame['function'] ?? 'unknown'; $class = isset($frame['class']) ? $frame['class'] . '::' : ''; $result[] = "#{$file}({$line}): {$class}{$function}()"; } return implode("\n", $result); } private static function getServerInfo(): array { return [ 'php_version' => PHP_VERSION, 'server_software' => $_SERVER['SERVER_SOFTWARE'] ?? 'unknown', 'server_name' => $_SERVER['SERVER_NAME'] ?? 'unknown', 'request_time' => $_SERVER['REQUEST_TIME'] ?? time(), 'memory_limit' => ini_get('memory_limit'), 'max_execution_time' => ini_get('max_execution_time'), ]; } }