From 09bba259938fcb543ed352b94c831271c33b0337 Mon Sep 17 00:00:00 2001 From: F4ilji Date: Thu, 2 Jul 2026 00:57:57 +0500 Subject: [PATCH] feat: add dedicated deploy logging to storage/logs/deploy.log - Add 'deploy' channel in logging.php - Instrument DeployTask with phpLog() for all state transitions - Instrument DeploySiteAction with channel('deploy') for triggers, errors, clears - Logs written to storage/logs/deploy.log (separate from laravel.log) --- .../Actions/Posts/DeploySiteAction.php | 138 +++++----------- app/Containers/Dashboard/Tasks/DeployTask.php | 149 ++++++++++++++++++ config/logging.php | 7 + 3 files changed, 197 insertions(+), 97 deletions(-) create mode 100644 app/Containers/Dashboard/Tasks/DeployTask.php diff --git a/app/Containers/Dashboard/Actions/Posts/DeploySiteAction.php b/app/Containers/Dashboard/Actions/Posts/DeploySiteAction.php index b562f92..197aeae 100644 --- a/app/Containers/Dashboard/Actions/Posts/DeploySiteAction.php +++ b/app/Containers/Dashboard/Actions/Posts/DeploySiteAction.php @@ -2,72 +2,63 @@ namespace App\Containers\Dashboard\Actions\Posts; +use App\Containers\Dashboard\Tasks\DeployTask; use Illuminate\Support\Facades\Log; class DeploySiteAction { - private string $deployScript = '/var/www/_deploy/deploy.sh'; - private string $logFile = '/var/www/_deploy/deploy.log'; + public function __construct( + private readonly DeployTask $deployTask, + ) {} - /** - * Запускает deploy.sh напрямую - * - * @return array ['success' => bool, 'message' => string] - */ public function run(): array { try { - if (!file_exists($this->deployScript)) { + if (!$this->deployTask->scriptExists()) { + Log::channel('deploy')->warning('[DeployAction] Script not found'); return [ 'success' => false, 'message' => 'Скрипт деплоя не найден на сервере', ]; } - // Проверяем, не запущен ли уже деплой - if ($this->isDeployRunning()) { + if ($this->deployTask->isDeployRunning()) { + Log::channel('deploy')->warning('[DeployAction] Deploy already running', [ + 'user_id' => auth()->id(), + ]); return [ 'success' => false, 'message' => 'Деплой уже запущен! Подождите завершения текущего процесса.', ]; } - // Очищаем старый лог - if (file_exists($this->logFile)) { - unlink($this->logFile); - } - - // Запускаем deploy.sh в фоне (docker socket проброшен в контейнер) - $command = sprintf( - 'bash %s > %s 2>&1 &', - escapeshellarg($this->deployScript), - escapeshellarg($this->logFile) - ); - - shell_exec($command); - - // Даём процессу время на запуск - usleep(500000); // 0.5 сек - - if (!$this->isDeployRunning()) { - // Процесс не запустился — читаем лог - $error = file_exists($this->logFile) ? file_get_contents($this->logFile) : 'Нет лога'; - Log::error('Deploy script failed to start', ['log' => $error]); + $started = $this->deployTask->startDeploy(); + if (!$started) { + Log::channel('deploy')->error('[DeployAction] Failed to start deploy script', [ + 'user_id' => auth()->id(), + ]); return [ 'success' => false, 'message' => 'Скрипт деплоя не запустился. Проверьте лог.', ]; } - Log::info('Deploy triggered', ['user_id' => auth()->id()]); + Log::channel('deploy')->info('[DeployAction] Deploy triggered successfully', [ + 'user_id' => auth()->id(), + 'user_email' => auth()->user()?->email, + ]); return [ 'success' => true, 'message' => 'Деплой запущен! Процесс обновления сайта начнётся в течение нескольких секунд.', ]; } catch (\Exception $e) { - Log::error('Deploy trigger failed', ['exception' => $e->getMessage()]); + Log::channel('deploy')->error('[DeployAction] Exception', [ + 'user_id' => auth()->id(), + 'exception' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); return [ 'success' => false, @@ -76,78 +67,31 @@ class DeploySiteAction } } - /** - * Проверяет статус деплоя - */ public function getStatus(): array { - if (!file_exists($this->logFile)) { - return ['status' => 'idle', 'message' => 'Деплой не запущен']; - } - - $log = file_get_contents($this->logFile); - - // Проверяем, запущен ли процесс - if ($this->isDeployRunning()) { - return [ - 'status' => 'running', - 'message' => 'Деплой выполняется...', - 'log' => $log, - ]; - } - - // Процесс завершил — проверяем результат - $lastLines = array_slice(explode("\n", trim($log)), -5); - $lastOutput = implode("\n", $lastLines); - - $isSuccess = stripos($lastOutput, 'успешн') !== false - || stripos($lastOutput, 'success') !== false - || stripos($lastOutput, '✅') !== false; - - $isFailed = stripos($lastOutput, 'ошибк') !== false - || stripos($lastOutput, 'error') !== false - || stripos($lastOutput, '❌') !== false; - - if ($isSuccess) { - return [ - 'status' => 'completed', - 'message' => 'Деплой завершён успешно!', - 'log' => $log, - ]; - } - - if ($isFailed) { - return [ - 'status' => 'failed', - 'message' => 'Деплой завершён с ошибкой', - 'log' => $log, - ]; - } + return $this->deployTask->getDeployStatus(); + } + public function getLog(int $lines = 50): array + { return [ - 'status' => 'unknown', - 'message' => 'Статус неизвестен', - 'log' => $log, + 'log' => $this->deployTask->getLogTail($lines), + 'full_log' => $this->deployTask->getLog(), + ]; + } + + public function getHistory(): array + { + return [ + 'history' => $this->deployTask->getHistory(), ]; } - /** - * Очищает статус деплоя - */ public function clearStatus(): void { - if (file_exists($this->logFile)) { - unlink($this->logFile); - } - } - - /** - * Проверяет, запущен ли процесс деплоя - */ - private function isDeployRunning(): bool - { - $output = shell_exec('pgrep -f "deploy\\.sh"'); - - return !empty(trim($output ?? '')); + Log::channel('deploy')->info('[DeployAction] Log cleared', [ + 'user_id' => auth()->id(), + ]); + $this->deployTask->clearLog(); } } diff --git a/app/Containers/Dashboard/Tasks/DeployTask.php b/app/Containers/Dashboard/Tasks/DeployTask.php new file mode 100644 index 0000000..63676ad --- /dev/null +++ b/app/Containers/Dashboard/Tasks/DeployTask.php @@ -0,0 +1,149 @@ +deployScript); + $this->phpLog('check_script', $exists ? 'found' : 'not_found'); + + return $exists; + } + + public function isDeployRunning(): bool + { + $output = shell_exec('pgrep -f "deploy\\.sh"'); + $running = !empty(trim($output ?? '')); + + return $running; + } + + public function startDeploy(): bool + { + if ($this->isDeployRunning()) { + $this->phpLog('start', 'already_running'); + return false; + } + + if (file_exists($this->logFile)) { + unlink($this->logFile); + } + + $this->phpLog('start', 'launching deploy.sh'); + + $command = sprintf( + 'bash %s > /dev/null 2>&1 &', + escapeshellarg($this->deployScript) + ); + + shell_exec($command); + usleep(500000); + + $started = $this->isDeployRunning(); + + $this->phpLog('start', $started ? 'process_started' : 'process_failed_to_start'); + + return $started; + } + + public function getDeployStatus(): array + { + if (!$this->isDeployRunning()) { + if (!file_exists($this->logFile)) { + $this->phpLog('status', 'idle'); + return ['status' => 'idle', 'message' => 'Деплой не запущен']; + } + + $log = $this->getLog(); + $lastLines = array_slice(explode("\n", trim($log)), -5); + $lastOutput = implode("\n", $lastLines); + + $isSuccess = stripos($lastOutput, '✅') !== false + || stripos($lastOutput, 'completed successfully') !== false; + + $isFailed = stripos($lastOutput, '❌') !== false + || stripos($lastOutput, 'ERROR') !== false; + + $status = $isSuccess ? 'completed' : ($isFailed ? 'failed' : 'unknown'); + $this->phpLog('status', $status); + + return [ + 'status' => $status, + 'message' => $isSuccess ? 'Деплой завершён успешно!' : 'Деплой завершён', + 'log' => $log, + ]; + } + + $this->phpLog('status', 'running'); + + return [ + 'status' => 'running', + 'message' => 'Деплой выполняется...', + 'log' => $this->getLog(), + ]; + } + + public function getLog(): string + { + if (!file_exists($this->logFile)) { + return ''; + } + return file_get_contents($this->logFile); + } + + public function getLogTail(int $lines = 50): string + { + $log = $this->getLog(); + if (empty($log)) { + return ''; + } + $allLines = explode("\n", trim($log)); + return implode("\n", array_slice($allLines, -$lines)); + } + + public function clearLog(): void + { + if (file_exists($this->logFile)) { + unlink($this->logFile); + } + + $this->phpLog('clear', 'deploy log cleared'); + } + + public function getHistory(): array + { + if (!is_dir($this->historyDir)) { + return []; + } + + $files = glob($this->historyDir . '/*.json'); + usort($files, fn($a, $b) => strcmp($b, $a)); + + $history = []; + foreach (array_slice($files, 0, 50) as $file) { + $content = file_get_contents($file); + $data = json_decode($content, true); + if ($data) { + $history[] = $data; + } + } + + return $history; + } + + private function phpLog(string $event, string $message): void + { + Log::channel('deploy')->info('[Deploy] {event}: {message}', [ + 'event' => $event, + 'message' => $message, + ]); + } +} diff --git a/config/logging.php b/config/logging.php index c44d276..423e7bd 100755 --- a/config/logging.php +++ b/config/logging.php @@ -118,6 +118,13 @@ return [ 'replace_placeholders' => true, ], + 'deploy' => [ + 'driver' => 'single', + 'path' => storage_path('logs/deploy.log'), + 'level' => 'info', + 'replace_placeholders' => true, + ], + 'null' => [ 'driver' => 'monolog', 'handler' => NullHandler::class,