分享一组比 laravel 调度事件中 AppendOutputTo 方法更为便捷的宏方法

分享一组比 laravel 调度事件中 AppendOutputTo 方法更为便捷的宏方法

特性

  • 默认根据命令名称自动生成输出日志文件路径
  • 默认输出日志文件路径格式为 storage/logs/schedules/命令名称/命令名称.log
  • 由 single 渠道日志自动记录命令运行的开始运行时间,这样就会形成 laravel 标准的日志文件,可以更好的由第三方扩展去解析查看,例如 opcodesio/log-viewer
  • 更为便捷的一组按年、季、月、周、日切割输出日志的方法

源码 - app/Support/Macros/SchedulingEventMacro.php

<?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...

原文链接

本作品采用《CC 协议》,转载必须注明作者和本文链接
No practice, no gain in one's wit. 我的 Gitub
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
60
粉丝
133
喜欢
994
收藏
1354
排名:45
访问:15.5 万
私信
所有博文
社区赞助商