Skip to content

Commit

Permalink
Create compatibility with XHGui
Browse files Browse the repository at this point in the history
  • Loading branch information
Robin Mulder committed Nov 8, 2024
1 parent c8081fb commit a3ae3a3
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 98 deletions.
55 changes: 55 additions & 0 deletions Model/Profiler/Driver/Buggregator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace JustBetter\XhprofProfiler\Model\Profiler\Driver;

use JustBetter\XhprofProfiler\Api\DriverInterface;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Exception\RuntimeException;
use SpiralPackages\Profiler\Driver\XhprofDriver;
use SpiralPackages\Profiler\Storage\WebStorage;
use Symfony\Component\HttpClient\NativeHttpClient;


class Buggregator implements DriverInterface

Check failure on line 14 in Model/Profiler/Driver/Buggregator.php

View workflow job for this annotation

GitHub Actions / analyse

Class JustBetter\XhprofProfiler\Model\Profiler\Driver\Buggregator implements unknown interface JustBetter\XhprofProfiler\Api\DriverInterface.
{
public const IGNORED_FUNCTIONS_KEY = 'ignored_functions';

public function __construct(
protected XhprofDriver $driver,
protected DeploymentConfig $deploymentConfig,
)
{}

public function start(): void
{
$this->driver->start(
[
self::IGNORED_FUNCTIONS_KEY => ['SpiralPackages\Profiler\Profiler::end'],
]
);
}

/**
* Terminates the current profiling session and stores the results.
* @param array $tags Optional tags to attach to the profiling data.
* @return void
* @throws FileSystemException
* @throws RuntimeException
*/
public function end(array $tags = []): void
{
$result = $this->driver->end();
$endpoint = $this->deploymentConfig->get('xhprofprofiler/endpoint');
$appName = $this->deploymentConfig->get('xhprofprofiler/app_name');
if (!empty($appName) && !empty($endpoint) && is_string($endpoint) && is_string($appName)) {
$storage = new WebStorage(new NativeHttpClient(), $endpoint);
$storage->store(
$appName,
$tags,
new \DateTimeImmutable(),
$result
);
}
}
}
35 changes: 35 additions & 0 deletions Model/Profiler/Driver/XHGui.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace JustBetter\XhprofProfiler\Model\Profiler\Driver;

use Magento\Framework\App\DeploymentConfig;
use Xhgui\Profiler\Profiler;
use Xhgui\Profiler\ProfilingFlags;

class XHGui implements \JustBetter\XhprofProfiler\Api\DriverInterface

Check failure on line 9 in Model/Profiler/Driver/XHGui.php

View workflow job for this annotation

GitHub Actions / analyse

Class JustBetter\XhprofProfiler\Model\Profiler\Driver\XHGui implements unknown interface JustBetter\XhprofProfiler\Api\DriverInterface.
{
public Profiler $driver;

public function __construct(
protected DeploymentConfig $deploymentConfig,
array $config = []
)
{
$this->initializeDriver($config);
}

private function initializeDriver(array $config): void
{
$this->driver = new Profiler($config);
}

public function start(): void
{
$this->driver->enable();
}

public function end(array $tags = []): void
{
$this->driver->save($this->driver->disable());
}
}
41 changes: 5 additions & 36 deletions Model/Profiler/XhprofProfiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,25 @@

namespace JustBetter\XhprofProfiler\Model\Profiler;

use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Exception\RuntimeException;
use SpiralPackages\Profiler\Driver\XhprofDriver;
use SpiralPackages\Profiler\Storage\WebStorage;
use Symfony\Component\HttpClient\NativeHttpClient;
use JustBetter\XhprofProfiler\Api\DriverInterface;

