先用php artisan make:command cleanoldlogs创建命令类,再确认日志驱动为daily;精准匹配laravel-y-m-d.log归档文件,排除laravel.log和隐藏文件;通过--days=7参数控制保留天数,校验输入合法性;注册到调度器时须用$schedule->command()而非exec(),并统一cron用户权限。

如何在 Laravel 中创建一个清理日志的自定义 Artisan 命令
直接上手:用 php artisan make:command CleanOldLogs 生成命令类,它会出现在 app/Console/Commands/CleanOldLogs.php。别急着写逻辑,先确认你的日志驱动是 daily(默认),否则 storage/logs/laravel-*.log 文件命名规则不匹配,删错或漏删都可能发生。
关键点在于定位日志路径和保留策略——Laravel 默认把日志存在 storage/logs/,但是否启用了日志轮转、是否用了 stack 或 custom 驱动,都会影响文件结构。建议先运行 ls -la storage/logs/ 看一眼实际文件名格式。
为什么不能直接用 File::delete() 批量删所有旧文件
因为 storage/logs/ 下可能混有非日志文件(比如调试生成的 debug.log、第三方包写的 package-error.log),也可能是正在被写入的当前日志(如 laravel.log)。硬删会中断日志写入,甚至导致 PHP 进程报 failed to open stream: No such file or directory 错误。
安全做法是只匹配符合 Laravel daily 命名规范的归档日志:
-
laravel-Y-m-d.log(如laravel-2024-05-20.log) - 排除
laravel.log(当前活跃日志) - 排除以点开头的隐藏文件(如
.gitkeep)
推荐用 FilesystemAdapter::files() + preg_match('/^laravel-\d{4}-\d{2}-\d{2}\.log$/', $file->getFilename()) 精准筛选,再结合 filemtime() 判断是否超过保留天数。
怎么让命令支持 --days 参数并默认保留 7 天
在 signature 属性里声明:protected $signature = 'logs:clean {--days=7 : Number of days to retain logs}';。这样运行 php artisan logs:clean --days=30 就能灵活控制。
注意参数类型默认是字符串,需手动转为整型:$days = (int) $this->option('days');。如果传了负数或非数字,命令应提前退出并提示错误,避免误删全部日志——这是线上事故高发点。
示例判断逻辑:
if ($days error('Days must be greater than 0.');
return 1;
}
添加到 schedule 后为什么没执行
常见原因有三个:未在 app/Console/Kernel.php 的 schedule() 方法中注册;服务器没配 cron 每分钟调用一次 php /path/to/artisan schedule:run;或者命令本身抛了未捕获异常,被 Laravel 的 scheduler 静默吞掉。
注册方式必须是:
$schedule->command('logs:clean --days=7')->daily();
不是 $schedule->exec('php artisan logs:clean --days=7')->daily()——后者绕过 Laravel 命令生命周期,无法使用 $this->info()、依赖注入等特性,也难调试。
验证是否生效:手动运行 php artisan schedule:run,观察输出;再查 storage/logs/schedule-*.log 里有没有对应记录。如果没日志,说明命令根本没进调度队列。
真正容易被忽略的是权限问题:web 用户(如 nginx)和 cron 用户(如 root)对 storage/logs/ 目录的读写权限不一致,可能导致定时任务删了一半就失败,且无报错。建议统一用部署用户跑 cron,并确保该用户对 storage/ 有完整权限。











