Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #74 from aheinz-sg/mongodb
Browse files Browse the repository at this point in the history
Bypass Doctrine in favor of Xhgui serialization
  • Loading branch information
jonaswouters committed Oct 1, 2015
2 parents 5891883 + 8836e2e commit 06722bc
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 70 deletions.
97 changes: 36 additions & 61 deletions DataCollector/XhprofCollector.php
Expand Up @@ -8,8 +8,12 @@
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\Bundle\DoctrineBundle\Registry as DoctrineRegistry;
use Doctrine\Common\Persistence\ManagerRegistry;
use Jns\Bundle\XhprofBundle\Document\XhguiRuns as XhguiRuns_Document;
use Jns\Bundle\XhprofBundle\Entity\XhguiRuns as XhguiRuns_Entity;
use Doctrine\ODM\MongoDB\DocumentManager;

/**
* XhprofDataCollector.
Expand All @@ -25,7 +29,7 @@ class XhprofCollector extends DataCollector
protected $collecting = false;
protected $collectingRequest;

public function __construct(ContainerInterface $container, $logger = null, DoctrineRegistry $doctrine = null)
public function __construct(ContainerInterface $container, $logger = null, ManagerRegistry $doctrine = null)
{
$this->container = $container;

Expand Down Expand Up @@ -110,36 +114,55 @@ public function stopProfiling($serverName, $uri)

$this->collecting = false;

$enableXhgui = $this->container->getParameter('jns_xhprof.enable_xhgui');

$xhprof_data = xhprof_disable();

if ($this->logger) {
$this->logger->debug('Disabled XHProf');
}

$xhprof_runs = new \XHProfRuns_Default();

$xhprof_runs = $this->createRun($serverName, $uri);
$source = null;

if ($enableXhgui) {
$this->runId = $this->saveProfilingDataToDB($xhprof_data, $uri, $serverName);
} else {
if ($xhprof_runs instanceof \XHProfRuns_Default) {
$source = $this->sanitizeUriForSource($uri);

$this->runId = $xhprof_runs->save_run($xhprof_data, $source);
}

$this->runId = $xhprof_runs->save_run($xhprof_data, $source);

$this->data = array(
'xhprof' => $this->runId,
'source' => $source,
'xhprof_url' => $this->container->getParameter('jns_xhprof.location_web'),
);

if ($xhprof_runs instanceof XhguiRuns_Document) {
$this->data['xhprof_url'] = $this->container->getParameter('jns_xhprof.location_web') . '/run/view?id=' . $this->data['xhprof'];
} else {
$this->data['xhprof_url'] = $this->container->getParameter('jns_xhprof.location_web') . '?run=' . $this->data['xhprof'] . '&source='.$this->data['source'];
}

return $this->data['xhprof'];
}

/**
* @return \iXHProfRuns
*/
protected function createRun($serverName, $uri) {
$enableXhgui = $this->container->getParameter('jns_xhprof.enable_xhgui');
if ($enableXhgui) {
$managerRegistry = $this->container->get($this->container->getParameter('jns_xhprof.manager_registry'));
$objectManager = $managerRegistry->getManager($this->container->getParameter('jns_xhprof.entity_manager'));
if ($objectManager instanceof DocumentManager) {
$xhprof_runs = new XhguiRuns_Document($objectManager);
} else {
$xhprof_runs = new XhguiRuns_Entity($serverName, $uri);
$xhprof_runs->setContainer($this->container);
}
} else {
$xhprof_runs = new \XHProfRuns_Default();
}
return $xhprof_runs;
}

/**
* Sanitize an uri to use it as source
*
Expand All @@ -153,54 +176,6 @@ private function sanitizeUriForSource($uri)
return preg_replace('/[^\w~\-_]+/', '-', $uri);
}

/**
* This function saves the profiling data as well as some additional data to a profiling database.
*
* @param array $xhprof_data
* @param string $uri
* @param string $serverName
* @throws \Exception if doctrine was not injected correctly
* @return string Returns the run id for the saved XHProf run
*/
private function saveProfilingDataToDB($xhprof_data, $uri, $serverName)
{
$pmu = isset($xhprof_data['main()']['pmu']) ? $xhprof_data['main()']['pmu'] : '';
$wt = isset($xhprof_data['main()']['wt']) ? $xhprof_data['main()']['wt'] : '';
$cpu = isset($xhprof_data['main()']['cpu']) ? $xhprof_data['main()']['cpu'] : '';

if (empty($this->doctrine)) {
throw new \Exception("Trying to save to database, but Doctrine was not set correctly");
}

$runId = uniqid();

$em = $this->doctrine->getManager($this->container->getParameter('jns_xhprof.entity_manager'));
$entityClass = $this->container->getParameter('jns_xhprof.entity_class');

$xhprofDetail = new $entityClass();
$xhprofDetail
->setId($runId)
->setUrl($uri)
->setCanonicalUrl($this->getCanonicalUrl($uri))
->setServerName($serverName)
->setPerfData(gzcompress(serialize(($xhprof_data))))
->setCookie(serialize($_COOKIE))
->setPost(serialize($_POST))
->setGet(serialize($_GET))
->setPmu($pmu)
->setWt($wt)
->setCpu($cpu)
->setServerId(getenv('SERVER_NAME'))
->setAggregateCallsInclude('')
->setTimestamp(new \DateTime())
;

$em->persist($xhprofDetail);
$em->flush();

return $runId;
}

