Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge release 0.20.1 into 0.21.x #462

Merged
merged 16 commits into from
Feb 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

A graphical interface for XHProf profiling data that can store the results in MongoDB or PDO database.

Application is [profiled](#profiling-a-web-request-or-cli-script) and the
Application is profiled and the
profiling data is transferred to XHGui, which takes that information, saves it
in MongoDB (or PDO database), and provides a convenient GUI for working with
it.

This project is the GUI for showing profiling results,
to profile your application, use specific minimal library:
- [perftools/php-profiler]

[perftools/php-profiler]: #profiling-a-web-request-or-cli-script

[![Build Status](https://travis-ci.org/perftools/xhgui.svg?branch=master)](https://travis-ci.org/perftools/xhgui)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/perftools/xhgui/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/perftools/xhgui/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/perftools/xhgui/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/perftools/xhgui/?branch=master)
Expand All @@ -15,13 +21,17 @@ it.

XHGui has the following requirements:

- Known to work: PHP >= 7.2, 8.0.
- Known to work: PHP >= 7.2, 8.0, 8.1
- If using MongoDB storage, see [MongoDB](#MongoDB) requirements
- If using PDO storage, see [PDO](#PDO) requirements
- To profile an application, one of the profiling PHP extensions is required.
See [Profiling a Web Request or CLI script](#profiling-a-web-request-or-cli-script).
The extension is not needed to run XHGui itself.

If you need to decide which backend to use, you can check the [compatibility
matrix](#compatibility-matrix) what features are implemented or missing per
backend.

## MongoDB

The default installation uses MongoDB database. Most of the documentation speaks about MongoDB.
Expand Down Expand Up @@ -92,6 +102,7 @@ NOTE: PDO may not support all the features of XHGui, see [#320].
> db.results.ensureIndex( { 'profile.main().cpu' : -1 } )
> db.results.ensureIndex( { 'meta.url' : 1 } )
> db.results.ensureIndex( { 'meta.simple_url' : 1 } )
> db.results.ensureIndex( { 'meta.SERVER.SERVER_NAME' : 1 } )
```

7. Install dependencies with composer
Expand Down Expand Up @@ -242,6 +253,33 @@ Some Notes:
health are exposed on `/metrics`. (This currently only works if using PDO for
storage.)

# Compatibility matrix

| Feature | MongoDB | PDO |
|---------------------------------|----------|----------|
| Prometheus exporter | ✗ | ✓ [#305] |
| Searcher::latest() | ✓ | ✓ |
| Searcher::query() | ✓ | ✗ [#384] |
| Searcher::get() | ✓ | ✓ |
| Searcher::getForUrl() | ✓ | ✓ [#436] |
| Searcher::getPercentileForUrl() | ✓ | ✓ [#436] |
| Searcher::getAvgsForUrl() | ✓ | ✗ [#384] |
| Searcher::getAll(sort) | ✓ | ✓ [#436] |
| Searcher::getAll(direction) | ✓ | ✓ [#436] |
| Searcher::delete() | ✓ | ✓ |
| Searcher::truncate() | ✓ | ✓ |
| Searcher::saveWatch() | ✓ | ✓ [#435] |
| Searcher::getAllWatches() | ✓ | ✓ [#435] |
| Searcher::truncateWatches() | ✓ | ✓ [#435] |
| Searcher::stats() | ✗ [#305] | ✓ |
| Searcher::getAllServerNames() | ✓ [#460] | ✗ |

[#305]: https://github.com/perftools/xhgui/pull/305
[#384]: https://github.com/perftools/xhgui/pull/384
[#435]: https://github.com/perftools/xhgui/pull/435
[#436]: https://github.com/perftools/xhgui/pull/436
[#460]: https://github.com/perftools/xhgui/pull/460

# Releases / Changelog

See the [releases](https://github.com/perftools/xhgui/releases) for changelogs,
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"platform-check": "php-only"
},
"require": {
"php": "^7.2 || ^8.0",
"php": "^7.2 || ~8.0 || ~8.1",
"ext-json": "*",
"alcaeus/mongo-php-adapter": "^1.1",
"pimple/pimple": "^3.0",
Expand Down
4 changes: 2 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mongo.init.d/xhgui.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ db.results.ensureIndex( { 'profile.main().wt' : -1 } );
db.results.ensureIndex( { 'profile.main().mu' : -1 } );
db.results.ensureIndex( { 'profile.main().cpu' : -1 } );
db.results.ensureIndex( { 'meta.url' : 1 } );
db.results.ensureIndex( { 'meta.SERVER.SERVER_NAME' : 1 } );
4 changes: 2 additions & 2 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="./vendor/autoload.php">
<php>
<server name="SYMFONY_PHPUNIT_VERSION" value="6.5" />
<server name="SYMFONY_PHPUNIT_VERSION" value="8.5" />
<env name="SYMFONY_DEPRECATIONS_HELPER" value="max[self]=0"/>
</php>
<testsuites>
<testsuite>
<testsuite name="Tests">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
Expand Down
3 changes: 3 additions & 0 deletions src/Controller/RunController.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public function index(Request $request): void
'projection' => true,
]));

$serverNames = $this->searcher->getAllServerNames();

$title = 'Recent runs';
$titleMap = [
'wt' => 'Longest wall time',
Expand All @@ -71,6 +73,7 @@ public function index(Request $request): void
'search' => $search,
'has_search' => implode('', $search) !== '',
'title' => $title,
'server_names' => $serverNames,
]);
}

Expand Down
8 changes: 8 additions & 0 deletions src/Searcher/MongoSearcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -364,4 +364,12 @@ public function stats(): array
'bytes' => 0,
];
}

/**
* {@inheritdoc}
*/
public function getAllServerNames(): ?array
{
return $this->_collection->distinct('meta.SERVER.SERVER_NAME');
}
}
8 changes: 8 additions & 0 deletions src/Searcher/PdoSearcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,14 @@ public function stats()
return $row;
}

/**
* {@inheritdoc}
*/
public function getAllServerNames(): ?array
{
return null;
}

/**
* {@inheritdoc}
*/
Expand Down
7 changes: 7 additions & 0 deletions src/Searcher/SearcherInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,11 @@ public function truncateWatches();
* @return array array of stats
*/
public function stats();

/**
* Get all the known server names.
*
* @return array|null array of server names or null if not supported
*/
public function getAllServerNames(): ?array;
}
19 changes: 1 addition & 18 deletions src/ServiceProvider/RouteProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,8 @@ public function register(Container $di): void

private function registerRoutes(Container $di, App $app): void
{
/*
$app->error(static function (Exception $e) use ($di, $app): void {
// @var Twig $view
$view = $di['view'];
$view->parserOptions['cache'] = false;
$view->parserExtensions = [
new TwigExtension($app),
];

$app->view($view);
$app->render('error/view.twig', [
'message' => $e->getMessage(),
'stack_trace' => $e->getTraceAsString(),
]);
});
*/

/**
* Wrap Request/Response with RequestProxuy/RequestWrapper
* Wrap Request/Response with RequestProxy/RequestWrapper
*/
$wrap = static function ($handler) use ($di, $app) {
return function () use ($handler, $di, $app) {
Expand Down
19 changes: 0 additions & 19 deletions templates/error/view.twig

This file was deleted.

11 changes: 10 additions & 1 deletion templates/runs/list.twig
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,16 @@
<div class="control-group span4">
<label class="control-label" for="server-name">Server Name</label>
<div class="controls">
<input type="text" id="server-name" name="server_name" value="{{ search.server_name }}">
{% if server_names is iterable %}
<select id="server-name" name="server_name">
{% for server_name in server_names %}
<option></option>
<option value="{{ server_name }}"{% if server_name == search.server_name %} selected{% endif %}>{{ server_name }}</option>
{% endfor %}
</select>
{% else %}
<input type="text" id="server-name" name="server_name" value="{{ search.server_name }}">
{% endif %}
</div>
<label class="control-label" for="url">URL</label>
<div class="controls">
Expand Down
2 changes: 1 addition & 1 deletion tests/Controller/RunTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public function testCallgraphData(): void
]);

$result = $this->runs->callgraphData($this->request);
$this->assertInternalType('array', $result);
$this->assertIsArray($result);
$this->assertArrayHasKey('metric', $result);
$this->assertArrayHasKey('total', $result);
$this->assertArrayHasKey('nodes', $result);
Expand Down
18 changes: 16 additions & 2 deletions tests/Saver/MongoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,23 @@ public function testSave(): void
$collection = $this->getMockBuilder(MongoCollection::class)
->disableOriginalConstructor()
->getMock();
$collection->expects($this->exactly(count($data)))

$collection
->expects($this->exactly(count($data)))
->method('insert')
->withConsecutive($this->equalTo($data));
->withConsecutive(...array_map(function () {
return [
$this->callback(function ($data) {
$this->assertIsArray($data);
$this->assertArrayHasKey('_id', $data);
$this->assertArrayHasKey('meta', $data);
$this->assertArrayHasKey('profile', $data);

return true;
}),
$this->equalTo(['w' => 0]),
];
}, $data));

$saver = new MongoSaver($collection);

Expand Down
8 changes: 8 additions & 0 deletions tests/Searcher/MongoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,12 @@ public function testTruncateWatchesPreserveIndexes(): void
$this->assertEquals($options['name'], $name);
}
}

public function testGetAllServerNames(): void
{
$result = $this->mongo->getAllServerNames();
$this->assertCount(2, $result);
$this->assertContains('localhost', $result);
$this->assertContains('foo', $result);
}
}
20 changes: 20 additions & 0 deletions tests/fixtures/normalized.json
Original file line number Diff line number Diff line change
Expand Up @@ -279,5 +279,25 @@
"pmu": 0
}
}
},
{
"_id": "aaaaaaaaaaaaaaaaaaaaaab2",
"meta": {
"url": "/bar",
"simple_url": "/bar",
"get": [],
"env": [],
"SERVER": {"REQUEST_TIME": 1358614812, "SERVER_NAME": "foo", "REQUEST_METHOD": "GET"},
"request_ts_micro": {"sec": 1358614812, "usec": 123456}
},
"profile": {
"main()": {
"ct": 1,
"wt": 10,
"cpu": 12,
"mu": 3000,
"pmu": 3001
}
}
}
]