Reputation: 58242
Given the following snippet:
$schedule->call(function () {
// echo "HELLO 123"; // nope
// return "HELLO 123"; // also nope
})
->everyMinute()
->sendOutputTo(storage_path('logs/cron/hello.cron.log'))
->emailOutputTo('[email protected]');
The scheduled task is running, the emails are being generated, but with no content. I can capture output via the $schedule->exec
and $schedule->command
.
Ideal end state here is, run a few internal, non-command processes within the call
method and output the results into a file/email.
Upvotes: 7
Views: 2388
Reputation: 2046
I find it quiet unsatisfying that Laravel does not support running multiple Artisan commands consecutively, including capturing their outputs and stopping in case of exceptions.
\Storage::append()
from @Chris' answer does only write into the storage/app/
directory, but I wanted the logs in storage/logs/
. So I replaced it with \File::append()
.
However, my log file storage/logs/schedule.log
is a symbolic link in production environment and file_put_contents()
, on which both methods rely on, is not able to write to symbolic links.
This is the solution I came up with:
use Illuminate\Support\Facades\Artisan;
use Symfony\Component\Console\Output\OutputInterface;
// ...
$schedule->callWithOutput(function (OutputInterface $output) {
Artisan::call(FooCommand::class, outputBuffer: $output);
Artisan::call(BarCommand::class, outputBuffer: $output);
}, storage_path('logs/schedule.log'))>everyMinute();
Schedule::callWithOutput()
is a macro that takes our callback and captures all output in a BufferedOutput()
. It is eventually redirected to the log file (second argument) with shell_exec()
, so it works with regular files and symlinks:
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;
use Symfony\Component\Console\Output\BufferedOutput;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Schedule::macro('callWithOutput', function (callable $callback, string $logFile, array $parameters = []) {
/** @var Schedule $this */
return $this->call(function () use ($callback, $logFile, $parameters) {
$output = new BufferedOutput();
try {
App::call($callback, compact('output') + $parameters);
} finally {
shell_exec("echo -n '{$output->fetch()}' >> $logFile");
}
});
});
}
}
You can also pass $parameters
(second parameter of Schedule::call()
) to Schedule::callWithOutput()
as third parameter.
If you're not executing Artisan commands, you can write output with $output->write()
or $output->writeln()
.
Upvotes: 0
Reputation: 142
I just googled the documentation of the class you are using with the words laravel scheduled output
and the documentation (Just above the anchor in a red box) states:
Note: The emailOutputTo and sendOutputTo methods are exclusive to the
command method and are not supported for call.
Hope that helps.
Upvotes: 5
Reputation: 58242
The accepted answer got me to the solution, but for future reference:
$schedule->call(function () {
// Do stuff
// Report to file
\Storage::append('logs/cron/hello.cron.log', "HELLO 123");
})->everyMinute();
Upvotes: 2