/**
* Return the canonical URL for the passed in one.
*
Expand Down Expand Up @@ -255,7 +230,7 @@ public function getXhprof()
*/
public function getXhprofUrl()
{
return $this->data['xhprof_url'] . '?run=' . $this->data['xhprof'] . '&source='.$this->data['source'];
return $this->data['xhprof_url'];
}

/**
Expand Down
7 changes: 1 addition & 6 deletions DependencyInjection/Configuration.php
Expand Up @@ -26,14 +26,9 @@ public function getConfigTree()
$rootNode = $treeBuilder->root('jns_xhprof');

$rootNode
->validate()
->ifTrue(function($v) {
return $v['enable_xhgui'] && null === $v['entity_class'];
})
->thenInvalid('If you activate xhgui, you have to define an entity_class.')
->end()
->children()
->scalarNode('location_web')->defaultValue('http://xhprof')->end()
->scalarNode('manager_registry')->defaultValue('doctrine')->end()
->scalarNode('entity_manager')->defaultValue('default')->end()
->scalarNode('entity_class')->defaultValue(null)->end()
->scalarNode('enable_xhgui')->defaultFalse()->end()
Expand Down
86 changes: 86 additions & 0 deletions Document/XhguiRuns.php
@@ -0,0 +1,86 @@
<?php

namespace Jns\Bundle\XhprofBundle\Document;

use iXHProfRuns;
use Doctrine\ODM\MongoDB\DocumentManager;

class XhguiRuns implements iXHProfRuns
{
/**
* @var DocumentManager
*/
private $documentManager;

public function __construct(DocumentManager $documentManager) {
$this->documentManager = $documentManager;
}

/**
* {@inheritDoc}
*/
public function get_run($run_id, $type, &$run_desc) {
throw new \Exception('not implemented');
}

/**
* {@inheritDoc}
*/
public function save_run($xhprof_data, $type, $run_id = null) {
if (!class_exists('\Xhgui_Profiles') || !class_exists('\Xhgui_Saver_Mongo')) {
throw new \Exception('composer require perftools/xhgui dev-master');
}
$data = $this->prepareForSave($xhprof_data);
$dbname = $this->documentManager->getConfiguration()->getDefaultDB();
$mongo = $this->documentManager->getConnection()->getMongoClient()->selectDB($dbname);
$profiles = new \Xhgui_Profiles($mongo);
$saver = new \Xhgui_Saver_Mongo($profiles);
try {
$saver->save($data);
$run_id = (string)$data['_id'];
} catch (\Exception $e) {
error_log($e->getMessage());
}
return $run_id;
}

/**
* @see https://github.com/perftools/xhgui/blob/ad532c42e55cf8b3413b8d7a2241eea1140b537f/external/header.php#L88
* @todo instead of this copy pasta, refactor the perftools/xhgui side of things then reuse here
*/
private function prepareForSave($xhprof_data) {
$data = array('profile' => $xhprof_data);
$uri = array_key_exists('REQUEST_URI', $_SERVER)
? $_SERVER['REQUEST_URI']
: null;
if (empty($uri) && isset($_SERVER['argv'])) {
$cmd = basename($_SERVER['argv'][0]);
$uri = $cmd . ' ' . implode(' ', array_slice($_SERVER['argv'], 1));
}
$time = array_key_exists('REQUEST_TIME', $_SERVER)
? $_SERVER['REQUEST_TIME']
: time();
$requestTimeFloat = explode('.', $_SERVER['REQUEST_TIME_FLOAT']);
if (!isset($requestTimeFloat[1])) {
$requestTimeFloat[1] = 0;
}
// if (Xhgui_Config::read('save.handler') === 'file') {
// $requestTs = array('sec' => $time, 'usec' => 0);
// $requestTsMicro = array('sec' => $requestTimeFloat[0], 'usec' => $requestTimeFloat[1]);
// } else {
$requestTs = new \MongoDate($time);
$requestTsMicro = new \MongoDate($requestTimeFloat[0], $requestTimeFloat[1]);
// }
$data['meta'] = array(
'url' => $uri,
'SERVER' => $_SERVER,
'get' => $_GET,
'env' => $_ENV,
'simple_url' => \Xhgui_Util::simpleUrl($uri),
'request_ts' => $requestTs,
'request_ts_micro' => $requestTsMicro,
'request_date' => date('Y-m-d', $time),
);
return $data;
}
}
80 changes: 80 additions & 0 deletions Entity/XhguiRuns.php
@@ -0,0 +1,80 @@
<?php

