Skip to content

Commit

Permalink
Added possibility to save profiles in the files
Browse files Browse the repository at this point in the history
  • Loading branch information
shagtv committed Feb 18, 2019
1 parent 081dfdd commit 1825d78
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 40 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Live Profiler ChangeLog

## master

There are next changes:

## 1.1.0

There are next changes:

- added possibility to save profiles in the files
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends git unzip \
&& curl --silent --show-error https://getcomposer.org/installer | php \
&& ./composer.phar -q install \
&& ./composer.phar -q update \
&& echo 'date.timezone = Europe/London' >> /usr/local/etc/php/php.ini \
&& pecl install xhprof-0.9.4 && docker-php-ext-enable xhprof # xhprof profiler installation

CMD ["php", "/app/bin/example.php"]
CMD ["php", "/app/bin/example_save_to_file.php"]
2 changes: 1 addition & 1 deletion DockerfileHHVM
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ COPY . /app
WORKDIR /app

RUN curl -sS https://getcomposer.org/installer | hhvm --php \
&& hhvm ./composer.phar -q install
&& hhvm ./composer.phar -q update

CMD ["hhvm", "/app/bin/example.php"]
2 changes: 1 addition & 1 deletion DockerfileTidyWays
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends git unzip \
&& curl --silent --show-error https://getcomposer.org/installer | php \
&& ./composer.phar -q install
&& ./composer.phar -q update

# tideways profiler installation
RUN git clone https://github.com/tideways/php-profiler-extension.git /tmp/xhptof
Expand Down
2 changes: 1 addition & 1 deletion DockerfileUprofiler
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ WORKDIR /app

RUN apt-get update && apt-get install -y --no-install-recommends git unzip \
&& curl --silent --show-error https://getcomposer.org/installer | php \
&& ./composer.phar -q install \
&& ./composer.phar -q update \
&& echo 'date.timezone = Europe/London' >> /usr/local/etc/php/php.ini

