In one of our services we use a lot of API calls. At certain point I realised the need of speed-tracking for those API calls. On the dev environment it's pretty easy to do with @debug.stopwatch. But I wanted to measure the speed on production and save data to log file. To solve it I wrapped original stopwatch service with logger functionality. It's pretty much of configuration + small service class. I also have done it in the way when I can turn service on/off via configuration parameter
stopwatch_enable: true
or stopwatch_enable: false
As a result log file (the name could be
prod.api.watch.log
, you can specify any name you want inconfig_{env}.yml
) will contain entries like following[2014-11-26 18:56:53] api.DEBUG: API_getSomething, 1938 {"extraParamIfNeeded":"value"}
So, lets start with adding the parameter to environment we want to have stopwatch enabled:
app/config/parameters_prod.yml
.stopwatch_enable: true
Next is service by self. Simple class, dependent on:
- Symfony Stopwatch,
- Logger,
- turn on/off parameter.
/**
* Wrapper for basic Symfony's Stopwatch service
* It add logging functionality for basic one
*/
namespace CompanyName\CompanyBundle\Service;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Stopwatch\StopwatchEvent;
class StopwatchService
{
/**
* @var Stopwatch
*/
private $stopwatch;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var bool
*/
private $stopwatchEnabled;
/**
* @param Stopwatch $stopwatch
* @param LoggerInterface $logger
* @param bool $stopwatchEnabled
*/
public function __construct(
Stopwatch $stopwatch,
LoggerInterface $logger,
$stopwatchEnabled = false
) {
$this->stopwatch = $stopwatch;
$this->logger = $logger;
$this->stopwatchEnabled = $stopwatchEnabled;
}
/**
* @see Stopwatch::start
* @param string $eventName
* @return false|StopwatchEvent
*/
public function start($eventName)
{
return ($this->stopwatchEnabled) ? $this->stopwatch->start($eventName) : false;
}
/**
* @see Stopwatch::stop
* @param string $eventName
* @param array $extraParams
* @return false|StopwatchEvent
*/
public function stop($eventName, array $extraParams = array())
{
if (! $this->stopwatchEnabled) {
return false;
}
$event = $this->stopwatch->stop($eventName);
$this->logEvent($event, $eventName, $extraParams);
return $event;
}
/**
* Save event data to the log
*
* @param StopwatchEvent $event
* @param string $eventName
* @param array $params
*/
private function logEvent(StopwatchEvent $event, $eventName, array $params)
{
$this->logger->debug($eventName . ', ' . $event->getDuration(), $params);
}
}
Now we need to specify StopwatchService in service config
src/CompanyName/CompanyBundle/Resources/config/services.yml
symfony.stopwatch.service:
class: Symfony\Component\Stopwatch\Stopwatch
api.stopwatch:
class: CompanyName\CompanyBundle\Service\StopwatchService
arguments: [ @symfony.stopwatch.service, @logger, %stopwatch_enable% ]
tags:
- { name: monolog.logger, channel: api }
And configure new logger. You need to do this in
config_{env}.yml
file.main:
type: fingers_crossed
action_level: error
handler: nested
channels: [!api]
api:
type: stream
path: %kernel.logs_dir%/%kernel.environment%.api.watch.log
level: debug
channels: [api]
And finally call the service from the place where you want to measure execution time. Here I give the example of how to call stopwatch in controller. In real life you probably make an API calls from withing other services or models. In this case you need to inject stopwatch service to your model.
$stopwatch = $this->container->get('api.stopwatch');
$stopwatch->start('API_getSomething');
// ... API call or some other code you need to measure ...
$stopwatch->stop('API_getSomething', array('extraParamIfNeeded' => 'value'));