The webui is provided by a seperate application named profiler Signed-off-by: Carl Schwan <carl@carlschwan.eu>pull/30823/head
parent
cf4c77e064
commit
7d272c54d0
@ -0,0 +1,47 @@ |
||||
<?php declare(strict_types = 1); |
||||
/** |
||||
* @copyright 2021 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license AGPL-3.0-or-later |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCA\DAV\Profiler; |
||||
|
||||
use OCP\IRequest; |
||||
use Sabre\DAV\Server; |
||||
use Sabre\HTTP\RequestInterface; |
||||
use Sabre\HTTP\ResponseInterface; |
||||
|
||||
class ProfilerPlugin extends \Sabre\DAV\ServerPlugin { |
||||
private IRequest $request; |
||||
|
||||
public function __construct(IRequest $request) { |
||||
$this->request = $request; |
||||
} |
||||
|
||||
/** @return void */ |
||||
public function initialize(Server $server) { |
||||
$server->on('afterMethod:*', [$this, 'afterMethod']); |
||||
} |
||||
|
||||
/** @return void */ |
||||
public function afterMethod(RequestInterface $request, ResponseInterface $response) { |
||||
$response->addHeader('X-Debug-Token', $this->request->getId()); |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
<?php declare(strict_types = 1); |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCA\User_LDAP\DataCollector; |
||||
|
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\Response; |
||||
use OCP\DataCollector\AbstractDataCollector; |
||||
|
||||
class LdapDataCollector extends AbstractDataCollector { |
||||
public function startLdapRequest(string $query, array $args): void { |
||||
$this->data[] = [ |
||||
'start' => microtime(true), |
||||
'query' => $query, |
||||
'args' => $args, |
||||
'end' => microtime(true), |
||||
]; |
||||
} |
||||
|
||||
public function stopLastLdapRequest(): void { |
||||
$this->data[count($this->data) - 1]['end'] = microtime(true); |
||||
} |
||||
|
||||
public function getName(): string { |
||||
return 'ldap'; |
||||
} |
||||
|
||||
public function collect(Request $request, Response $response, \Throwable $exception = null): void { |
||||
} |
||||
} |
@ -0,0 +1,154 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\DB; |
||||
|
||||
use Doctrine\DBAL\Logging\DebugStack; |
||||
use Doctrine\DBAL\Types\ConversionException; |
||||
use Doctrine\DBAL\Types\Type; |
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\Response; |
||||
|
||||
class DbDataCollector extends \OCP\DataCollector\AbstractDataCollector { |
||||
protected ?DebugStack $debugStack = null; |
||||
private Connection $connection; |
||||
|
||||
/** |
||||
* DbDataCollector constructor. |
||||
*/ |
||||
public function __construct(Connection $connection) { |
||||
$this->connection = $connection; |
||||
} |
||||
|
||||
public function setDebugStack(DebugStack $debugStack, $name = 'default'): void { |
||||
$this->debugStack = $debugStack; |
||||
} |
||||
|
||||
/** |
||||
* @inheritDoc |
||||
*/ |
||||
public function collect(Request $request, Response $response, \Throwable $exception = null): void { |
||||
$queries = $this->sanitizeQueries($this->debugStack->queries); |
||||
|
||||
$this->data = [ |
||||
'queries' => $queries, |
||||
]; |
||||
} |
||||
|
||||
public function getName(): string { |
||||
return 'db'; |
||||
} |
||||
|
||||
public function getQueries(): array { |
||||
return $this->data['queries']; |
||||
} |
||||
|
||||
private function sanitizeQueries(array $queries): array { |
||||
foreach ($queries as $i => $query) { |
||||
$queries[$i] = $this->sanitizeQuery($query); |
||||
} |
||||
|
||||
return $queries; |
||||
} |
||||
|
||||
private function sanitizeQuery(array $query): array { |
||||
$query['explainable'] = true; |
||||
$query['runnable'] = true; |
||||
if (null === $query['params']) { |
||||
$query['params'] = []; |
||||
} |
||||
if (!\is_array($query['params'])) { |
||||
$query['params'] = [$query['params']]; |
||||
} |
||||
if (!\is_array($query['types'])) { |
||||
$query['types'] = []; |
||||
} |
||||
foreach ($query['params'] as $j => $param) { |
||||
$e = null; |
||||
if (isset($query['types'][$j])) { |
||||
// Transform the param according to the type |
||||
$type = $query['types'][$j]; |
||||
if (\is_string($type)) { |
||||
$type = Type::getType($type); |
||||
} |
||||
if ($type instanceof Type) { |
||||
$query['types'][$j] = $type->getBindingType(); |
||||
try { |
||||
$param = $type->convertToDatabaseValue($param, $this->connection->getDatabasePlatform()); |
||||
} catch (\TypeError $e) { |
||||
} catch (ConversionException $e) { |
||||
} |
||||
} |
||||
} |
||||
|
||||
[$query['params'][$j], $explainable, $runnable] = $this->sanitizeParam($param, $e); |
||||
if (!$explainable) { |
||||
$query['explainable'] = false; |
||||
} |
||||
|
||||
if (!$runnable) { |
||||
$query['runnable'] = false; |
||||
} |
||||
} |
||||
|
||||
return $query; |
||||
} |
||||
|
||||
/** |
||||
* Sanitizes a param. |
||||
* |
||||
* The return value is an array with the sanitized value and a boolean |
||||
* indicating if the original value was kept (allowing to use the sanitized |
||||
* value to explain the query). |
||||
*/ |
||||
private function sanitizeParam($var, ?\Throwable $error): array { |
||||
if (\is_object($var)) { |
||||
return [$o = new ObjectParameter($var, $error), false, $o->isStringable() && !$error]; |
||||
} |
||||
|
||||
if ($error) { |
||||
return ['⚠ '.$error->getMessage(), false, false]; |
||||
} |
||||
|
||||
if (\is_array($var)) { |
||||
$a = []; |
||||
$explainable = $runnable = true; |
||||
foreach ($var as $k => $v) { |
||||
[$value, $e, $r] = $this->sanitizeParam($v, null); |
||||
$explainable = $explainable && $e; |
||||
$runnable = $runnable && $r; |
||||
$a[$k] = $value; |
||||
} |
||||
|
||||
return [$a, $explainable, $runnable]; |
||||
} |
||||
|
||||
if (\is_resource($var)) { |
||||
return [sprintf('/* Resource(%s) */', get_resource_type($var)), false, false]; |
||||
} |
||||
|
||||
return [$var, true, true]; |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
|
||||
/* |
||||
* This file is part of the Symfony package. |
||||
* |
||||
* |
||||
* For the full copyright and license information, please view the LICENSE |
||||
* file that was distributed with this source code. |
||||
*/ |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* @author Fabien Potencier <fabien@symfony.com> |
||||
* |
||||
* @license AGPL-3.0-or-later AND MIT |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\DB; |
||||
|
||||
final class ObjectParameter { |
||||
private $object; |
||||
private $error; |
||||
private $stringable; |
||||
private $class; |
||||
|
||||
/** |
||||
* @param object $object |
||||
*/ |
||||
public function __construct($object, ?\Throwable $error) { |
||||
$this->object = $object; |
||||
$this->error = $error; |
||||
$this->stringable = \is_callable([$object, '__toString']); |
||||
$this->class = \get_class($object); |
||||
} |
||||
|
||||
/** |
||||
* @return object |
||||
*/ |
||||
public function getObject() { |
||||
return $this->object; |
||||
} |
||||
|
||||
public function getError(): ?\Throwable { |
||||
return $this->error; |
||||
} |
||||
|
||||
public function isStringable(): bool { |
||||
return $this->stringable; |
||||
} |
||||
|
||||
public function getClass(): string { |
||||
return $this->class; |
||||
} |
||||
} |
@ -0,0 +1,177 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Memcache; |
||||
|
||||
use OCP\IMemcacheTTL; |
||||
|
||||
/** |
||||
* Cache wrapper that logs the cache operation in a log file |
||||
*/ |
||||
class LoggerWrapperCache extends Cache implements IMemcacheTTL { |
||||
/** @var Redis */ |
||||
protected $wrappedCache; |
||||
|
||||
/** @var string $logFile */ |
||||
private $logFile; |
||||
|
||||
/** @var string $prefix */ |
||||
protected $prefix; |
||||
|
||||
public function __construct(Redis $wrappedCache, string $logFile) { |
||||
parent::__construct($wrappedCache->getPrefix()); |
||||
$this->wrappedCache = $wrappedCache; |
||||
$this->logFile = $logFile; |
||||
} |
||||
|
||||
/** |
||||
* @return string Prefix used for caching purposes |
||||
*/ |
||||
public function getPrefix() { |
||||
return $this->prefix; |
||||
} |
||||
|
||||
protected function getNameSpace() { |
||||
return $this->prefix; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function get($key) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::get::' . $key . "\n", |
||||
FILE_APPEND |
||||
); |
||||
return $this->wrappedCache->get($key); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function set($key, $value, $ttl = 0) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::set::' . $key . '::' . $ttl . '::' . json_encode($value) . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->set($key, $value, $$ttl); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function hasKey($key) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::hasKey::' . $key . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->hasKey($key); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function remove($key) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::remove::' . $key . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->remove($key); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function clear($prefix = '') { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::clear::' . $prefix . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->clear($prefix); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function add($key, $value, $ttl = 0) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::add::' . $key . '::' . $value . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->add($key, $value, $ttl); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function inc($key, $step = 1) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::inc::' . $key . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->inc($key, $step); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function dec($key, $step = 1) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::dec::' . $key . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->dec($key, $step); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function cas($key, $old, $new) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::cas::' . $key . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->cas($key, $old, $new); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function cad($key, $old) { |
||||
file_put_contents( |
||||
$this->logFile, |
||||
$this->getNameSpace() . '::cad::' . $key . "\n", |
||||
FILE_APPEND |
||||
); |
||||
|
||||
return $this->wrappedCache->cad($key, $old); |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function setTTL($key, $ttl) { |
||||
$this->wrappedCache->setTTL($key, $ttl); |
||||
} |
||||
|
||||
public static function isAvailable(): bool { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,220 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Memcache; |
||||
|
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\Response; |
||||
use OCP\DataCollector\AbstractDataCollector; |
||||
use OCP\IMemcacheTTL; |
||||
|
||||
/** |
||||
* Cache wrapper that logs profiling information |
||||
*/ |
||||
class ProfilerWrapperCache extends AbstractDataCollector implements IMemcacheTTL, \ArrayAccess { |
||||
/** @var Redis $wrappedCache*/ |
||||
protected $wrappedCache; |
||||
|
||||
/** @var string $prefix */ |
||||
protected $prefix; |
||||
|
||||
/** @var string $type */ |
||||
private $type; |
||||
|
||||
public function __construct(Redis $wrappedCache, string $type) { |
||||
$this->prefix = $wrappedCache->getPrefix(); |
||||
$this->wrappedCache = $wrappedCache; |
||||
$this->type = $type; |
||||
$this->data['queries'] = []; |
||||
$this->data['cacheHit'] = 0; |
||||
$this->data['cacheMiss'] = 0; |
||||
} |
||||
|
||||
public function getPrefix(): string { |
||||
return $this->prefix; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function get($key) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->get($key); |
||||
if ($ret === null) { |
||||
$this->data['cacheMiss']++; |
||||
} else { |
||||
$this->data['cacheHit']++; |
||||
} |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::get::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function set($key, $value, $ttl = 0) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->set($key, $value, $ttl); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::set::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function hasKey($key) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->hasKey($key); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::hasKey::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function remove($key) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->remove($key); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::remove::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function clear($prefix = '') { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->clear($prefix); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::clear::' . $prefix, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function add($key, $value, $ttl = 0) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->add($key, $value, $ttl); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::add::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function inc($key, $step = 1) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->inc($key, $step); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::inc::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function dec($key, $step = 1) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->dec($key, $step); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::dev::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function cas($key, $old, $new) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->cas($key, $old, $new); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::cas::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function cad($key, $old) { |
||||
$start = microtime(true); |
||||
$ret = $this->wrappedCache->cad($key, $old); |
||||
$this->data['queries'][] = [ |
||||
'start' => $start, |
||||
'end' => microtime(true), |
||||
'op' => $this->getPrefix() . '::cad::' . $key, |
||||
]; |
||||
return $ret; |
||||
} |
||||
|
||||
/** @inheritDoc */ |
||||
public function setTTL($key, $ttl) { |
||||
$this->wrappedCache->setTTL($key, $ttl); |
||||
} |
||||
|
||||
public function offsetExists($offset): bool { |
||||
return $this->hasKey($offset); |
||||
} |
||||
|
||||
public function offsetSet($offset, $value): void { |
||||
$this->set($offset, $value); |
||||
} |
||||
|
||||
/** |
||||
* @return mixed |
||||
*/ |
||||
#[\ReturnTypeWillChange] |
||||
public function offsetGet($offset) { |
||||
return $this->get($offset); |
||||
} |
||||
|
||||
public function offsetUnset($offset): void { |
||||
$this->remove($offset); |
||||
} |
||||
|
||||
public function collect(Request $request, Response $response, \Throwable $exception = null): void { |
||||
// Nothing to do here $data is already ready |
||||
} |
||||
|
||||
public function getName(): string { |
||||
return 'cache/' . $this->type . '/' . $this->prefix; |
||||
} |
||||
|
||||
public static function isAvailable(): bool { |
||||
return true; |
||||
} |
||||
} |
@ -0,0 +1,286 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* @author Alexandre Salomé <alexandre.salome@gmail.com> |
||||
* |
||||
* @license AGPL-3.0-or-later AND MIT |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Profiler; |
||||
|
||||
use OCP\Profiler\IProfile; |
||||
|
||||
/** |
||||
* Storage for profiler using files. |
||||
*/ |
||||
class FileProfilerStorage { |
||||
// Folder where profiler data are stored. |
||||
private string $folder; |
||||
|
||||
/** |
||||
* Constructs the file storage using a "dsn-like" path. |
||||
* |
||||
* Example : "file:/path/to/the/storage/folder" |
||||
* |
||||
* @throws \RuntimeException |
||||
*/ |
||||
public function __construct(string $folder) { |
||||
$this->folder = $folder; |
||||
|
||||
if (!is_dir($this->folder) && false === @mkdir($this->folder, 0777, true) && !is_dir($this->folder)) { |
||||
throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $this->folder)); |
||||
} |
||||
} |
||||
|
||||
public function find(?string $url, ?int $limit, ?string $method, int $start = null, int $end = null, string $statusCode = null): array { |
||||
$file = $this->getIndexFilename(); |
||||
|
||||
if (!file_exists($file)) { |
||||
return []; |
||||
} |
||||
|
||||
$file = fopen($file, 'r'); |
||||
fseek($file, 0, \SEEK_END); |
||||
|
||||
$result = []; |
||||
while (\count($result) < $limit && $line = $this->readLineFromFile($file)) { |
||||
$values = str_getcsv($line); |
||||
[$csvToken, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode] = $values; |
||||
$csvTime = (int) $csvTime; |
||||
|
||||
if ($url && false === strpos($csvUrl, $url) || $method && false === strpos($csvMethod, $method) || $statusCode && false === strpos($csvStatusCode, $statusCode)) { |
||||
continue; |
||||
} |
||||
|
||||
if (!empty($start) && $csvTime < $start) { |
||||
continue; |
||||
} |
||||
|
||||
if (!empty($end) && $csvTime > $end) { |
||||
continue; |
||||
} |
||||
|
||||
$result[$csvToken] = [ |
||||
'token' => $csvToken, |
||||
'method' => $csvMethod, |
||||
'url' => $csvUrl, |
||||
'time' => $csvTime, |
||||
'parent' => $csvParent, |
||||
'status_code' => $csvStatusCode, |
||||
]; |
||||
} |
||||
|
||||
fclose($file); |
||||
|
||||
return array_values($result); |
||||
} |
||||
|
||||
public function purge(): void { |
||||
$flags = \FilesystemIterator::SKIP_DOTS; |
||||
$iterator = new \RecursiveDirectoryIterator($this->folder, $flags); |
||||
$iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::CHILD_FIRST); |
||||
|
||||
foreach ($iterator as $file) { |
||||
if (is_file($file)) { |
||||
unlink($file); |
||||
} else { |
||||
rmdir($file); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public function read(string $token): ?IProfile { |
||||
if (!$token || !file_exists($file = $this->getFilename($token))) { |
||||
return null; |
||||
} |
||||
|
||||
if (\function_exists('gzcompress')) { |
||||
$file = 'compress.zlib://'.$file; |
||||
} |
||||
|
||||
return $this->createProfileFromData($token, unserialize(file_get_contents($file))); |
||||
} |
||||
|
||||
/** |
||||
* @throws \RuntimeException |
||||
*/ |
||||
public function write(IProfile $profile): bool { |
||||
$file = $this->getFilename($profile->getToken()); |
||||
|
||||
$profileIndexed = is_file($file); |
||||
if (!$profileIndexed) { |
||||
// Create directory |
||||
$dir = \dirname($file); |
||||
if (!is_dir($dir) && false === @mkdir($dir, 0777, true) && !is_dir($dir)) { |
||||
throw new \RuntimeException(sprintf('Unable to create the storage directory (%s).', $dir)); |
||||
} |
||||
} |
||||
|
||||
$profileToken = $profile->getToken(); |
||||
// when there are errors in sub-requests, the parent and/or children tokens |
||||
// may equal the profile token, resulting in infinite loops |
||||
$parentToken = $profile->getParentToken() !== $profileToken ? $profile->getParentToken() : null; |
||||
$childrenToken = array_filter(array_map(function (IProfile $p) use ($profileToken) { |
||||
return $profileToken !== $p->getToken() ? $p->getToken() : null; |
||||
}, $profile->getChildren())); |
||||
|
||||
// Store profile |
||||
$data = [ |
||||
'token' => $profileToken, |
||||
'parent' => $parentToken, |
||||
'children' => $childrenToken, |
||||
'data' => $profile->getCollectors(), |
||||
'method' => $profile->getMethod(), |
||||
'url' => $profile->getUrl(), |
||||
'time' => $profile->getTime(), |
||||
'status_code' => $profile->getStatusCode(), |
||||
]; |
||||
|
||||
$context = stream_context_create(); |
||||
|
||||
if (\function_exists('gzcompress')) { |
||||
$file = 'compress.zlib://'.$file; |
||||
stream_context_set_option($context, 'zlib', 'level', 3); |
||||
} |
||||
|
||||
if (false === file_put_contents($file, serialize($data), 0, $context)) { |
||||
return false; |
||||
} |
||||
|
||||
if (!$profileIndexed) { |
||||
// Add to index |
||||
if (false === $file = fopen($this->getIndexFilename(), 'a')) { |
||||
return false; |
||||
} |
||||
|
||||
fputcsv($file, [ |
||||
$profile->getToken(), |
||||
$profile->getMethod(), |
||||
$profile->getUrl(), |
||||
$profile->getTime(), |
||||
$profile->getParentToken(), |
||||
$profile->getStatusCode(), |
||||
]); |
||||
fclose($file); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Gets filename to store data, associated to the token. |
||||
* |
||||
* @return string The profile filename |
||||
*/ |
||||
protected function getFilename(string $token): string { |
||||
// Uses 4 last characters, because first are mostly the same. |
||||
$folderA = substr($token, -2, 2); |
||||
$folderB = substr($token, -4, 2); |
||||
|
||||
return $this->folder.'/'.$folderA.'/'.$folderB.'/'.$token; |
||||
} |
||||
|
||||
/** |
||||
* Gets the index filename. |
||||
* |
||||
* @return string The index filename |
||||
*/ |
||||
protected function getIndexFilename(): string { |
||||
return $this->folder.'/index.csv'; |
||||
} |
||||
|
||||
/** |
||||
* Reads a line in the file, backward. |
||||
* |
||||
* This function automatically skips the empty lines and do not include the line return in result value. |
||||
* |
||||
* @param resource $file The file resource, with the pointer placed at the end of the line to read |
||||
* |
||||
* @return ?string A string representing the line or null if beginning of file is reached |
||||
*/ |
||||
protected function readLineFromFile($file): ?string { |
||||
$line = ''; |
||||
$position = ftell($file); |
||||
|
||||
if (0 === $position) { |
||||
return null; |
||||
} |
||||
|
||||
while (true) { |
||||
$chunkSize = min($position, 1024); |
||||
$position -= $chunkSize; |
||||
fseek($file, $position); |
||||
|
||||
if (0 === $chunkSize) { |
||||
// bof reached |
||||
break; |
||||
} |
||||
|
||||
$buffer = fread($file, $chunkSize); |
||||
|
||||
if (false === ($upTo = strrpos($buffer, "\n"))) { |
||||
$line = $buffer.$line; |
||||
continue; |
||||
} |
||||
|
||||
$position += $upTo; |
||||
$line = substr($buffer, $upTo + 1).$line; |
||||
fseek($file, max(0, $position), \SEEK_SET); |
||||
|
||||
if ('' !== $line) { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
return '' === $line ? null : $line; |
||||
} |
||||
|
||||
protected function createProfileFromData(string $token, array $data, IProfile $parent = null): IProfile { |
||||
$profile = new Profile($token); |
||||
$profile->setMethod($data['method']); |
||||
$profile->setUrl($data['url']); |
||||
$profile->setTime($data['time']); |
||||
$profile->setStatusCode($data['status_code']); |
||||
$profile->setCollectors($data['data']); |
||||
|
||||
if (!$parent && $data['parent']) { |
||||
$parent = $this->read($data['parent']); |
||||
} |
||||
|
||||
if ($parent) { |
||||
$profile->setParent($parent); |
||||
} |
||||
|
||||
foreach ($data['children'] as $token) { |
||||
if (!$token || !file_exists($file = $this->getFilename($token))) { |
||||
continue; |
||||
} |
||||
|
||||
if (\function_exists('gzcompress')) { |
||||
$file = 'compress.zlib://'.$file; |
||||
} |
||||
|
||||
$profile->addChild($this->createProfileFromData($token, unserialize(file_get_contents($file)), $profile)); |
||||
} |
||||
|
||||
return $profile; |
||||
} |
||||
} |
@ -0,0 +1,168 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Profiler; |
||||
|
||||
use OCP\DataCollector\IDataCollector; |
||||
use OCP\Profiler\IProfile; |
||||
|
||||
class Profile implements \JsonSerializable, IProfile { |
||||
private string $token; |
||||
|
||||
private ?int $time = null; |
||||
|
||||
private ?string $url = null; |
||||
|
||||
private ?string $method = null; |
||||
|
||||
private ?int $statusCode = null; |
||||
|
||||
/** @var array<string, IDataCollector> */ |
||||
private array $collectors = []; |
||||
|
||||
private ?IProfile $parent = null; |
||||
|
||||
/** @var IProfile[] */ |
||||
private array $children = []; |
||||
|
||||
public function __construct(string $token) { |
||||
$this->token = $token; |
||||
} |
||||
|
||||
public function getToken(): string { |
||||
return $this->token; |
||||
} |
||||
|
||||
public function setToken(string $token): void { |
||||
$this->token = $token; |
||||
} |
||||
|
||||
public function getTime(): ?int { |
||||
return $this->time; |
||||
} |
||||
|
||||
public function setTime(int $time): void { |
||||
$this->time = $time; |
||||
} |
||||
|
||||
public function getUrl(): ?string { |
||||
return $this->url; |
||||
} |
||||
|
||||
public function setUrl(string $url): void { |
||||
$this->url = $url; |
||||
} |
||||
|
||||
public function getMethod(): ?string { |
||||
return $this->method; |
||||
} |
||||
|
||||
public function setMethod(string $method): void { |
||||
$this->method = $method; |
||||
} |
||||
|
||||
public function getStatusCode(): ?int { |
||||
return $this->statusCode; |
||||
} |
||||
|
||||
public function setStatusCode(int $statusCode): void { |
||||
$this->statusCode = $statusCode; |
||||
} |
||||
|
||||
public function addCollector(IDataCollector $collector) { |
||||
$this->collectors[$collector->getName()] = $collector; |
||||
} |
||||
|
||||
public function getParent(): ?IProfile { |
||||
return $this->parent; |
||||
} |
||||
|
||||
public function setParent(?IProfile $parent): void { |
||||
$this->parent = $parent; |
||||
} |
||||
|
||||
public function getParentToken(): ?string { |
||||
return $this->parent ? $this->parent->getToken() : null; |
||||
} |
||||
|
||||
/** @return IProfile[] */ |
||||
public function getChildren(): array { |
||||
return $this->children; |
||||
} |
||||
|
||||
/** |
||||
* @param IProfile[] $children |
||||
*/ |
||||
public function setChildren(array $children): void { |
||||
$this->children = []; |
||||
foreach ($children as $child) { |
||||
$this->addChild($child); |
||||
} |
||||
} |
||||
|
||||
public function addChild(IProfile $profile): void { |
||||
$this->children[] = $profile; |
||||
$profile->setParent($this); |
||||
} |
||||
|
||||
/** |
||||
* @return IDataCollector[] |
||||
*/ |
||||
public function getCollectors(): array { |
||||
return $this->collectors; |
||||
} |
||||
|
||||
/** |
||||
* @param IDataCollector[] $collectors |
||||
*/ |
||||
public function setCollectors(array $collectors): void { |
||||
$this->collectors = $collectors; |
||||
} |
||||
|
||||
public function __sleep(): array { |
||||
return ['token', 'parent', 'children', 'collectors', 'method', 'url', 'time', 'statusCode']; |
||||
} |
||||
|
||||
#[\ReturnTypeWillChange] |
||||
public function jsonSerialize() { |
||||
// Everything but parent |
||||
return [ |
||||
'token' => $this->token, |
||||
'method' => $this->method, |
||||
'children' => $this->children, |
||||
'url' => $this->url, |
||||
'statusCode' => $this->statusCode, |
||||
'time' => $this->time, |
||||
'collectors' => $this->collectors, |
||||
]; |
||||
} |
||||
|
||||
public function getCollector(string $collectorName): ?IDataCollector { |
||||
if (!array_key_exists($collectorName, $this->collectors)) { |
||||
return null; |
||||
} |
||||
return $this->collectors[$collectorName]; |
||||
} |
||||
} |
@ -0,0 +1,105 @@ |
||||
<?php |
||||
|
||||
declare(strict_types = 1); |
||||
|
||||
/** |
||||
* @copyright 2021 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Profiler; |
||||
|
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\Response; |
||||
use OCP\DataCollector\IDataCollector; |
||||
use OCP\Profiler\IProfiler; |
||||
use OCP\Profiler\IProfile; |
||||
use OC\SystemConfig; |
||||
|
||||
class Profiler implements IProfiler { |
||||
/** @var array<string, IDataCollector> */ |
||||
private array $dataCollectors = []; |
||||
|
||||
private ?FileProfilerStorage $storage = null; |
||||
|
||||
private bool $enabled = false; |
||||
|
||||
public function __construct(SystemConfig $config) { |
||||
$this->enabled = $config->getValue('profiler', false); |
||||
if ($this->enabled) { |
||||
$this->storage = new FileProfilerStorage($config->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/profiler'); |
||||
} |
||||
} |
||||
|
||||
public function add(IDataCollector $dataCollector): void { |
||||
$this->dataCollectors[$dataCollector->getName()] = $dataCollector; |
||||
} |
||||
|
||||
public function loadProfileFromResponse(Response $response): ?IProfile { |
||||
if (!$token = $response->getHeaders()['X-Debug-Token']) { |
||||
return null; |
||||
} |
||||
|
||||
return $this->loadProfile($token); |
||||
} |
||||
|
||||
public function loadProfile(string $token): ?IProfile { |
||||
return $this->storage->read($token); |
||||
} |
||||
|
||||
public function saveProfile(IProfile $profile): bool { |
||||
return $this->storage->write($profile); |
||||
} |
||||
|
||||
public function collect(Request $request, Response $response): IProfile { |
||||
$profile = new Profile($request->getId()); |
||||
$profile->setTime(time()); |
||||
$profile->setUrl($request->getRequestUri()); |
||||
$profile->setMethod($request->getMethod()); |
||||
$profile->setStatusCode($response->getStatus()); |
||||
foreach ($this->dataCollectors as $dataCollector) { |
||||
$dataCollector->collect($request, $response, null); |
||||
|
||||
// We clone for subrequests |
||||
$profile->addCollector(clone $dataCollector); |
||||
} |
||||
return $profile; |
||||
} |
||||
|
||||
/** |
||||
* @return array[] |
||||
*/ |
||||
public function find(?string $url, ?int $limit, ?string $method, ?int $start, ?int $end, |
||||
string $statusCode = null): array { |
||||
return $this->storage->find($url, $limit, $method, $start, $end, $statusCode); |
||||
} |
||||
|
||||
public function dataProviders(): array { |
||||
return array_keys($this->dataCollectors); |
||||
} |
||||
|
||||
public function isEnabled(): bool { |
||||
return $this->enabled; |
||||
} |
||||
|
||||
public function setEnabled(bool $enabled): void { |
||||
$this->enabled = $enabled; |
||||
} |
||||
} |
@ -0,0 +1,55 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Profiler; |
||||
|
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\Response; |
||||
use OCP\DataCollector\AbstractDataCollector; |
||||
|
||||
class RoutingDataCollector extends AbstractDataCollector { |
||||
private string $appName; |
||||
private string $controllerName; |
||||
private string $actionName; |
||||
|
||||
public function __construct(string $appName, string $controllerName, string $actionName) { |
||||
$this->appName = $appName; |
||||
$this->controllerName = $controllerName; |
||||
$this->actionName = $actionName; |
||||
} |
||||
|
||||
public function collect(Request $request, Response $response, \Throwable $exception = null): void { |
||||
$this->data = [ |
||||
'appName' => $this->appName, |
||||
'controllerName' => $this->controllerName, |
||||
'actionName' => $this->actionName, |
||||
]; |
||||
} |
||||
|
||||
public function getName(): string { |
||||
return 'router'; |
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* @author Fabien Potencier <fabien@symfony.com> |
||||
* |
||||
* @license AGPL-3.0-or-later AND MIT |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\DataCollector; |
||||
|
||||
/** |
||||
* Children of this class must store the collected data in |
||||
* the data property. |
||||
* |
||||
* @author Fabien Potencier <fabien@symfony.com> |
||||
* @author Bernhard Schussek <bschussek@symfony.com> |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* @since 24.0.0 |
||||
*/ |
||||
abstract class AbstractDataCollector implements IDataCollector, \JsonSerializable { |
||||
/** @var array */ |
||||
protected $data = []; |
||||
|
||||
/** |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getName(): string { |
||||
return static::class; |
||||
} |
||||
|
||||
/** |
||||
* Reset the state of the profiler. By default it only empties the |
||||
* $this->data contents, but you can override this method to do |
||||
* additional cleaning. |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function reset(): void { |
||||
$this->data = []; |
||||
} |
||||
|
||||
/** |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function __sleep(): array { |
||||
return ['data']; |
||||
} |
||||
|
||||
/** |
||||
* @internal to prevent implementing \Serializable |
||||
* @since 24.0.0 |
||||
*/ |
||||
final protected function serialize() { |
||||
} |
||||
|
||||
/** |
||||
* @internal to prevent implementing \Serializable |
||||
* @since 24.0.0 |
||||
*/ |
||||
final protected function unserialize(string $data) { |
||||
} |
||||
|
||||
/** |
||||
* @since 24.0.0 |
||||
*/ |
||||
#[\ReturnTypeWillChange] |
||||
public function jsonSerialize() { |
||||
return $this->data; |
||||
} |
||||
} |
@ -0,0 +1,55 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* @author Fabien Potencier <fabien@symfony.com> |
||||
* |
||||
* @license AGPL-3.0-or-later AND MIT |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\DataCollector; |
||||
|
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\Response; |
||||
|
||||
/** |
||||
* DataCollectorInterface. |
||||
* |
||||
* @since 24.0.0 |
||||
*/ |
||||
interface IDataCollector { |
||||
/** |
||||
* Collects data for the given Request and Response. |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function collect(Request $request, Response $response, \Throwable $exception = null): void; |
||||
|
||||
/** |
||||
* Reset the state of the profiler. |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function reset(): void; |
||||
|
||||
/** |
||||
* Returns the name of the collector. |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getName(): string; |
||||
} |
@ -0,0 +1,168 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\Profiler; |
||||
|
||||
use OCP\DataCollector\IDataCollector; |
||||
|
||||
/** |
||||
* This interface store the results of the profiling of one |
||||
* request. You can get the saved profiles from the @see IProfiler. |
||||
* |
||||
* ```php |
||||
* <?php |
||||
* $profiler = \OC::$server->get(IProfiler::class); |
||||
* $profiles = $profiler->find('/settings/users', 10); |
||||
* ``` |
||||
* |
||||
* This interface is meant to be used directly and not extended. |
||||
* @since 24.0.0 |
||||
*/ |
||||
interface IProfile { |
||||
/** |
||||
* Get the token of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getToken(): string; |
||||
|
||||
/** |
||||
* Set the token of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setToken(string $token): void; |
||||
|
||||
/** |
||||
* Get the time of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getTime(): ?int; |
||||
|
||||
/** |
||||
* Set the time of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setTime(int $time): void; |
||||
|
||||
/** |
||||
* Get the url of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getUrl(): ?string; |
||||
|
||||
/** |
||||
* Set the url of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setUrl(string $url): void; |
||||
|
||||
/** |
||||
* Get the method of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getMethod(): ?string; |
||||
|
||||
/** |
||||
* Set the method of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setMethod(string $method): void; |
||||
|
||||
/** |
||||
* Get the status code of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getStatusCode(): ?int; |
||||
|
||||
/** |
||||
* Set the status code of the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setStatusCode(int $statusCode): void; |
||||
|
||||
/** |
||||
* Add a data collector to the profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function addCollector(IDataCollector $collector); |
||||
|
||||
/** |
||||
* Get the parent profile to this profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getParent(): ?IProfile; |
||||
|
||||
/** |
||||
* Set the parent profile to this profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setParent(?IProfile $parent): void; |
||||
|
||||
/** |
||||
* Get the parent token to this profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getParentToken(): ?string; |
||||
|
||||
/** |
||||
* Get the profile's children |
||||
* @return IProfile[] |
||||
* @since 24.0.0 |
||||
**/ |
||||
public function getChildren(): array; |
||||
|
||||
/** |
||||
* Set the profile's children |
||||
* @param IProfile[] $children |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setChildren(array $children): void; |
||||
|
||||
/** |
||||
* Add the child profile |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function addChild(IProfile $profile): void; |
||||
|
||||
/** |
||||
* Get all the data collectors |
||||
* @return IDataCollector[] |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getCollectors(): array; |
||||
|
||||
/** |
||||
* Set all the data collectors |
||||
* @param IDataCollector[] $collectors |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setCollectors(array $collectors): void; |
||||
|
||||
/** |
||||
* Get a data collector by name |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function getCollector(string $collectorName): ?IDataCollector; |
||||
} |
@ -0,0 +1,101 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright 2022 Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @author Carl Schwan <carl@carlschwan.eu> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\Profiler; |
||||
|
||||
use OC\AppFramework\Http\Request; |
||||
use OCP\AppFramework\Http\Response; |
||||
use OCP\DataCollector\IDataCollector; |
||||
|
||||
/** |
||||
* This interface allows to interact with the built-in Nextcloud profiler. |
||||
* @since 24.0.0 |
||||
*/ |
||||
interface IProfiler { |
||||
/** |
||||
* Add a new data collector to the profiler. This allows to later on |
||||
* collect all the data from every registered collector. |
||||
* |
||||
* @see IDataCollector |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function add(IDataCollector $dataCollector): void; |
||||
|
||||
/** |
||||
* Load a profile from a response object |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function loadProfileFromResponse(Response $response): ?IProfile; |
||||
|
||||
/** |
||||
* Load a profile from the response token |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function loadProfile(string $token): ?IProfile; |
||||
|
||||
/** |
||||
* Save a profile on the disk. This allows to later load it again in the |
||||
* profiler user interface. |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function saveProfile(IProfile $profile): bool; |
||||
|
||||
/** |
||||
* Find a profile from various search parameters |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function find(?string $url, ?int $limit, ?string $method, ?int $start, ?int $end, string $statusCode = null): array; |
||||
|
||||
/** |
||||
* Get the list of data providers by identifier |
||||
* @return string[] |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function dataProviders(): array; |
||||
|
||||
/** |
||||
* Check if the profiler is enabled. |
||||
* |
||||
* If it is not enabled, data provider shouldn't be created and |
||||
* shouldn't collect any data. |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function isEnabled(): bool; |
||||
|
||||
/** |
||||
* Set if the profiler is enabled. |
||||
* @see isEnabled |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function setEnabled(bool $enabled): void; |
||||
|
||||
/** |
||||
* Collect all the information from the current request and construct |
||||
* a IProfile from it. |
||||
* @since 24.0.0 |
||||
*/ |
||||
public function collect(Request $request, Response $response): IProfile; |
||||
} |
Loading…
Reference in new issue