CleanSizeRotatingLogsCommand.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <?php
  2. namespace UCore\Commands;
  3. use Illuminate\Console\Command;
  4. use Illuminate\Support\Facades\File;
  5. use Carbon\Carbon;
  6. /**
  7. * 清理 size_rotating_daily 日志文件的计划任务
  8. *
  9. * 功能:
  10. * 1. 删除超过指定天数的日志备份文件
  11. * 2. 自动读取 size_rotating_daily.days 配置作为默认保留天数
  12. * 3. 提供详细的清理报告
  13. */
  14. class CleanSizeRotatingLogsCommand extends Command
  15. {
  16. /**
  17. * 命令签名
  18. *
  19. * @var string
  20. */
  21. protected $signature = 'ucore:clean-size-rotating-logs {--days= : 保留的天数,默认使用 size_rotating_daily.days 配置} {--dry-run : 仅显示将要删除的文件,不实际删除}';
  22. /**
  23. * 命令描述
  24. *
  25. * @var string
  26. */
  27. protected $description = '清理 size_rotating_daily 目录中超过指定天数的日志备份文件';
  28. /**
  29. * 日志备份目录路径
  30. *
  31. * @var string
  32. */
  33. protected string $logBackupPath;
  34. /**
  35. * 构造函数
  36. */
  37. public function __construct()
  38. {
  39. parent::__construct();
  40. $this->logBackupPath = storage_path('logs/size_rotating_daily');
  41. }
  42. /**
  43. * 获取默认保留天数
  44. * 从 size_rotating_daily 日志配置中读取 days 参数
  45. *
  46. * @return int
  47. */
  48. protected function getDefaultDays(): int
  49. {
  50. $loggingConfig = config('logging.channels.size_rotating_daily', []);
  51. return $loggingConfig['days'] ?? 7; // 如果配置不存在,默认7天
  52. }
  53. /**
  54. * 执行命令
  55. *
  56. * @return int
  57. */
  58. public function handle(): int
  59. {
  60. // 如果没有指定 days 参数,使用配置文件中的值
  61. $days = $this->option('days');
  62. if ($days === null) {
  63. $days = $this->getDefaultDays();
  64. } else {
  65. $days = (int) $days;
  66. }
  67. $dryRun = $this->option('dry-run');
  68. $this->info("开始清理 size_rotating_daily 日志文件...");
  69. // 显示配置来源
  70. if ($this->option('days') === null) {
  71. $this->info("保留天数: {$days} 天 (来自 size_rotating_daily.days 配置)");
  72. } else {
  73. $this->info("保留天数: {$days} 天 (命令行参数)");
  74. }
  75. $this->info("备份目录: {$this->logBackupPath}");
  76. if ($dryRun) {
  77. $this->warn("*** 这是一次试运行,不会实际删除文件 ***");
  78. }
  79. // 检查目录是否存在
  80. if (!File::exists($this->logBackupPath)) {
  81. $this->warn("备份目录不存在: {$this->logBackupPath}");
  82. return Command::SUCCESS;
  83. }
  84. // 计算截止日期
  85. $cutoffDate = Carbon::now()->subDays($days);
  86. $this->info("将删除 {$cutoffDate->format('Y-m-d')} 之前的日志文件");
  87. // 获取所有日志文件
  88. $files = File::files($this->logBackupPath);
  89. if (empty($files)) {
  90. $this->info("备份目录中没有找到任何文件");
  91. return Command::SUCCESS;
  92. }
  93. $deletedFiles = [];
  94. $keptFiles = [];
  95. $totalSize = 0;
  96. foreach ($files as $file) {
  97. $filename = $file->getFilename();
  98. $filePath = $file->getPathname();
  99. $fileModifiedTime = Carbon::createFromTimestamp($file->getMTime());
  100. $fileSize = $file->getSize();
  101. // 检查文件是否超过保留期限
  102. if ($fileModifiedTime->lt($cutoffDate)) {
  103. $deletedFiles[] = [
  104. 'name' => $filename,
  105. 'path' => $filePath,
  106. 'modified' => $fileModifiedTime->format('Y-m-d H:i:s'),
  107. 'size' => $fileSize
  108. ];
  109. $totalSize += $fileSize;
  110. // 如果不是试运行,则删除文件
  111. if (!$dryRun) {
  112. try {
  113. File::delete($filePath);
  114. $this->line("✓ 已删除: {$filename} ({$this->formatBytes($fileSize)})");
  115. } catch (\Exception $e) {
  116. $this->error("✗ 删除失败: {$filename} - {$e->getMessage()}");
  117. }
  118. } else {
  119. $this->line("○ 将删除: {$filename} ({$this->formatBytes($fileSize)}) - 修改时间: {$fileModifiedTime->format('Y-m-d H:i:s')}");
  120. }
  121. } else {
  122. $keptFiles[] = [
  123. 'name' => $filename,
  124. 'modified' => $fileModifiedTime->format('Y-m-d H:i:s'),
  125. 'size' => $fileSize
  126. ];
  127. $this->line("○ 保留: {$filename} ({$this->formatBytes($fileSize)}) - 修改时间: {$fileModifiedTime->format('Y-m-d H:i:s')}");
  128. }
  129. }
  130. // 显示清理报告
  131. $this->newLine();
  132. $this->info("=== 清理报告 ===");
  133. $this->info("总文件数: " . count($files));
  134. $this->info("删除文件数: " . count($deletedFiles));
  135. $this->info("保留文件数: " . count($keptFiles));
  136. $this->info("释放空间: " . $this->formatBytes($totalSize));
  137. if ($dryRun && !empty($deletedFiles)) {
  138. $this->newLine();
  139. $this->warn("要实际执行删除操作,请运行: php artisan ucore:clean-size-rotating-logs --days={$days}");
  140. }
  141. return Command::SUCCESS;
  142. }
  143. /**
  144. * 格式化字节大小
  145. *
  146. * @param int $bytes
  147. * @return string
  148. */
  149. protected function formatBytes(int $bytes): string
  150. {
  151. $units = ['B', 'KB', 'MB', 'GB', 'TB'];
  152. $bytes = max($bytes, 0);
  153. $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
  154. $pow = min($pow, count($units) - 1);
  155. $bytes /= pow(1024, $pow);
  156. return round($bytes, 2) . ' ' . $units[$pow];
  157. }
  158. }