class XhprofProfiler
{
public const HEADER = 'X-Xhprof-Enabled';

public const IGNORED_FUNCTIONS_KEY = 'ignored_functions';

public array $tags;

public function __construct(
protected XhprofDriver $driver,
protected DeploymentConfig $deploymentConfig,
array $tags = []
protected DriverInterface $driver,

Check failure on line 10 in Model/Profiler/XhprofProfiler.php

View workflow job for this annotation

GitHub Actions / analyse

Parameter $driver of method JustBetter\XhprofProfiler\Model\Profiler\XhprofProfiler::__construct() has invalid type JustBetter\XhprofProfiler\Api\DriverInterface.

Check failure on line 10 in Model/Profiler/XhprofProfiler.php

View workflow job for this annotation

GitHub Actions / analyse

Property JustBetter\XhprofProfiler\Model\Profiler\XhprofProfiler::$driver has unknown class JustBetter\XhprofProfiler\Api\DriverInterface as its type.
protected array $tags = []
)
{
$this->tags = $tags;
}

public function handle(): void
{
$this->driver->start(
[
self::IGNORED_FUNCTIONS_KEY => ['SpiralPackages\Profiler\Profiler::end'],
]
);
$this->driver->start();

Check failure on line 19 in Model/Profiler/XhprofProfiler.php

View workflow job for this annotation

GitHub Actions / analyse

Call to method start() on an unknown class JustBetter\XhprofProfiler\Api\DriverInterface.
}

/**
* @throws FileSystemException
* @throws RuntimeException
*/
public function terminate(array $tags = []): void
{
$result = $this->driver->end();
$endpoint = $this->deploymentConfig->get('xhprofprofiler/endpoint');
$appName = $this->deploymentConfig->get('xhprofprofiler/app_name');
if (!empty($appName) && !empty($endpoint) && is_string($endpoint) && is_string($appName)) {
$storage = new WebStorage(new NativeHttpClient(), $endpoint);
$storage->store(
$appName,
\array_merge($this->tags ?? [], $tags),
new \DateTimeImmutable(),
$result
);
}
$this->driver->end(array_merge($this->tags ?? [], $tags));

Check failure on line 24 in Model/Profiler/XhprofProfiler.php

View workflow job for this annotation

GitHub Actions / analyse

Call to method end() on an unknown class JustBetter\XhprofProfiler\Api\DriverInterface.
}
}
18 changes: 11 additions & 7 deletions Plugin/AppInterfacePlugin.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

namespace JustBetter\XhprofProfiler\Plugin;

use JustBetter\XhprofProfiler\Model\Profiler\XhprofProfiler;
Expand All @@ -9,17 +10,20 @@

