Created
November 8, 2014 23:29
-
-
Save fprochazka/fdba54944e285015f220 to your computer and use it in GitHub Desktop.
NewRelicProfilingListener
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace NewRelic; | |
use Kdyby; | |
use Nette; | |
use Tracy\Debugger; | |
/** | |
* @method setAppname($name, $license = NULL, $xmit = FALSE) | |
* @method noticeError($message, $exception = NULL) | |
* @method nameTransaction($name) | |
* @method endOfTransaction() | |
* @method endTransaction($ignore = FALSE) | |
* @method startTransaction($appname, $license = NULL) | |
* @method ignoreTransaction() | |
* @method ignoreApdex() | |
* @method backgroundJob($flag) | |
* @method captureParams($enable) | |
* @method addCustomParameter($key, $value) | |
* @method addCustomTracer($callback) | |
* @method getBrowserTimingHeader($flag = TRUE) | |
* @method getBrowserTimingFooter($flag = TRUE) | |
* @method disableAutorum() | |
* @method setUserAttributes($user, $account, $product) | |
*/ | |
class Client extends Nette\Object | |
{ | |
/** | |
* @param string $name | |
* @param string $value | |
*/ | |
public function customMetric($name, $value) | |
{ | |
$this->__call(__FUNCTION__, ['Custom/' . $name, $value]); | |
} | |
public function customTimeMetric($name, &$second, &$first) | |
{ | |
if (empty($second) || empty($first)) { | |
return; | |
} | |
$this->customMetric($name, round(abs($second - $first) * 1000, 0)); | |
} | |
public function __call($name, $args) | |
{ | |
$function = 'newrelic_' . self::underscore($name); | |
if (!extension_loaded('newrelic')) { | |
return FALSE; | |
} | |
if (!function_exists($function)) { | |
return parent::__call($name, $args); | |
} | |
return call_user_func_array($function, $args); | |
} | |
/** | |
* camelCaseAction name -> under_score | |
* @param string | |
* @return string | |
*/ | |
private static function underscore($s) | |
{ | |
$s = preg_replace('#(.)(?=[A-Z])#', '$1_', $s); | |
$s = strtolower($s); | |
$s = rawurlencode($s); | |
return $s; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace NewRelic; | |
use Kdyby; | |
use Nette; | |
use Nette\Application\Application; | |
use Nette\Application\Request; | |
use Tracy\Debugger; | |
use Nette\Utils\Strings; | |
/** | |
* Instead of misleading `www/index.php` requests, show presenter:action breakdown. | |
*/ | |
class NewRelicProfilingListener extends Nette\Object implements Kdyby\Events\Subscriber | |
{ | |
const APP_METRIC_PREFIX = 'Rohlik'; | |
/** | |
* @var \Nette\DI\Container | |
*/ | |
private $container; | |
/** | |
* @var Client | |
*/ | |
private $client; | |
public function __construct(Nette\DI\Container $container) | |
{ | |
$this->container = $container; | |
$this->client = new Client; | |
} | |
public function getSubscribedEvents() | |
{ | |
return array( | |
'Nette\\Application\\Application::onStartup', | |
'Nette\\Application\\Application::onShutdown', | |
'Nette\\Application\\Application::onRequest', | |
'Nette\\Application\\Application::onResponse', | |
); | |
} | |
public function onStartup(Application $app) | |
{ | |
$_ENV['APP_STARTUP_TIME_FLOAT'] = microtime(TRUE); | |
} | |
public function onRequest(Application $app, Request $request) | |
{ | |
if (!empty($request->parameters['exception']) && $request->parameters['exception'] instanceof \Exception) { | |
return; | |
} | |
$_ENV['APP_REQUEST_TIME_FLOAT'] = microtime(TRUE); | |
if (PHP_SAPI === 'cli') { | |
$this->client->setAppname('rohlik.cz/Cron'); | |
$this->client->nameTransaction('$ ' . basename($_SERVER['argv'][0]) . ' ' . implode(' ', array_slice($_SERVER['argv'], 1))); | |
$this->client->backgroundJob(TRUE); | |
} else { | |
$module = explode(':', trim($request->getPresenterName(), ':'))[0]; | |
$this->client->setAppname('rohlik.cz/' . ($module === 'Nette' ? 'Front' : $module)); | |
if ($module === 'Cron') { | |
$this->client->backgroundJob(TRUE); | |
} | |
$params = $request->getParameters(); | |
$this->client->nameTransaction($request->getPresenterName() . (isset($params['action']) ? ':' . $params['action'] : '') . (isset($params['do']) ? '?signal=' . preg_replace('~[0-9]+~', '*', $params['do']) : '')); | |
} | |
$this->client->customTimeMetric('Nette/RequestTime', $_ENV['APP_REQUEST_TIME_FLOAT'], $_ENV['APP_STARTUP_TIME_FLOAT']); | |
$this->client->customTimeMetric('Nette/CompilationTime', $_ENV['COMPILATION_TIME_FLOAT'], $_ENV['REQUEST_TIME_FLOAT']); | |
$this->client->customTimeMetric('Nette/StartupTime', $_ENV['APP_STARTUP_TIME_FLOAT'], $_ENV['COMPILATION_TIME_FLOAT']); | |
} | |
public function onResponse(Application $app, Nette\Application\IResponse $response) | |
{ | |
$_ENV['APP_RESPONSE_TIME_FLOAT'] = microtime(TRUE); | |
$this->client->customTimeMetric('Nette/ResponseTime', $_ENV['APP_RESPONSE_TIME_FLOAT'], $_ENV['APP_REQUEST_TIME_FLOAT']); | |
if (($presenter = $app->getPresenter()) && $presenter instanceof Nette\Application\UI\Presenter) { | |
$module = explode(':', trim($presenter->getName(), ':'))[0]; | |
$module = $module === 'Api' ? 'Api:V1' : $module; | |
// $this->client->customTimeMetric('Presenter/' . $module . '/Run', $_ENV['APP_PRESENTER_LEAVE'], $_ENV['APP_PRESENTER_ENTER']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/Shutdown', $_ENV['APP_PRESENTER_LEAVE'], $_ENV['APP_PRESENTER_SEND_RESPONSE']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/InitGlobals', $_ENV['APP_PRESENTER_REQUIREMENTS_BEGIN'], $_ENV['APP_PRESENTER_BEFORE_INIT']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/Startup', $_ENV['APP_PRESENTER_STARTUP_END'], $_ENV['APP_PRESENTER_REQUIREMENTS_BEGIN']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/Action', $_ENV['APP_PRESENTER_ACTION_END'], $_ENV['APP_PRESENTER_ACTION_BEGIN']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/Render', $_ENV['APP_PRESENTER_RENDER_END'], $_ENV['APP_PRESENTER_RENDER_BEGIN']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/BeforeRender', $_ENV['APP_PRESENTER_RENDER_BEGIN'], $_ENV['APP_PRESENTER_ACTION_END']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/ProcessSignal', $_ENV['APP_PRESENTER_SIGNAL_END'], $_ENV['APP_PRESENTER_SIGNAL_BEGIN']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/AfterRender', $_ENV['APP_PRESENTER_AFTER_RENDER_END'], $_ENV['APP_PRESENTER_RENDER_END']); | |
$this->client->customTimeMetric('Presenter/' . $module . '/SendTemplate', $_ENV['APP_PRESENTER_SEND_TEMPLATE_END'], $_ENV['APP_PRESENTER_SEND_TEMPLATE_BEGIN']); | |
} | |
$user = $this->container->getService('user'); | |
/** @var UserContext $user */ | |
if ($user->isLoggedIn()) { | |
$this->client->addCustomParameter('user', $user->getId()); | |
} | |
} | |
public function onShutdown(Application $app) | |
{ | |
$_ENV['APP_SHUTDOWN_TIME_FLOAT'] = microtime(TRUE); | |
$this->client->customTimeMetric('Nette/ResponseSendingTime', $_ENV['APP_SHUTDOWN_TIME_FLOAT'], $_ENV['APP_RESPONSE_TIME_FLOAT']); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace NewRelic; | |
use Kdyby; | |
use Nette; | |
use Nette\Application; | |
use Nette\ComponentModel\IContainer; | |
trait PresenterProfiler | |
{ | |
private $methodCalls = array( | |
'loadState' => 0, | |
'saveGlobalState' => 0, | |
); | |
/** | |
* $_ENV['APP_PRESENTER_LEAVE'] - $_ENV['APP_PRESENTER_ENTER'] = run() | |
* $_ENV['APP_PRESENTER_LEAVE'] - $_ENV['APP_PRESENTER_SEND_RESPONSE'] = shutdown() | |
*/ | |
public function run(Application\Request $request) | |
{ | |
$_ENV['APP_PRESENTER_ENTER'] = microtime(TRUE); | |
try { | |
return parent::run($request); | |
} finally { | |
$_ENV['APP_PRESENTER_LEAVE'] = microtime(TRUE); | |
} | |
} | |
/** | |
* At the end of setParent, the globals init is called | |
*/ | |
public function setParent(IContainer $parent = NULL, $name = NULL) | |
{ | |
parent::setParent($parent, $name); | |
$_ENV['APP_PRESENTER_BEFORE_INIT'] = microtime(TRUE); | |
return $this; | |
} | |
/** | |
* First load state is called right before checkRequirements() | |
* | |
* $_ENV['APP_PRESENTER_REQUIREMENTS_BEGIN'] - $_ENV['APP_PRESENTER_BEFORE_INIT'] = initGlobalParameters() | |
*/ | |
public function loadState(array $params) | |
{ | |
$this->methodCalls['loadState'] += 1; | |
parent::loadState($params); | |
if (count($this->methodCalls['loadState']) === 1) { | |
$_ENV['APP_PRESENTER_REQUIREMENTS_BEGIN'] = microtime(TRUE); | |
} | |
} | |
/** | |
* action is after startup | |
* | |
* $_ENV['APP_PRESENTER_STARTUP_END'] - $_ENV['APP_PRESENTER_REQUIREMENTS_BEGIN'] = checkRequirements() + startup() | |
* $_ENV['APP_PRESENTER_ACTION_END'] - $_ENV['APP_PRESENTER_ACTION_BEGIN'] = action<default>() | |
* $_ENV['APP_PRESENTER_RENDER_END'] - $_ENV['APP_PRESENTER_RENDER_BEGIN'] = render<default>() | |
* $_ENV['APP_PRESENTER_RENDER_BEGIN'] - $_ENV['APP_PRESENTER_ACTION_END'] = beforeRender() | |
*/ | |
protected function tryCall($method, array $params) | |
{ | |
if ($isAction = Nette\Utils\Strings::startsWith($method, 'action')) { | |
$_ENV['APP_PRESENTER_STARTUP_END'] = microtime(TRUE); | |
$_ENV['APP_PRESENTER_ACTION_BEGIN'] = microtime(TRUE); | |
} elseif ($isRender = Nette\Utils\Strings::startsWith($method, 'render')) { | |
$_ENV['APP_PRESENTER_RENDER_BEGIN'] = microtime(TRUE); | |
} | |
try { | |
return parent::tryCall($method, $params); | |
} finally { | |
if ($isAction) { | |
$_ENV['APP_PRESENTER_ACTION_END'] = microtime(TRUE); | |
} elseif (!empty($isRender)) { | |
$_ENV['APP_PRESENTER_RENDER_END'] = microtime(TRUE); | |
} | |
} | |
} | |
/** | |
* $_ENV['APP_PRESENTER_SIGNAL_END'] - $_ENV['APP_PRESENTER_SIGNAL_BEGIN'] = processSignal() | |
*/ | |
public function processSignal() | |
{ | |
$_ENV['APP_PRESENTER_SIGNAL_BEGIN'] = microtime(TRUE); | |
parent::processSignal(); | |
$_ENV['APP_PRESENTER_SIGNAL_END'] = microtime(TRUE); | |
} | |
/** | |
* $_ENV['APP_PRESENTER_AFTER_RENDER_END'] - $_ENV['APP_PRESENTER_RENDER_END'] = afterRender() | |
*/ | |
protected function saveGlobalState() | |
{ | |
$this->methodCalls['saveGlobalState'] += 1; | |
if (count($this->methodCalls['saveGlobalState']) === 1) { | |
$_ENV['APP_PRESENTER_AFTER_RENDER_END'] = microtime(TRUE); | |
} | |
parent::saveGlobalState(); | |
} | |
/** | |
* $_ENV['APP_PRESENTER_SEND_TEMPLATE_END'] - $_ENV['APP_PRESENTER_SEND_TEMPLATE_BEGIN'] = sendTemplate() | |
*/ | |
public function sendTemplate() | |
{ | |
$_ENV['APP_PRESENTER_SEND_TEMPLATE_BEGIN'] = microtime(TRUE); | |
try { | |
parent::sendTemplate(); | |
} finally { | |
$_ENV['APP_PRESENTER_SEND_TEMPLATE_END'] = microtime(TRUE); | |
} | |
} | |
public function sendResponse(Application\IResponse $response) | |
{ | |
$_ENV['APP_PRESENTER_SEND_RESPONSE'] = microtime(TRUE); | |
parent::sendResponse($response); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment