分享一组比 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
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

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