# uprofiler installation
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ php composer.phar require badoo/liveprof
LIVE_PROFILER_CONNECTION_URL=mysql://db_user:db_password@db_mysql:3306/Profiler?charset=utf8 php vendor/badoo/liveprof/bin/install.php
```

* It's also possible to save profiling result into files. To do it prepare a directory with write permissions.
* Init a profiler before working code in the project entry point (usually public/index.php).

You should add a profiler call before your code to start profiling with default parameters:
Expand All @@ -69,7 +70,9 @@ There is a full list of methods you can use to change options:

// Start profiling
\Badoo\LiveProfiler\LiveProfiler::getInstance()
->setMode(\Badoo\LiveProfiler\LiveProfiler::MODE_DB) // optional, MODE_DB - save profiles to db, MODE_FILES - save profiles to files
->setConnectionString('mysql://db_user:db_password@db_mysql:3306/Profiler?charset=utf8') // optional, you can also set the connection url in the environment variable LIVE_PROFILER_CONNECTION_URL
->setPath('/app/data/') // optional, path to save profiles, you can also set the file path in the environment variable LIVE_PROFILER_PATH
->setApp('Site1') // optional, current app name to use one profiler in several apps, "Default" by default
->setLabel('users') // optional, the request name, by default the url path or script name in cli
->setDivider(700) // optional, profiling starts for 1 of 700 requests with the same app and label, 1000 by default
Expand Down Expand Up @@ -104,6 +107,7 @@ Environment Variables
=====================

`LIVE_PROFILER_CONNECTION_URL`: [url](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.8/reference/configuration.html#configuration) for the database connection
`LIVE_PROFILER_PATH`: path to save profiles is \Badoo\LiveProfiler\LiveProfiler::MODE_FILES mode

Work flow
=========
Expand Down
2 changes: 1 addition & 1 deletion bin/example.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
$profiling_result = $Profiler->end();
print_r($Profiler->getLastProfileData());

echo $profiling_result ? "Prof;ling successfully finished\n" : "Error in profiling\n";
echo $profiling_result ? "Profiling successfully finished\n" : "Error in profiling\n";

/**
* Test functions
Expand Down
74 changes: 74 additions & 0 deletions bin/example_save_to_file.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php
/**
* Runs profiling test code
* @maintainer Timur Shagiakhmetov <[email protected]>
*/

if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
} elseif (file_exists(__DIR__ . '/../../../../vendor/autoload.php')) {
require_once __DIR__ . '/../../../../vendor/autoload.php';
}

use Badoo\LiveProfiler\LiveProfiler;

// Initialize profiler with file mode
$path = '/tmp';
$Profiler = new LiveProfiler($path, LiveProfiler::MODE_FILES);
$Profiler->setDivider(1);

// Run this method before profiled code
$Profiler->start();

// Start of profiled code
testCode(1);
// End of profiled code

// Run this method after profiled code
$profiling_result = $Profiler->end();

echo $profiling_result ? "Profiling successfully finished\n" : "Error in profiling\n";

echo "Profiling data from the file:\n";
$filename = sprintf('%s/%s/%s/%s.json', $path, $Profiler->getApp(), base64_encode($Profiler->getLabel()), strtotime($Profiler->getDateTime()));
echo file_get_contents($filename) . "\n";

/**
* Test functions
* @param int $level
*/
function testCode($level = 2)
{
getSlower($level);
getFaster($level);
}

/**
* @param int $level
* @return float
*/
function getSlower($level)
{
$result = 0;
for ($i = 0; $i < $level; $i++) {
for ($j = 0; $j < 1000000; $j++) {
$result = $j * (1000000 - $j);
}
}
return $result;
}

/**
* @param int $level
* @return float
*/
function getFaster($level)
{
$result = 0;
for ($i = 0; $i < (10 - $level); $i++) {
for ($j = 0; $j < 1000000; $j++) {
$result = $i * (1000000 - $i);
}
}
return $result;
}
143 changes: 114 additions & 29 deletions src/Badoo/LiveProfiler/LiveProfiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@

class LiveProfiler
{
CONST MODE_DB = 'db';
CONST MODE_FILES = 'files';

/** @var LiveProfiler */
protected static $instance;
/** @var string */
protected $mode = self::MODE_DB;
/** @var string */
protected $path = '';
/** @var Connection */
protected $Conn;
/** @var LoggerInterface */
Expand Down Expand Up @@ -43,30 +50,32 @@ class LiveProfiler

/**
* LiveProfiler constructor.
* @param string $connection_string
* @param string $connection_string_or_path
* @param string $mode
*/
public function __construct($connection_string = '')
public function __construct($connection_string_or_path = '', $mode = self::MODE_DB)
{
if ($connection_string) {
$this->connection_string = $connection_string;
} else {
$this->connection_string = getenv('LIVE_PROFILER_CONNECTION_URL');
}
$this->mode = $mode;

$this->app = 'Default';
$this->label = $this->getAutoLabel();
$this->datetime = date('Y-m-d H:i:s');


$this->detectProfiler();
$this->Logger = new Logger();
$this->DataPacker = new DataPacker();

if ($mode === self::MODE_DB) {
$this->connection_string = $connection_string_or_path ?: getenv('LIVE_PROFILER_CONNECTION_URL');
} else {
$this->setPath($connection_string_or_path ?: getenv('LIVE_PROFILER_PATH'));
}
}

public static function getInstance($connection_string = '')
public static function getInstance($connection_string = '', $mode = self::MODE_DB)
{
if (self::$instance === null) {
self::$instance = new static($connection_string);
self::$instance = new static($connection_string, $mode);
}

return self::$instance;
Expand Down Expand Up @@ -119,14 +128,7 @@ public function end()
}

$this->last_profile_data = $data;
$packed_data = $this->DataPacker->pack($data);

$result = false;
try {
$result = $this->save($this->app, $this->label, $this->datetime, $packed_data);
} catch (DBALException $Ex) {
$this->Logger->error('Error in insertion profile data: ' . $Ex->getMessage());
}
$result = $this->save($this->app, $this->label, $this->datetime, $data);

if (!$result) {
$this->Logger->warning('Can\'t insert profile data');
Expand Down Expand Up @@ -219,6 +221,46 @@ public function reset()
return true;
}

/**
* @param string $mode
* @return $this
*/
public function setMode($mode)
{
$this->mode = $mode;
return $this;
}

/**
* @return string
*/
public function getMode()
{
return $this->mode;
}

/**
* @param string $path
* @return $this
*/
public function setPath($path)
{
if (!is_dir($path)) {
$this->Logger->error('Directory ' . $path . ' does not exists');
}

$this->path = $path;
return $this;
}

/**
* @return string
*/
public function getPath()
{
return $this->path;
}

/**
* @param string $app
* @return $this
Expand Down Expand Up @@ -380,21 +422,64 @@ public function setConnectionString($connection_string)
* @param string $app
* @param string $label
* @param string $datetime
* @param string $data
* @param array $data
* @return bool
* @throws DBALException
*/
protected function save($app, $label, $datetime, $data)
{
return (bool)$this->getConnection()->insert(
'details',
[
'app' => $app,
'label' => $label,
'perfdata' => $data,
'timestamp' => $datetime
]
);
if ($this->mode === self::MODE_DB) {
return $this->saveToDB($app, $label, $datetime, $data);
}

return $this->saveToFile($app, $label, $datetime, $data);
}

/**
* @param string $app
* @param string $label
* @param string $datetime
* @param array $data
* @return bool
*/
protected function saveToDB($app, $label, $datetime, $data)
{
$packed_data = $this->DataPacker->pack($data);

try {
return (bool)$this->getConnection()->insert(
'details',
[
'app' => $app,
'label' => $label,
'perfdata' => $packed_data,
'timestamp' => $datetime
]
);
} catch (DBALException $Ex) {
$this->Logger->error('Error in insertion profile data: ' . $Ex->getMessage());
return false;
}
}

/**
* @param string $app
* @param string $label
* @param string $datetime
* @param array $data
* @return bool
*/
private function saveToFile($app, $label, $datetime, $data)
{
$path = sprintf('%s/%s/%s', $this->path, $app, base64_encode($label));

if (!is_dir($path) && !mkdir($path, 0755, true) && !is_dir($path)) {
$this->Logger->error('Directory "'. $path .'" was not created');
return false;
}

$filename = sprintf('%s/%s.json', $path, strtotime($datetime));
$packed_data = $this->DataPacker->pack($data);
return (bool)file_put_contents($filename, $packed_data);
}

/**
Expand Down
Loading

0 comments on commit 1825d78

Please sign in to comment.