diff --git a/app/code/Magento/Cron/Console/Command/CronCommand.php b/app/code/Magento/Cron/Console/Command/CronCommand.php
index 4032a74802652..4e8c4ea1a4404 100644
--- a/app/code/Magento/Cron/Console/Command/CronCommand.php
+++ b/app/code/Magento/Cron/Console/Command/CronCommand.php
@@ -1,7 +1,8 @@
create(Cron::class, ['parameters' => $params]);
$cronObserver->launch();
- $output->writeln('' . 'Ran jobs by schedule.' . '');
+
+ // phpcs:ignore Magento2.Functions.DiscouragedFunction.Discouraged,Magento2.Exceptions.TryProcessSystemResources.MissingTryCatch
+ if (stream_isatty(STDOUT)) {
+ $output->writeln('' . 'Ran jobs by schedule.' . '');
+ }
return Cli::RETURN_SUCCESS;
}
diff --git a/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php b/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php
index 230b24ef4471f..d000f7dbbeb31 100644
--- a/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php
+++ b/app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php
@@ -1,7 +1,7 @@
getCronGroupConfigurationValue($groupId, 'use_separate_process') == 1
) {
$this->_shell->execute(
- $phpPath . ' %s cron:run --group=' . $groupId . ' --' . Cli::INPUT_KEY_BOOTSTRAP . '='
+ '%s %s cron:run --group=%s --' . Cli::INPUT_KEY_BOOTSTRAP . '='
. self::STANDALONE_PROCESS_STARTED . '=1',
[
- BP . '/bin/magento'
+ $phpPath,
+ BP . '/bin/magento',
+ $groupId,
]
);
continue;
@@ -848,7 +850,7 @@ private function processPendingJobs(string $groupId, array $jobsRoot, int $curre
/** @var Schedule $schedule */
foreach ($pendingJobs as $schedule) {
if (isset($processedJobs[$schedule->getJobCode()])) {
- // process only on job per run
+ // process only one of each job per run
continue;
}
$jobConfig = isset($jobsRoot[$schedule->getJobCode()]) ? $jobsRoot[$schedule->getJobCode()] : null;
diff --git a/app/code/Magento/Cron/Shell/CommandRendererBackground.php b/app/code/Magento/Cron/Shell/CommandRendererBackground.php
new file mode 100644
index 0000000000000..c2234c668a355
--- /dev/null
+++ b/app/code/Magento/Cron/Shell/CommandRendererBackground.php
@@ -0,0 +1,47 @@
+filesystem->getDirectoryRead(DirectoryList::LOG)->getAbsolutePath();
+ // @phpcs:ignore Magento2.Functions.DiscouragedFunction.Discouraged
+ $logFile = escapeshellarg($logDir . 'magento.cron.' . $groupId . '.log');
+ }
+
+ return $this->osInfo->isWindows() ?
+ 'start /B "magento background task" ' . $command
+ : str_replace('2>&1', ">> $logFile 2>&1 &", $command);
+ }
+}
diff --git a/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php b/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php
index 83ebcddaf60b3..5d30cc88e1f62 100644
--- a/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php
+++ b/app/code/Magento/Cron/Test/Unit/Console/Command/CronCommandTest.php
@@ -1,8 +1,10 @@
objectManagerFactory, $this->deploymentConfigMock)
);
$commandTester->execute([]);
- $expectedMsg = 'Ran jobs by schedule.' . PHP_EOL;
+ $expectedMsg = '';
$this->assertEquals($expectedMsg, $commandTester->getDisplay());
}
}
diff --git a/app/code/Magento/Cron/Test/Unit/Shell/CommandRendererBackgroundTest.php b/app/code/Magento/Cron/Test/Unit/Shell/CommandRendererBackgroundTest.php
new file mode 100644
index 0000000000000..238843a6df8fa
--- /dev/null
+++ b/app/code/Magento/Cron/Test/Unit/Shell/CommandRendererBackgroundTest.php
@@ -0,0 +1,113 @@
+osInfo = $this->getMockBuilder(OsInfo::class)
+ ->getMock();
+
+ $directoryMock = $this->getMockBuilder(ReadInterface::class)
+ ->getMock();
+ $directoryMock->expects($this->any())
+ ->method('getAbsolutePath')
+ ->willReturn($this->logPath);
+
+ $this->filesystem = $this->getMockBuilder(Filesystem::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->filesystem->expects($this->any())
+ ->method('getDirectoryRead')
+ ->willReturn($directoryMock);
+ }
+
+ /**
+ * @covers ::render
+ * @dataProvider commandPerOsTypeDataProvider
+ *
+ * @param bool $isWindows
+ * @param string $expectedResults
+ * @param string[] $arguments
+ */
+ public function testRender($isWindows, $expectedResults, $arguments)
+ {
+ $this->osInfo->expects($this->once())
+ ->method('isWindows')
+ ->willReturn($isWindows);
+
+ $commandRenderer = new CommandRendererBackground($this->filesystem, $this->osInfo);
+ $this->assertEquals(
+ $expectedResults,
+ $commandRenderer->render($this->testCommand, $arguments)
+ );
+ }
+
+ /**
+ * Data provider for each os type
+ *
+ * @return array
+ */
+ public function commandPerOsTypeDataProvider()
+ {
+ return [
+ 'windows' => [
+ true,
+ 'start /B "magento background task" ' . $this->testCommand . ' 2>&1',
+ [],
+ ],
+ 'unix-without-group-name' => [
+ false,
+ $this->testCommand . ' >> /dev/null 2>&1 &',
+ [],
+ ],
+ 'unix-with-group-name' => [
+ false,
+ $this->testCommand . " >> '{$this->logPath}magento.cron.group-name.log' 2>&1 &",
+ ['php-executable', 'script-path', 'group-name'],
+ ],
+ ];
+ }
+}
diff --git a/app/code/Magento/Cron/etc/di.xml b/app/code/Magento/Cron/etc/di.xml
index e7286169359bd..bcb11691753d6 100644
--- a/app/code/Magento/Cron/etc/di.xml
+++ b/app/code/Magento/Cron/etc/di.xml
@@ -1,8 +1,8 @@
@@ -28,15 +28,14 @@
-
-
+
- Magento\Framework\Shell\CommandRendererBackground
+ Magento\Cron\Shell\CommandRendererBackground
- shellBackground
+ shellBackgroundCron
Magento\Cron\Model\VirtualLogger
@@ -65,7 +64,7 @@
-
-
- {magentoRoot}bin/magento cron:run | grep -v "Ran jobs by schedule" >> {magentoLog}magento.cron.log
+ - {magentoRoot}bin/magento cron:run >> {magentoLog}magento.cron.log 2>&1
- false
diff --git a/app/etc/di.xml b/app/etc/di.xml
index 385518fd79a20..11c174fffdfb8 100644
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -2014,4 +2014,11 @@
+
+
+
+
+ Magento\Framework\Shell\CommandRendererBackground
+
+