class AppInterfacePlugin
{
public const HEADER = 'X-Xhprof-Enabled';

public function __construct(
protected XhprofProfiler $profiler,
protected Http $request
protected Http $request
)
{
}

public function aroundLaunch(
Application $subject,
callable $proceed,
) : ResponseInterface {
callable $proceed,
): ResponseInterface
{

if (!$this->isEnabled()) {
return $proceed();
Expand All @@ -34,12 +38,12 @@ public function aroundLaunch(
return $response;
}

public function isEnabled(): bool
private function isEnabled(): bool
{
if ($this->request->getHeader(XhprofProfiler::HEADER)) {
return filter_var($this->request->getHeader(XhprofProfiler::HEADER), FILTER_VALIDATE_BOOLEAN);
if ($this->request->getHeader(self::HEADER)) {
return filter_var($this->request->getHeader(self::HEADER), FILTER_VALIDATE_BOOLEAN);
}

return isset($_ENV['XHPROF_ENABLED']) ? (bool) $_ENV['XHPROF_ENABLED'] : false;
return isset($_ENV['XHPROF_ENABLED']) ? (bool)$_ENV['XHPROF_ENABLED'] : false;
}
}
101 changes: 47 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This module integrates Xhprof profiling capabilities into your Magento 2 applica

- Magento 2.4.7 or higher
- Xhprof PHP extension
- Compatible with XHGui
- Compatible with Buggregator

## Installation
Expand All @@ -31,7 +32,16 @@ This module integrates Xhprof profiling capabilities into your Magento 2 applica
bin/magento setup:di:compile
```

## Configuration
## Configuration for Buggregator

By default this module uses XHGui for processing the profiling data. We can use the Buggregator driver by overriding the default driver via xml:
```
<type name="JustBetter\XhprofProfiler\Model\Profiler\XhprofProfiler">
<arguments>
<argument name="driver" xsi:type="object">JustBetter\XhprofProfiler\Model\Profiler\Driver\Buggregator</argument>
</arguments>
</type>
```

Configure the module by adding the following configuration to your `app/etc/env.php` file:

Expand All @@ -48,30 +58,36 @@ return [
- **app_name**: The name of your application.
- **endpoint**: The endpoint where the profiling data will be stored.

## Usage
## Compatibility

The profiling is automatically enabled for all requests. The module uses the `AppInterfacePlugin` to start and terminate the profiler around each request.
This module is compatible with [XHGui](https://github.com/perftools/xhgui) and [Buggregator](https://buggregator.dev/). These are graphical interfaces for viewing XHProf profiling data.

### Key Classes and Methods
### To integrate with XHGui:

- **`XhprofProfiler`**
- **Constants:**
- `HEADER`: The header key used to enable Xhprof profiling.
- `IGNORED_FUNCTIONS_KEY`: The key for ignored functions in the profiler.
- **Methods:**
- `__construct()`: Initializes the profiler with the given driver, deployment configuration, and optional tags.
- `handle()`: Starts the profiler.
- `terminate()`: Ends the profiler and stores the profiling data.
1. Follow the installation guide of XHGui at [XHGui](https://github.com/perftools/xhgui)
2. By default we use the default xhprof profiler and the results are getting uploaded to XHGui.
3. Update default configuration via di.xml, check [config.default.php](https://github.com/perftools/xhgui/blob/0.23.x/config/config.default.php) for the possible options.

- **`AppInterfacePlugin`**
- **Methods:**
- `aroundLaunch()`: Wraps around the application launch to start and stop the profiler.

## Buggregator Compatibility
#### Default XHGui configuration
You can pass custom configuration to the XHGui driver via the arguments like the default configuration.
```
<type name="JustBetter\XhprofProfiler\Model\Profiler\Driver\XHGui">
<arguments>
<argument name="config" xsi:type="array">
<item name="profiler" xsi:type="string">xhprof</item>
<item name="save.handler" xsi:type="string">upload</item>
<item name="save.handler.upload" xsi:type="array">
<item name="url" xsi:type="string">http://xhgui.xhgui.orb.local/run/import</item>
<item name="timeout" xsi:type="number">3</item>
<item name="token" xsi:type="string">token</item>
</item>
</argument>
</arguments>
</type>
This module is compatible with [Buggregator](https://buggregator.dev/), a debug and profiler tool for PHP applications. Buggregator can collect and visualize profiling data generated by this module, providing a comprehensive debugging and performance analysis experience.
```

To integrate with Buggregator:
### To integrate with Buggregator:

1. Ensure Buggregator is installed and configured in your environment.
2. Configure the endpoint in `app/etc/env.php` to point to Buggregator's profiling data endpoint.
Expand All @@ -88,45 +104,22 @@ return [
];
```

## Example

Here is an example of how the profiler is used in the plugin:

```php
namespace JustBetter\XhprofProfiler\Plugin;

use JustBetter\XhprofProfiler\Model\Profiler\XhprofProfiler;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\AppInterface as Application;

class AppInterfacePlugin
{
public function __construct(
protected XhprofProfiler $profiler
)
{
}

public function aroundLaunch(
Application $subject,
callable $proceed,
) : ResponseInterface {
$this->profiler->handle();
$response = $proceed();
$this->profiler->terminate();
return $response;
}
}
```
## Usage

## Exception Handling
The profiling is automatically enabled for all requests. The module uses the `AppInterfacePlugin` to start and terminate the profiler around each request.

The `terminate` method of the `XhprofProfiler` class can throw the following exceptions:
- **FileSystemException**: If there is an issue with the file system.
- **RuntimeException**: If there is a runtime issue.
### Key Classes and Methods

Ensure you have proper exception handling in place when integrating this module.
- **`XhprofProfiler`**
- **Methods:**
- `__construct()`: Initializes the profiler with the given driver and optional tags.
- `handle()`: Starts the profiler.
- `terminate()`: Ends the profiler and stores the profiling data.

- **`AppInterfacePlugin`**
- **Methods:**
- `aroundLaunch()`: Wraps around the application launch to start and stop the profiler.

## License

This project is licensed under the MIT License - see the LICENSE file for details.
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"php": ">=8.2",
"magento/framework": "*",
"magento/module-config": "^101.2",
"spiral-packages/profiler": "^1.2"
"spiral-packages/profiler": "^1.2",
"perftools/php-profiler": "^1.1"
},
"repositories": {
"magento": {
Expand Down
18 changes: 18 additions & 0 deletions etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,22 @@
<type name="Magento\Framework\AppInterface">
<plugin name="JustBetter_XhprofProfiler::profile" type="JustBetter\XhprofProfiler\Plugin\AppInterfacePlugin" sortOrder="1" />
</type>
<type name="JustBetter\XhprofProfiler\Model\Profiler\XhprofProfiler">
<arguments>
<argument name="driver" xsi:type="object">JustBetter\XhprofProfiler\Model\Profiler\Driver\XHGui</argument>
</arguments>
</type>
<type name="JustBetter\XhprofProfiler\Model\Profiler\Driver\XHGui">
<arguments>
<argument name="config" xsi:type="array">
<item name="profiler" xsi:type="string">xhprof</item>
<item name="save.handler" xsi:type="string">upload</item>
<item name="save.handler.upload" xsi:type="array">
<item name="url" xsi:type="string">http://xhgui.xhgui.orb.local/run/import</item>
<item name="timeout" xsi:type="number">3</item>
<item name="token" xsi:type="string">token</item>
</item>
</argument>
</arguments>
</type>
</config>

0 comments on commit a3ae3a3

Please sign in to comment.