分享一组比 laravel 调度事件中 AppendOutputTo 方法更为便捷的宏方法
特性
- 默认根据命令名称自动生成输出日志文件路径
- 默认输出日志文件路径格式为
storage/logs/schedules/命令名称/命令名称.log
- 由 single 渠道日志自动记录命令运行的开始运行时间,这样就会形成 laravel 标准的日志文件,可以更好的由第三方扩展去解析查看,例如 opcodesio/log-viewer
- 更为便捷的一组按年、季、月、周、日切割输出日志的方法
<?php
declare(strict_types=1);
namespace App\Support\Macros;
use Illuminate\Console\Scheduling\Event;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Stringable;
/**
* @mixin Event
*
* @property $channels
*/
class SchedulingEventMacro
{
public function userAppendOutputToDaily(): callable
{
return fn (
?string $filename = null,
?string $dirname = null
): Event => $this->userAppendOutputTo($filename, sprintf('daily-%s', date('Y-m-d')), $dirname);
}
public function userAppendOutputToWeekly(): callable
{
return fn (
?string $filename = null,
?string $dirname = null
): Event => $this->userAppendOutputTo($filename, sprintf('weekly-%s', date('Y-W')), $dirname);
}
public function userAppendOutputToMonthly(): callable
{
return fn (
?string $filename = null,
?string $dirname = null
): Event => $this->userAppendOutputTo($filename, sprintf('monthly-%s', date('Y-m')), $dirname);
}
public function userAppendOutputToQuarterly(): callable
{
return fn (
?string $filename = null,
?string $dirname = null
): Event => $this->userAppendOutputTo(
$filename,
sprintf('quarterly-%s-%s', date('Y'), now()->quarter),
$dirname
);
}
public function userAppendOutputToYearly(): callable
{
return fn (
?string $filename = null,
?string $dirname = null
): Event => $this->userAppendOutputTo($filename, sprintf('yearly-%s', date('Y')), $dirname);
}
public function userAppendOutputTo(): callable
{
return function (?string $filename = null, ?string $suffix = null, ?string $dirname = null): Event {
$outputPath = value(
function (?string $filename, ?string $suffix, ?string $dirname): string {
$filename = value(
function (?string $filename): string {
if ($filename) {
return $filename;
}
// artisan
if (str($this->command)->contains("'artisan'")) {
$commands = (array) explode(' ', $this->command);
return $commands[array_search("'artisan'", $commands, true) + 1];
}
/** @see \Illuminate\Console\Scheduling\CallbackEvent::withoutOverlapping */
if (empty($this->description)) {
throw new \LogicException(
"Please incoming the \$filename parameter, Or use the 'name' method before 'userAppendOutputTo'."
);
}
// exec|call|job
return $this->description;
},
$filename
);
$normalizedFilename = str($filename)->replace([\DIRECTORY_SEPARATOR, '\\', ' '], ['-', '-', '-']);
return (
$dirname
? str($dirname)
: str(storage_path('logs'))
->finish(\DIRECTORY_SEPARATOR)
->append('schedules')
->finish(\DIRECTORY_SEPARATOR)
->append($normalizedFilename)
)
->finish(\DIRECTORY_SEPARATOR)
->append($normalizedFilename)
->when(
$suffix,
static fn (
Stringable $stringable,
string $suffix
) => $stringable->finish('-')->finish($suffix)
)
->append('.log')
->toString();
},
$filename,
$suffix,
$dirname,
);
// dump($outputPath);
return $this
->before(function () use ($outputPath): void {
$singleLogPath = config('logging.channels.single.path');
$unsetSingleChannelHandler = function (): void {
unset($this->channels['single']);
};
config()->set('logging.channels.single.path', $outputPath);
$unsetSingleChannelHandler->call(app('log'));
Log::channel('single')->info('>>>>>>>>');
config()->set('logging.channels.single.path', $singleLogPath);
$unsetSingleChannelHandler->call(app('log'));
})
->appendOutputTo($outputPath);
};
}
}
注册
<?php
declare(strict_types=1);
namespace App\Providers;
use App\Support\Macros\SchedulingEventMacro;
use Illuminate\Console\Scheduling\Event;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::mixin($this->app->make(SchedulingEventMacro::class));
}
}
使用
<?php
declare(strict_types=1);
namespace App\Console;
use App\Console\Commands\CpSyncDataCommand;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Spatie\ScheduleMonitor\Models\MonitoredScheduledTaskLogItem;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
$schedule->command('model:prune', ['--model' => MonitoredScheduledTaskLogItem::class])->daily()->userAppendOutputToMonthly()->withoutOverlapping();
$schedule->command('telescope:prune')->daily()->skip($this->app->isProduction())->userAppendOutputToMonthly()->withoutOverlapping();
$schedule->command(CpSyncDataCommand::class)->hourly()->userAppendOutputToDaily()->withoutOverlapping();
}
}
效果 - storage/logs/schedules/cp:sync-data/cp:sync-data-daily-2024-01-16.log
[2024-01-16 00:00:03] testing.INFO: >>>>>>>> {"php-version":"8.1.26","php-interface":"cli","laravel-version":"10.40.0","running-in-console":true,"X-Request-Id":"68a4470b-d6b4-4bb5-aa8e-feb8c4c43e29","command":"'/usr/bin/php8.1' 'artisan' cp:sync-data"}
Your Command output...
[2024-01-16 01:00:02] testing.INFO: >>>>>>>> {"php-version":"8.1.26","php-interface":"cli","laravel-version":"10.40.0","running-in-console":true,"X-Request-Id":"7e461dac-d11c-4e25-94b5-d6976f009157","command":"'/usr/bin/php8.1' 'artisan' cp:sync-data"}
Your Command output...
原文链接
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。