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];
}
}