Skip to content
Closed
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
2 changes: 2 additions & 0 deletions apps/dav/appinfo/v1/caldav.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
use OCA\DAV\Connector\Sabre\MaintenancePlugin;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\Profiler\ProfilerPlugin;
use OCP\Accounts\IAccountManager;
use Psr\Log\LoggerInterface;

Expand Down Expand Up @@ -116,6 +117,7 @@
$server->addPlugin(\OC::$server->query(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class));
}
$server->addPlugin(new ExceptionLoggerPlugin('caldav', $logger));
$server->addPlugin(\OC::$server->get(ProfilerPlugin::class));

// And off we go!
$server->exec();
2 changes: 2 additions & 0 deletions apps/dav/appinfo/v1/carddav.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
use OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin;
use OCA\DAV\Connector\Sabre\MaintenancePlugin;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\Profiler\ProfilerPlugin;
use OCP\Accounts\IAccountManager;
use OCP\App\IAppManager;
use Psr\Log\LoggerInterface;
Expand Down Expand Up @@ -103,6 +104,7 @@
\OC::$server->get(LoggerInterface::class)
)));
$server->addPlugin(new ExceptionLoggerPlugin('carddav', \OC::$server->get(LoggerInterface::class)));
$server->addPlugin(\OC::$server->get(ProfilerPlugin::class));

// And off we go!
$server->exec();
2 changes: 2 additions & 0 deletions apps/dav/appinfo/v2/direct.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*
*/
use \OCA\DAV\Direct\ServerFactory;
use OCA\DAV\Profiler\ProfilerPlugin;

// no php execution timeout for webdav
if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
Expand All @@ -48,5 +49,6 @@
\OC::$server->getBruteForceThrottler(),
\OC::$server->getRequest()
);
$server->addPlugin(\OC::$server->get(ProfilerPlugin::class));

$server->exec();
2 changes: 2 additions & 0 deletions apps/dav/lib/Connector/Sabre/ServerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
*/
namespace OCA\DAV\Connector\Sabre;

use OCA\DAV\Profiler\ProfilerPlugin;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Folder;
use OCA\DAV\AppInfo\PluginManager;
Expand Down Expand Up @@ -99,6 +100,7 @@ public function createServer(string $baseUri,
$server->setBaseUri($baseUri);

// Load plugins
$server->addPlugin(\OC::$server->get(ProfilerPlugin::class));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config, $this->l10n));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin($this->config));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin());
Expand Down
63 changes: 53 additions & 10 deletions apps/dav/lib/Profiler/ProfilerPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,68 @@

namespace OCA\DAV\Profiler;

use OCP\AppFramework\Http\Response;
use OCP\Diagnostics\IEventLogger;
use OCP\IRequest;
use OCP\Profiler\IProfiler;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;

