Skip to content

Commit

Permalink
Add Cron Jobs names to New Relic transactions
Browse files Browse the repository at this point in the history
  • Loading branch information
lbajsarowicz committed Dec 13, 2019
1 parent 17183cf commit 5819ded
Show file tree
Hide file tree
Showing 7 changed files with 400 additions and 22 deletions.
54 changes: 39 additions & 15 deletions app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

/**
* Handling cron jobs
*/

namespace Magento\Cron\Observer;

use Magento\Cron\Model\Schedule;
Expand Down Expand Up @@ -69,6 +71,11 @@ class ProcessCronQueueObserver implements ObserverInterface
*/
const LOCK_PREFIX = 'CRON_GROUP_';

/**
* Cron Job name pattern for Profiling
*/
const CRON_TIMERID = 'job %s';

/**
* @var \Magento\Cron\Model\ResourceModel\Schedule\Collection
*/
Expand Down Expand Up @@ -311,7 +318,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule,

$schedule->setExecutedAt(strftime('%Y-%m-%d %H:%M:%S', $this->dateTime->gmtTimestamp()))->save();

$this->startProfiling();
$this->startProfiling($jobCode);
try {
$this->logger->info(sprintf('Cron Job %s is run', $jobCode));
//phpcs:ignore Magento2.Functions.DiscouragedFunction
Expand All @@ -323,7 +330,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule,
'Cron Job %s has an error: %s. Statistics: %s',
$jobCode,
$e->getMessage(),
$this->getProfilingStat()
$this->getProfilingStat($jobCode)
)
);
if (!$e instanceof \Exception) {
Expand All @@ -335,7 +342,7 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule,
}
throw $e;
} finally {
$this->stopProfiling();
$this->stopProfiling($jobCode);
}

$schedule->setStatus(
Expand All @@ -351,40 +358,55 @@ protected function _runJob($scheduledTime, $currentTime, $jobConfig, $schedule,
sprintf(
'Cron Job %s is successfully finished. Statistics: %s',
$jobCode,
$this->getProfilingStat()
$this->getProfilingStat($jobCode)
)
);
}

/**
* Starts profiling
*
* @param string $jobName
* @return void
*/
private function startProfiling()
private function startProfiling(string $jobName = '')
{
$this->statProfiler->clear();
$this->statProfiler->start('job', microtime(true), memory_get_usage(true), memory_get_usage());
$this->statProfiler->start(
sprintf(self::CRON_TIMERID, $jobName),
microtime(true),
memory_get_usage(true),
memory_get_usage()
);
}

/**
* Stops profiling
*
* @param string $jobName
* @return void
*/
private function stopProfiling()
private function stopProfiling(string $jobName = '')
{
$this->statProfiler->stop('job', microtime(true), memory_get_usage(true), memory_get_usage());
$this->statProfiler->stop(
sprintf(self::CRON_TIMERID, $jobName),
microtime(true),
memory_get_usage(true),
memory_get_usage()
);
}

