10, 'connect_timeout' => 5, 'allow_redirects' => [ 'max' => 3, 'track_redirects' => true, ], 'http_errors' => false, ]); $url = $this->healthCheck->full_url; $startTime = microtime(true); Log::info('CheckHealthEndpoint: Starting check', [ 'health_check_id' => $this->healthCheck->id, 'url' => $url, ]); $response = $client->get($url, [ 'headers' => [ 'Accept' => 'application/json', 'User-Agent' => 'IsMySiteUp Health Monitor/1.0', ], ]); $endTime = microtime(true); $responseTime = (int) round(($endTime - $startTime) * 1000); $statusCode = $response->getStatusCode(); $body = (string) $response->getBody(); // Try to decode JSON $responseData = null; $isValidJson = false; try { $responseData = json_decode($body, true, 512, JSON_THROW_ON_ERROR); $isValidJson = true; } catch (\JsonException $e) { Log::warning('CheckHealthEndpoint: Invalid JSON response', [ 'health_check_id' => $this->healthCheck->id, 'error' => $e->getMessage(), 'body_preview' => substr($body, 0, 500), ]); } // Determine if endpoint is healthy $isHealthy = $this->determineHealth($statusCode, $isValidJson, $responseData); $wasHealthy = $this->healthCheck->is_healthy; // Update health check record $this->healthCheck->update([ 'last_checked_at' => now(), 'is_healthy' => $isHealthy, 'response_data' => $responseData, 'response_time_ms' => $responseTime, 'status_code' => $statusCode, ]); Log::info('CheckHealthEndpoint: Check completed', [ 'health_check_id' => $this->healthCheck->id, 'is_healthy' => $isHealthy, 'status_code' => $statusCode, 'response_time_ms' => $responseTime, ]); // Send notification if health status changed from healthy to unhealthy if ($wasHealthy === true && $isHealthy === false) { $this->healthCheck->site->user->notify(new HealthCheckAlert( $this->healthCheck, 'unhealthy', $statusCode, $responseTime )); Log::warning('CheckHealthEndpoint: Unhealthy alert sent', [ 'health_check_id' => $this->healthCheck->id, 'status_code' => $statusCode, ]); } // Optionally send notification when health is restored if ($wasHealthy === false && $isHealthy === true) { $this->healthCheck->site->user->notify(new HealthCheckAlert( $this->healthCheck, 'healthy', $statusCode, $responseTime )); Log::info('CheckHealthEndpoint: Healthy alert sent', [ 'health_check_id' => $this->healthCheck->id, ]); } // Check performance threshold and send alert if exceeded if ($this->healthCheck->performance_threshold && $responseTime > $this->healthCheck->performance_threshold) { $this->healthCheck->site->user->notify(new PerformanceAlert( resourceType: 'Health Check', resourceName: $this->healthCheck->endpoint, responseTime: $responseTime, threshold: $this->healthCheck->performance_threshold, url: $this->healthCheck->full_url, resourceId: $this->healthCheck->site_id )); Log::warning('CheckHealthEndpoint: Performance threshold exceeded', [ 'health_check_id' => $this->healthCheck->id, 'response_time_ms' => $responseTime, 'threshold_ms' => $this->healthCheck->performance_threshold, ]); } } catch (ConnectException $e) { // Connection timeout or DNS failure Log::error('CheckHealthEndpoint: Connection failed', [ 'health_check_id' => $this->healthCheck->id, 'error' => $e->getMessage(), ]); $this->healthCheck->update([ 'last_checked_at' => now(), 'is_healthy' => false, 'response_data' => ['error' => 'Connection failed: ' . $e->getMessage()], 'response_time_ms' => null, 'status_code' => null, ]); } catch (RequestException $e) { // HTTP error occurred $statusCode = $e->getResponse() ? $e->getResponse()->getStatusCode() : null; Log::error('CheckHealthEndpoint: Request failed', [ 'health_check_id' => $this->healthCheck->id, 'status_code' => $statusCode, 'error' => $e->getMessage(), ]); $this->healthCheck->update([ 'last_checked_at' => now(), 'is_healthy' => false, 'response_data' => ['error' => 'Request failed: ' . $e->getMessage()], 'response_time_ms' => null, 'status_code' => $statusCode, ]); } catch (\Exception $e) { // Any other exception Log::error('CheckHealthEndpoint: Unexpected error', [ 'health_check_id' => $this->healthCheck->id, 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), ]); $this->healthCheck->update([ 'last_checked_at' => now(), 'is_healthy' => false, 'response_data' => ['error' => 'Unexpected error: ' . $e->getMessage()], 'response_time_ms' => null, 'status_code' => null, ]); throw $e; } } /** * Determine if the endpoint is healthy based on response. */ protected function determineHealth(int $statusCode, bool $isValidJson, ?array $responseData): bool { // Check if status code is successful (2xx) if ($statusCode < 200 || $statusCode >= 300) { return false; } // Check if response is valid JSON if (!$isValidJson) { return false; } // Validate expected JSON keys if configured if (!empty($this->healthCheck->expected_json_keys)) { if (!$this->healthCheck->validateResponseKeys($responseData ?? [])) { Log::warning('CheckHealthEndpoint: Missing expected JSON keys', [ 'health_check_id' => $this->healthCheck->id, 'expected_keys' => $this->healthCheck->expected_json_keys, 'response_keys' => array_keys($responseData ?? []), ]); return false; } } return true; } /** * Handle a job failure. */ public function failed(\Throwable $exception): void { Log::error('CheckHealthEndpoint: Job failed after all retries', [ 'health_check_id' => $this->healthCheck->id, 'exception' => $exception->getMessage(), ]); } }