class ProfilerPlugin extends \Sabre\DAV\ServerPlugin {
private IRequest $request;
class ProfilerPlugin extends ServerPlugin {
private bool $finalized = false;
public function __construct(
private IRequest $request,
private IProfiler $profiler,
private IEventLogger $eventLogger,
) {
$a = 1;
}

public function initialize(Server $server): void {
$server->on('beforeMethod:*', [$this, 'beforeMethod'], 1);
$server->on('afterMethod:*', [$this, 'afterMethod'], 9999);
$server->on('afterResponse', [$this, 'afterResponse'], 9999);
$server->on('exception', [$this, 'exception']);
}

public function beforeMethod(): void {
$this->eventLogger->start('dav:server:method', 'Processing dav request');
}

public function __construct(IRequest $request) {
$this->request = $request;
public function afterMethod(RequestInterface $request, ResponseInterface $response): void {
$this->eventLogger->end('dav:server:method');
$this->eventLogger->start('dav:server:response', 'Sending dav response');
if ($this->profiler->isEnabled()) {
$response->addHeader('X-Debug-Token', $this->request->getId());
}
}

/** @return void */
public function initialize(Server $server) {
$server->on('afterMethod:*', [$this, 'afterMethod']);
public function afterResponse(RequestInterface $request, ResponseInterface $response): void {
$this->eventLogger->end('dav:server:response');
$this->finalize($response->getStatus());
}

/** @return void */
public function afterMethod(RequestInterface $request, ResponseInterface $response) {
$response->addHeader('X-Debug-Token', $this->request->getId());
public function exception(): void {
$this->finalize();
}

public function __destruct() {
// in error cases, the "afterResponse" isn't called, so we do the finalization now
$this->finalize();
}

public function finalize(int $status = null): void {
if ($this->finalized) {
return;
}
$this->finalized = true;

$this->eventLogger->end('runtime');
if ($this->profiler->isEnabled()) {
$profile = $this->profiler->collect($this->request, new Response($status ?? 0));
$this->profiler->saveProfile($profile);
}
}
}
11 changes: 1 addition & 10 deletions apps/dav/lib/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public function __construct(IRequest $request, string $baseUri) {
$this->server->httpRequest->setUrl($this->request->getRequestUri());
$this->server->setBaseUri($this->baseUri);

$this->server->addPlugin(new ProfilerPlugin($this->request));
$this->server->addPlugin(\OC::$server->get(ProfilerPlugin::class));
$this->server->addPlugin(new BlockLegacyClientPlugin(\OC::$server->getConfig()));
$this->server->addPlugin(new AnonymousOptionsPlugin());
$authPlugin = new Plugin();
Expand Down Expand Up @@ -361,16 +361,7 @@ function () {
}

public function exec() {
/** @var IEventLogger $eventLogger */
$eventLogger = \OC::$server->get(IEventLogger::class);
$eventLogger->start('dav_server_exec', '');
$this->server->exec();
$eventLogger->end('dav_server_exec');
if ($this->profiler->isEnabled()) {
$eventLogger->end('runtime');
$profile = $this->profiler->collect(\OC::$server->get(IRequest::class), new Response());
$this->profiler->saveProfile($profile);
}
}

private function requestIsForSubtree(array $subTrees): bool {
Expand Down
34 changes: 34 additions & 0 deletions core/Command/Profiler/Clear.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

// SPDX-FileCopyrightText: 2022 Robin Appelman <[email protected]>
// SPDX-License-Identifier: AGPL-3.0-or-later

namespace OC\Core\Command\Profiler;

use OC\Core\Command\Base;
use OCP\Profiler\IProfiler;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Clear extends Base {
private IProfiler $profiler;

public function __construct(IProfiler $profiler) {
parent::__construct();
$this->profiler = $profiler;
}

protected function configure(): void {
$this
->setName('profiler:clear')
->setDescription('Remove all saved profiles');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$this->profiler->clear();

return 0;
}
}
33 changes: 33 additions & 0 deletions core/Command/Profiler/Disable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

// SPDX-FileCopyrightText: 2022 Robin Appelman <[email protected]>
// SPDX-License-Identifier: AGPL-3.0-or-later

namespace OC\Core\Command\Profiler;

use OCP\IConfig;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Disable extends Command {
private IConfig $config;

public function __construct(IConfig $config) {
parent::__construct();
$this->config = $config;
}

protected function configure(): void {
$this
->setName('profiler:disable')
->setDescription('Disable profiling');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$this->config->setSystemValue('profiler', false);
return 0;
}
}
33 changes: 33 additions & 0 deletions core/Command/Profiler/Enable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

// SPDX-FileCopyrightText: 2022 Robin Appelman <[email protected]>
// SPDX-License-Identifier: AGPL-3.0-or-later

namespace OC\Core\Command\Profiler;

use OCP\IConfig;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Enable extends Command {
private IConfig $config;

public function __construct(IConfig $config) {
parent::__construct();
$this->config = $config;
}

protected function configure(): void {
$this
->setName('profiler:enable')
->setDescription('Enable profiling');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$this->config->setSystemValue('profiler', true);
return 0;
}
}
57 changes: 57 additions & 0 deletions core/Command/Profiler/Export.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

// SPDX-FileCopyrightText: 2022 Robin Appelman <[email protected]>
// SPDX-License-Identifier: AGPL-3.0-or-later

namespace OC\Core\Command\Profiler;

use OC\Core\Command\Base;
use OCP\Profiler\IProfiler;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class Export extends Base {
private IProfiler $profiler;

public function __construct(IProfiler $profiler) {
parent::__construct();
$this->profiler = $profiler;
}

protected function configure(): void {
$this
->setName('profiler:export')
->setDescription('Export captured profiles as json')
->addOption('limit', null, InputOption::VALUE_REQUIRED, 'Maximum number of profiles to export')
->addOption('url', null, InputOption::VALUE_REQUIRED, 'Url to export profiles for')
->addOption('since', null, InputOption::VALUE_REQUIRED, 'Minimum date for exported profiles, as unix timestamp')
->addOption('before', null, InputOption::VALUE_REQUIRED, 'Maximum date for exported profiles, as unix timestamp')
->addOption('token', null, InputOption::VALUE_REQUIRED, 'Export only profile for a single request token');
}

protected function execute(InputInterface $input, OutputInterface $output): int {
$since = $input->getOption('since') ? (int)$input->getOption('since') : null;
$before = $input->getOption('before') ? (int)$input->getOption('before') : null;
$limit = $input->getOption('limit') ? (int)$input->getOption('limit') : 1000;
$token = $input->getOption('token') ? $input->getOption('token') : null;
$url = $input->getOption('url');

if ($token) {
$profiles = [$this->profiler->loadProfile($token)];
$profiles = array_filter($profiles);
} else {
$profiles = $this->profiler->find($url, $limit, null, $since, $before);
$profiles = array_reverse($profiles);
$profiles = array_map(function (array $profile) {
return $this->profiler->loadProfile($profile['token']);
}, $profiles);
}

$output->writeln(json_encode($profiles));

return 0;
}
}
Loading