/**
* Retrieves statistics in the JSON format
*
* @param string $jobName
* @return string
*/
private function getProfilingStat()
private function getProfilingStat(string $jobName): string
{
$stat = $this->statProfiler->get('job');
$stat = $this->statProfiler->get(
sprintf(self::CRON_TIMERID, $jobName)
);
unset($stat[Stat::START]);
return json_encode($stat);
}
Expand Down Expand Up @@ -418,7 +440,9 @@ private function getNonExitedSchedules($groupId)
'status',
[
'in' => [
Schedule::STATUS_PENDING, Schedule::STATUS_RUNNING, Schedule::STATUS_SUCCESS
Schedule::STATUS_PENDING,
Schedule::STATUS_RUNNING,
Schedule::STATUS_SUCCESS
]
]
);
Expand Down Expand Up @@ -478,10 +502,10 @@ private function generateSchedules($groupId)
/**
* Generate jobs for config information
*
* @param array $jobs
* @param array $exists
* @param string $groupId
* @return void
* @param array $jobs
* @param array $exists
* @param string $groupId
* @return void
*/
protected function _generateJobs($jobs, $exists, $groupId)
{
Expand Down
19 changes: 17 additions & 2 deletions app/code/Magento/NewRelicReporting/Model/NewRelicWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*/
namespace Magento\NewRelicReporting\Model;

use Exception;

/**
* Wrapper for New Relic functions
*
Expand All @@ -31,10 +33,10 @@ public function addCustomParameter($param, $value)
/**
* Wrapper for 'newrelic_notice_error' function
*
* @param \Exception $exception
* @param Exception $exception
* @return void
*/
public function reportError($exception)
public function reportError(Exception $exception)
{
if ($this->isExtensionInstalled()) {
newrelic_notice_error($exception->getMessage(), $exception);
Expand Down Expand Up @@ -67,6 +69,19 @@ public function setTransactionName(string $transactionName): void
}
}

/**
* Wrapper for 'newrelic_end_transaction'
*
* @param bool $ignore
* @return void
*/
public function endTransaction($ignore = false)
{
if ($this->isExtensionInstalled()) {
newrelic_end_transaction($ignore);
}
}

/**
* Checks whether newrelic-php5 agent is installed
*
Expand Down
30 changes: 26 additions & 4 deletions app/code/Magento/NewRelicReporting/Plugin/CommandPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,24 @@ class CommandPlugin
*/
private $newRelicWrapper;

/**
* @var string[]
*/
private $skipCommands;

/**
* @param Config $config
* @param NewRelicWrapper $newRelicWrapper
* @param array $skipCommands
*/
public function __construct(
Config $config,
NewRelicWrapper $newRelicWrapper
NewRelicWrapper $newRelicWrapper,
array $skipCommands = []
) {
$this->config = $config;
$this->newRelicWrapper = $newRelicWrapper;
$this->skipCommands = $skipCommands;
}

/**
Expand All @@ -46,10 +54,24 @@ public function __construct(
*/
public function beforeRun(Command $command, ...$args)
{
$this->newRelicWrapper->setTransactionName(
sprintf('CLI %s', $command->getName())
);
if (!$this->isCommandSkipped($command)) {
$this->newRelicWrapper->setTransactionName(
sprintf('CLI %s', $command->getName())
);
}

return $args;
}

/**
* Determines whether the Command is declared to be skipped
*
* @param Command $command
* @return bool
*/
private function isCommandSkipped(Command $command): bool
{
$commandName = $command->getName();
return isset($this->skipCommands[$commandName]) && $this->skipCommands[$commandName] === true;
}
}
105 changes: 105 additions & 0 deletions app/code/Magento/NewRelicReporting/Plugin/StatPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\NewRelicReporting\Plugin;

use Magento\Framework\Profiler\Driver\Standard\Stat;
use Magento\NewRelicReporting\Model\Config;
use Magento\NewRelicReporting\Model\NewRelicWrapper;
use Psr\Log\LoggerInterface;

/**
* Class StatPlugin handles single Cron Jobs transaction names
*/
class StatPlugin
{
public const TIMER_NAME_CRON_PREFIX = 'job';

/**
* @var Config
*/
private $config;

/**
* @var NewRelicWrapper
*/
private $newRelicWrapper;

/**
* @var LoggerInterface
*/
private $logger;

/**
* @param Config $config
* @param NewRelicWrapper $newRelicWrapper
* @param LoggerInterface $logger
*/
public function __construct(
Config $config,
NewRelicWrapper $newRelicWrapper,
LoggerInterface $logger
) {
$this->config = $config;
$this->newRelicWrapper = $newRelicWrapper;
$this->logger = $logger;
}

/**
* Before running original profiler, register NewRelic transaction
*
* @param Stat $schedule
* @param array $args
* @return array
* @see \Magento\Cron\Observer\ProcessCronQueueObserver::startProfiling
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function beforeStart(Stat $schedule, ...$args): array
{
$timerName = current($args);

if ($this->isCronJob($timerName)) {
$this->newRelicWrapper->setTransactionName(
sprintf('Cron %s', $timerName)
);
}

return $args;
}

/**
* Before stopping original profiler, close NewRelic transaction
*
* @param Stat $schedule
* @param array $args
* @return array
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function beforeStop(Stat $schedule, ...$args): array
{
$timerName = current($args);

if ($this->isCronJob($timerName)) {
$this->newRelicWrapper->endTransaction();
}

return $args;
}

/**
* Determines whether provided name is Cron Job
*
* @param string $timerName
* @return bool
*/
private function isCronJob(string $timerName): bool
{
return 0 === strpos($timerName, static::TIMER_NAME_CRON_PREFIX);
}
}
Loading

0 comments on commit 5819ded

Please sign in to comment.