setName('clean:logs') ->setDescription('清除过期的日志文件') ->addOption('days', 'd', Option::VALUE_OPTIONAL, '保留天数(默认:10天)', 10) ->addOption('dry-run', null, Option::VALUE_NONE, '预览模式,不实际删除文件'); } protected function execute(Input $input, Output $output) { $days = (int)$input->getOption('days'); $dryRun = $input->getOption('dry-run'); if ($days <= 0) { $output->writeln('保留天数必须大于0'); return false; } if ($dryRun) { $output->writeln('运行在预览模式,不会实际删除文件'); } $output->writeln("===================================="); $output->writeln(" 清除过期日志文件"); $output->writeln("===================================="); $output->writeln("保留天数: {$days} 天"); $output->writeln(""); // 获取日志目录 $logPath = App::getRuntimePath() . 'log' . DIRECTORY_SEPARATOR; if (!is_dir($logPath)) { $output->writeln("日志目录不存在: {$logPath}"); return false; } // 计算截止时间(保留指定天数之前的日志) $cutoffTime = time() - ($days * 24 * 60 * 60); $cutoffDate = date('Y-m-d H:i:s', $cutoffTime); $output->writeln("清除 {$cutoffDate} 之前的日志文件"); $output->writeln(""); // 统计信息 $totalFiles = 0; $deletedFiles = 0; $totalSize = 0; $freedSize = 0; try { // 递归扫描日志目录 $result = $this->cleanLogDirectory($logPath, $cutoffTime, $dryRun, $output); $totalFiles = $result['total']; $deletedFiles = $result['deleted']; $totalSize = $result['totalSize']; $freedSize = $result['freedSize']; } catch (\Exception $e) { $output->writeln('清除日志时发生错误: ' . $e->getMessage() . ''); Log::error('清除日志失败: ' . $e->getMessage()); return false; } // 输出统计信息 $output->writeln(""); $output->writeln("===================================="); $output->writeln(" 清除完成"); $output->writeln("===================================="); $output->writeln("扫描文件数: {$totalFiles}"); $output->writeln("删除文件数: {$deletedFiles}"); $output->writeln("释放空间: " . $this->formatBytes($freedSize)); if ($dryRun) { $output->writeln(""); $output->writeln("预览模式:实际未删除任何文件"); } return true; } /** * 递归清理日志目录 */ protected function cleanLogDirectory($dir, $cutoffTime, $dryRun, Output $output) { $total = 0; $deleted = 0; $totalSize = 0; $freedSize = 0; if (!is_dir($dir)) { return ['total' => 0, 'deleted' => 0, 'totalSize' => 0, 'freedSize' => 0]; } $items = scandir($dir); foreach ($items as $item) { if ($item === '.' || $item === '..') { continue; } $path = $dir . $item; if (is_dir($path)) { // 递归处理子目录 $result = $this->cleanLogDirectory($path . DIRECTORY_SEPARATOR, $cutoffTime, $dryRun, $output); $total += $result['total']; $deleted += $result['deleted']; $totalSize += $result['totalSize']; $freedSize += $result['freedSize']; } elseif (is_file($path)) { $total++; $fileSize = filesize($path); $totalSize += $fileSize; // 获取文件修改时间 $fileMTime = filemtime($path); // 如果文件修改时间早于截止时间,则删除 if ($fileMTime < $cutoffTime) { $freedSize += $fileSize; if ($dryRun) { $output->writeln("[预览] 将删除: {$path} (" . date('Y-m-d H:i:s', $fileMTime) . ", " . $this->formatBytes($fileSize) . ")"); } else { if (@unlink($path)) { $deleted++; $output->writeln("已删除: {$path}"); } else { $output->writeln("删除失败: {$path}"); } } } } } return [ 'total' => $total, 'deleted' => $deleted, 'totalSize' => $totalSize, 'freedSize' => $freedSize, ]; } /** * 格式化字节数 */ protected function formatBytes($bytes, $precision = 2) { $units = ['B', 'KB', 'MB', 'GB', 'TB']; if ($bytes == 0) { return '0 B'; } $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= pow(1024, $pow); return round($bytes, $precision) . ' ' . $units[$pow]; } }