namespace Jns\Bundle\XhprofBundle\Entity;

use iXHProfRuns;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class XhguiRuns implements iXHProfRuns, ContainerAwareInterface
{
/**
* @var ContainerInterface
*/
protected $container;

/**
* Sets the Container associated with this Controller.
*
* @param ContainerInterface $container A ContainerInterface instance
*/
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}

public function __construct($serverName, $uri) {
$this->serverName = $serverName;
$this->uri = $uri;
}

/**
* {@inheritDoc}
*/
public function get_run($run_id, $type, &$run_desc) {
throw new \Exception('not implemented');
}

/**
* {@inheritDoc}
*/
public function save_run($xhprof_data, $type, $run_id = null) {
$pmu = isset($xhprof_data['main()']['pmu']) ? $xhprof_data['main()']['pmu'] : '';
$wt = isset($xhprof_data['main()']['wt']) ? $xhprof_data['main()']['wt'] : '';
$cpu = isset($xhprof_data['main()']['cpu']) ? $xhprof_data['main()']['cpu'] : '';

$doctrine = $this->container->get('doctrine');
if (empty($doctrine)) {
throw new \Exception("Trying to save to database, but Doctrine was not set correctly");
}

$runId = uniqid();

$em = $doctrine->getManager($this->container->getParameter('jns_xhprof.entity_manager'));
$entityClass = $this->container->getParameter('jns_xhprof.entity_class');

$xhprofDetail = new $entityClass();
$xhprofDetail
->setId($runId)
->setUrl($this->uri)
->setCanonicalUrl($this->getCanonicalUrl($uri))
->setServerName($this->serverName)
->setPerfData(gzcompress(serialize(($xhprof_data))))
->setCookie(serialize($_COOKIE))
->setPost(serialize($_POST))
->setGet(serialize($_GET))
->setPmu($pmu)
->setWt($wt)
->setCpu($cpu)
->setServerId(getenv('SERVER_NAME'))
->setAggregateCallsInclude('')
->setTimestamp(new \DateTime())
;

$em->persist($xhprofDetail);
$em->flush();

return $runId;
}
}
?>
60 changes: 60 additions & 0 deletions Tests/DataCollector/XhprofCollectorTest.php
@@ -0,0 +1,60 @@
<?php

namespace Jns\Bundle\XhprofBundle\Tests\DataCollector;

use Jns\Bundle\XhprofBundle\DataCollector\XhprofCollector;

class XhprofCollectorTest extends \PHPUnit_Framework_TestCase
{
/**
* @var XhprofCollector
*/
private $collector;

/**
* @var \Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;

/**
* @var \Doctrine\Common\Persistence\ManagerRegistry
*/
private $doctrine;

public function setUp() {
parent::setUp();
$this->container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->disableOriginalConstructor()->getMock();
$this->doctrine = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
$this->collector = new XhprofCollectorTestable($this->container, null, $this->doctrine);
}

/**
* @dataProvider data_createRun
*/
public function test_createRun($enable_xhgui, $manager_class, $runs_class) {
$this->container->method('get')->with('manager_registry')->will($this->returnValue($this->doctrine));
$this->container->method('getParameter')->will($this->returnValueMap(array(
array('jns_xhprof.enable_xhgui', $enable_xhgui),
array('jns_xhprof.entity_manager', 'entity_manager'),
array('jns_xhprof.manager_registry', 'manager_registry'),
)));
$this->doctrine->method('getManager')->with('entity_manager')->will($this->returnValue(!$manager_class ?: $this->getMock($manager_class)));
$xhprof_runs = $this->collector->createRun('serverName', 'uri');
$this->assertInstanceOf($runs_class, $xhprof_runs);
}

public function data_createRun() {
return array(
array(false, null, '\XHProfRuns_Default'),
array(true, '\Doctrine\ODM\MongoDB\DocumentManager', '\Jns\Bundle\XhprofBundle\Document\XhguiRuns'),
array(true, '\Doctrine\ORM\EntityManager', '\Jns\Bundle\XhprofBundle\Entity\XhguiRuns'),
);
}
}

class XhprofCollectorTestable extends XhprofCollector
{
public function createRun($serverName, $uri) {
return parent::createRun($serverName, $uri);
}
}

0 comments on commit 06722bc

Please sign in to comment.