mirror of
https://github.com/nextcloud/server.git
synced 2026-02-03 20:41:22 -05:00
feat(openmetrics): introduce OpenMetrics exporter
Expose a `/metrics` endpoint with some basic metrics Signed-off-by: Benjamin Gaussorgues <benjamin.gaussorgues@nextcloud.com>
This commit is contained in:
parent
c09168e911
commit
c57c4843e8
37 changed files with 1551 additions and 10 deletions
|
|
@ -2892,4 +2892,29 @@ $CONFIG = [
|
|||
* Defaults to `\OC::$SERVERROOT . '/resources/config/ca-bundle.crt'`.
|
||||
*/
|
||||
'default_certificates_bundle_path' => \OC::$SERVERROOT . '/resources/config/ca-bundle.crt',
|
||||
|
||||
/**
|
||||
* OpenMetrics skipped exporters
|
||||
* Allows to skip some exporters in the OpenMetrics endpoint ``/metrics``.
|
||||
*
|
||||
* Default to ``[]`` (empty array)
|
||||
*/
|
||||
'openmetrics_skipped_classes' => [
|
||||
'OC\OpenMetrics\Exporters\FilesByType',
|
||||
'OCA\Files_Sharing\OpenMetrics\SharesCount',
|
||||
],
|
||||
|
||||
/**
|
||||
* OpenMetrics allowed client IP addresses
|
||||
* Restricts the IP addresses able to make requests on the ``/metrics`` endpoint.
|
||||
*
|
||||
* Keep this list as restrictive as possible as metrics can consume a lot of resources.
|
||||
*
|
||||
* Default to ``[127.0.0.0/16', '::1/128]`` (allow loopback interface only)
|
||||
*/
|
||||
'openmetrics_allowed_clients' => [
|
||||
'192.168.0.0/16',
|
||||
'fe80::/10',
|
||||
'10.0.0.1',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
155
core/Controller/OpenMetricsController.php
Normal file
155
core/Controller/OpenMetricsController.php
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Core\Controller;
|
||||
|
||||
use OC\OpenMetrics\ExporterManager;
|
||||
use OC\Security\Ip\Address;
|
||||
use OC\Security\Ip\Range;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
|
||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||
use OCP\AppFramework\Http\Attribute\PublicPage;
|
||||
use OCP\IConfig;
|
||||
use OCP\IRequest;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
use OCP\OpenMetrics\MetricValue;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* OpenMetrics controller
|
||||
*
|
||||
* Gather and display metrics
|
||||
*
|
||||
* @package OC\Core\Controller
|
||||
*/
|
||||
class OpenMetricsController extends Controller {
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
private IConfig $config,
|
||||
private ExporterManager $exporterManager,
|
||||
private LoggerInterface $logger,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
|
||||
#[NoCSRFRequired]
|
||||
#[PublicPage]
|
||||
#[FrontpageRoute(verb: 'GET', url: '/metrics')]
|
||||
public function export(): Http\Response {
|
||||
if (!$this->isRemoteAddressAllowed()) {
|
||||
return new Http\Response(Http::STATUS_FORBIDDEN);
|
||||
}
|
||||
|
||||
return new Http\StreamTraversableResponse(
|
||||
$this->generate(),
|
||||
Http::STATUS_OK,
|
||||
[
|
||||
'Content-Type' => 'application/openmetrics-text; version=1.0.0; charset=utf-8',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function isRemoteAddressAllowed(): bool {
|
||||
$clientAddress = new Address($this->request->getRemoteAddress());
|
||||
$allowedRanges = $this->config->getSystemValue('openmetrics_allowed_clients', ['127.0.0.0/16', '::1/128']);
|
||||
if (!is_array($allowedRanges)) {
|
||||
$this->logger->warning('Invalid configuration for "openmetrics_allowed_clients"');
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($allowedRanges as $range) {
|
||||
$range = new Range($range);
|
||||
if ($range->contains($clientAddress)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function generate(): \Generator {
|
||||
foreach ($this->exporterManager->export() as $family) {
|
||||
yield $this->formatFamily($family);
|
||||
}
|
||||
|
||||
$elapsed = (string)(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']);
|
||||
yield <<<SUMMARY
|
||||
# TYPE nextcloud_exporter_duration gauge
|
||||
# UNIT nextcloud_exporter_duration seconds
|
||||
# HELP nextcloud_exporter_duration Exporter run time
|
||||
nextcloud_exporter_duration $elapsed
|
||||
|
||||
# EOF
|
||||
|
||||
SUMMARY;
|
||||
}
|
||||
|
||||
private function formatFamily(IMetricFamily $family): string {
|
||||
$output = '';
|
||||
$name = $family->name();
|
||||
if ($family->type() !== MetricType::unknown) {
|
||||
$output = '# TYPE nextcloud_' . $name . ' ' . $family->type()->name . "\n";
|
||||
}
|
||||
if ($family->unit() !== '') {
|
||||
$output .= '# UNIT nextcloud_' . $name . ' ' . $family->unit() . "\n";
|
||||
}
|
||||
if ($family->help() !== '') {
|
||||
$output .= '# HELP nextcloud_' . $name . ' ' . $family->help() . "\n";
|
||||
}
|
||||
foreach ($family->metrics() as $metric) {
|
||||
$output .= 'nextcloud_' . $name . $this->formatLabels($metric) . ' ' . $this->formatValue($metric);
|
||||
if ($metric->timestamp !== null) {
|
||||
$output .= ' ' . $this->formatTimestamp($metric);
|
||||
}
|
||||
$output .= "\n";
|
||||
}
|
||||
$output .= "\n";
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
private function formatLabels(Metric $metric): string {
|
||||
if (empty($metric->labels)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$labels = [];
|
||||
foreach ($metric->labels as $label => $value) {
|
||||
$labels[] .= $label . '=' . $this->escapeString((string)$value);
|
||||
}
|
||||
|
||||
return '{' . implode(',', $labels) . '}';
|
||||
}
|
||||
|
||||
private function escapeString(string $string): string {
|
||||
return json_encode(
|
||||
$string,
|
||||
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
private function formatValue(Metric $metric): string {
|
||||
if (is_bool($metric->value)) {
|
||||
return $metric->value ? '1' : '0';
|
||||
}
|
||||
if ($metric->value instanceof MetricValue) {
|
||||
return $metric->value->value;
|
||||
}
|
||||
|
||||
return (string)$metric->value;
|
||||
}
|
||||
|
||||
private function formatTimestamp(Metric $metric): string {
|
||||
return (string)$metric->timestamp;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
|
@ -17,3 +18,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ return array(
|
|||
'OCP\\AppFramework\\Http\\Response' => $baseDir . '/lib/public/AppFramework/Http/Response.php',
|
||||
'OCP\\AppFramework\\Http\\StandaloneTemplateResponse' => $baseDir . '/lib/public/AppFramework/Http/StandaloneTemplateResponse.php',
|
||||
'OCP\\AppFramework\\Http\\StreamResponse' => $baseDir . '/lib/public/AppFramework/Http/StreamResponse.php',
|
||||
'OCP\\AppFramework\\Http\\StreamTraversableResponse' => $baseDir . '/lib/public/AppFramework/Http/StreamTraversableResponse.php',
|
||||
'OCP\\AppFramework\\Http\\StrictContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/StrictContentSecurityPolicy.php',
|
||||
'OCP\\AppFramework\\Http\\StrictEvalContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/StrictEvalContentSecurityPolicy.php',
|
||||
'OCP\\AppFramework\\Http\\StrictInlineContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/StrictInlineContentSecurityPolicy.php',
|
||||
|
|
@ -724,6 +725,10 @@ return array(
|
|||
'OCP\\OCM\\IOCMProvider' => $baseDir . '/lib/public/OCM/IOCMProvider.php',
|
||||
'OCP\\OCM\\IOCMResource' => $baseDir . '/lib/public/OCM/IOCMResource.php',
|
||||
'OCP\\OCS\\IDiscoveryService' => $baseDir . '/lib/public/OCS/IDiscoveryService.php',
|
||||
'OCP\\OpenMetrics\\IMetricFamily' => $baseDir . '/lib/public/OpenMetrics/IMetricFamily.php',
|
||||
'OCP\\OpenMetrics\\Metric' => $baseDir . '/lib/public/OpenMetrics/Metric.php',
|
||||
'OCP\\OpenMetrics\\MetricType' => $baseDir . '/lib/public/OpenMetrics/MetricType.php',
|
||||
'OCP\\OpenMetrics\\MetricValue' => $baseDir . '/lib/public/OpenMetrics/MetricValue.php',
|
||||
'OCP\\PreConditionNotMetException' => $baseDir . '/lib/public/PreConditionNotMetException.php',
|
||||
'OCP\\Preview\\BeforePreviewFetchedEvent' => $baseDir . '/lib/public/Preview/BeforePreviewFetchedEvent.php',
|
||||
'OCP\\Preview\\IMimeIconProvider' => $baseDir . '/lib/public/Preview/IMimeIconProvider.php',
|
||||
|
|
@ -1422,6 +1427,7 @@ return array(
|
|||
'OC\\Core\\Controller\\OCJSController' => $baseDir . '/core/Controller/OCJSController.php',
|
||||
'OC\\Core\\Controller\\OCMController' => $baseDir . '/core/Controller/OCMController.php',
|
||||
'OC\\Core\\Controller\\OCSController' => $baseDir . '/core/Controller/OCSController.php',
|
||||
'OC\\Core\\Controller\\OpenMetricsController' => $baseDir . '/core/Controller/OpenMetricsController.php',
|
||||
'OC\\Core\\Controller\\PreviewController' => $baseDir . '/core/Controller/PreviewController.php',
|
||||
'OC\\Core\\Controller\\ProfileApiController' => $baseDir . '/core/Controller/ProfileApiController.php',
|
||||
'OC\\Core\\Controller\\RecommendedAppsController' => $baseDir . '/core/Controller/RecommendedAppsController.php',
|
||||
|
|
@ -1895,6 +1901,17 @@ return array(
|
|||
'OC\\OCS\\CoreCapabilities' => $baseDir . '/lib/private/OCS/CoreCapabilities.php',
|
||||
'OC\\OCS\\DiscoveryService' => $baseDir . '/lib/private/OCS/DiscoveryService.php',
|
||||
'OC\\OCS\\Provider' => $baseDir . '/lib/private/OCS/Provider.php',
|
||||
'OC\\OpenMetrics\\ExporterManager' => $baseDir . '/lib/private/OpenMetrics/ExporterManager.php',
|
||||
'OC\\OpenMetrics\\Exporters\\ActiveSessions' => $baseDir . '/lib/private/OpenMetrics/Exporters/ActiveSessions.php',
|
||||
'OC\\OpenMetrics\\Exporters\\ActiveUsers' => $baseDir . '/lib/private/OpenMetrics/Exporters/ActiveUsers.php',
|
||||
'OC\\OpenMetrics\\Exporters\\AppsCount' => $baseDir . '/lib/private/OpenMetrics/Exporters/AppsCount.php',
|
||||
'OC\\OpenMetrics\\Exporters\\AppsInfo' => $baseDir . '/lib/private/OpenMetrics/Exporters/AppsInfo.php',
|
||||
'OC\\OpenMetrics\\Exporters\\Cached' => $baseDir . '/lib/private/OpenMetrics/Exporters/Cached.php',
|
||||
'OC\\OpenMetrics\\Exporters\\FilesByType' => $baseDir . '/lib/private/OpenMetrics/Exporters/FilesByType.php',
|
||||
'OC\\OpenMetrics\\Exporters\\InstanceInfo' => $baseDir . '/lib/private/OpenMetrics/Exporters/InstanceInfo.php',
|
||||
'OC\\OpenMetrics\\Exporters\\Maintenance' => $baseDir . '/lib/private/OpenMetrics/Exporters/Maintenance.php',
|
||||
'OC\\OpenMetrics\\Exporters\\RunningJobs' => $baseDir . '/lib/private/OpenMetrics/Exporters/RunningJobs.php',
|
||||
'OC\\OpenMetrics\\Exporters\\UsersByBackend' => $baseDir . '/lib/private/OpenMetrics/Exporters/UsersByBackend.php',
|
||||
'OC\\PhoneNumberUtil' => $baseDir . '/lib/private/PhoneNumberUtil.php',
|
||||
'OC\\PreviewManager' => $baseDir . '/lib/private/PreviewManager.php',
|
||||
'OC\\PreviewNotAvailableException' => $baseDir . '/lib/private/PreviewNotAvailableException.php',
|
||||
|
|
|
|||
|
|
@ -163,6 +163,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OCP\\AppFramework\\Http\\Response' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Response.php',
|
||||
'OCP\\AppFramework\\Http\\StandaloneTemplateResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StandaloneTemplateResponse.php',
|
||||
'OCP\\AppFramework\\Http\\StreamResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StreamResponse.php',
|
||||
'OCP\\AppFramework\\Http\\StreamTraversableResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StreamTraversableResponse.php',
|
||||
'OCP\\AppFramework\\Http\\StrictContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StrictContentSecurityPolicy.php',
|
||||
'OCP\\AppFramework\\Http\\StrictEvalContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StrictEvalContentSecurityPolicy.php',
|
||||
'OCP\\AppFramework\\Http\\StrictInlineContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/StrictInlineContentSecurityPolicy.php',
|
||||
|
|
@ -765,6 +766,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OCP\\OCM\\IOCMProvider' => __DIR__ . '/../../..' . '/lib/public/OCM/IOCMProvider.php',
|
||||
'OCP\\OCM\\IOCMResource' => __DIR__ . '/../../..' . '/lib/public/OCM/IOCMResource.php',
|
||||
'OCP\\OCS\\IDiscoveryService' => __DIR__ . '/../../..' . '/lib/public/OCS/IDiscoveryService.php',
|
||||
'OCP\\OpenMetrics\\IMetricFamily' => __DIR__ . '/../../..' . '/lib/public/OpenMetrics/IMetricFamily.php',
|
||||
'OCP\\OpenMetrics\\Metric' => __DIR__ . '/../../..' . '/lib/public/OpenMetrics/Metric.php',
|
||||
'OCP\\OpenMetrics\\MetricType' => __DIR__ . '/../../..' . '/lib/public/OpenMetrics/MetricType.php',
|
||||
'OCP\\OpenMetrics\\MetricValue' => __DIR__ . '/../../..' . '/lib/public/OpenMetrics/MetricValue.php',
|
||||
'OCP\\PreConditionNotMetException' => __DIR__ . '/../../..' . '/lib/public/PreConditionNotMetException.php',
|
||||
'OCP\\Preview\\BeforePreviewFetchedEvent' => __DIR__ . '/../../..' . '/lib/public/Preview/BeforePreviewFetchedEvent.php',
|
||||
'OCP\\Preview\\IMimeIconProvider' => __DIR__ . '/../../..' . '/lib/public/Preview/IMimeIconProvider.php',
|
||||
|
|
@ -1463,6 +1468,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Core\\Controller\\OCJSController' => __DIR__ . '/../../..' . '/core/Controller/OCJSController.php',
|
||||
'OC\\Core\\Controller\\OCMController' => __DIR__ . '/../../..' . '/core/Controller/OCMController.php',
|
||||
'OC\\Core\\Controller\\OCSController' => __DIR__ . '/../../..' . '/core/Controller/OCSController.php',
|
||||
'OC\\Core\\Controller\\OpenMetricsController' => __DIR__ . '/../../..' . '/core/Controller/OpenMetricsController.php',
|
||||
'OC\\Core\\Controller\\PreviewController' => __DIR__ . '/../../..' . '/core/Controller/PreviewController.php',
|
||||
'OC\\Core\\Controller\\ProfileApiController' => __DIR__ . '/../../..' . '/core/Controller/ProfileApiController.php',
|
||||
'OC\\Core\\Controller\\RecommendedAppsController' => __DIR__ . '/../../..' . '/core/Controller/RecommendedAppsController.php',
|
||||
|
|
@ -1936,6 +1942,17 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\OCS\\CoreCapabilities' => __DIR__ . '/../../..' . '/lib/private/OCS/CoreCapabilities.php',
|
||||
'OC\\OCS\\DiscoveryService' => __DIR__ . '/../../..' . '/lib/private/OCS/DiscoveryService.php',
|
||||
'OC\\OCS\\Provider' => __DIR__ . '/../../..' . '/lib/private/OCS/Provider.php',
|
||||
'OC\\OpenMetrics\\ExporterManager' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/ExporterManager.php',
|
||||
'OC\\OpenMetrics\\Exporters\\ActiveSessions' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/ActiveSessions.php',
|
||||
'OC\\OpenMetrics\\Exporters\\ActiveUsers' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/ActiveUsers.php',
|
||||
'OC\\OpenMetrics\\Exporters\\AppsCount' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/AppsCount.php',
|
||||
'OC\\OpenMetrics\\Exporters\\AppsInfo' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/AppsInfo.php',
|
||||
'OC\\OpenMetrics\\Exporters\\Cached' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/Cached.php',
|
||||
'OC\\OpenMetrics\\Exporters\\FilesByType' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/FilesByType.php',
|
||||
'OC\\OpenMetrics\\Exporters\\InstanceInfo' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/InstanceInfo.php',
|
||||
'OC\\OpenMetrics\\Exporters\\Maintenance' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/Maintenance.php',
|
||||
'OC\\OpenMetrics\\Exporters\\RunningJobs' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/RunningJobs.php',
|
||||
'OC\\OpenMetrics\\Exporters\\UsersByBackend' => __DIR__ . '/../../..' . '/lib/private/OpenMetrics/Exporters/UsersByBackend.php',
|
||||
'OC\\PhoneNumberUtil' => __DIR__ . '/../../..' . '/lib/private/PhoneNumberUtil.php',
|
||||
'OC\\PreviewManager' => __DIR__ . '/../../..' . '/lib/private/PreviewManager.php',
|
||||
'OC\\PreviewNotAvailableException' => __DIR__ . '/../../..' . '/lib/private/PreviewNotAvailableException.php',
|
||||
|
|
|
|||
94
lib/private/OpenMetrics/ExporterManager.php
Normal file
94
lib/private/OpenMetrics/ExporterManager.php
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics;
|
||||
|
||||
use Generator;
|
||||
use OC\Log\PsrLoggerAdapter;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\IConfig;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\Server;
|
||||
|
||||
class ExporterManager {
|
||||
private array $skippedClasses;
|
||||
private const XML_ENTRY = 'openmetrics';
|
||||
|
||||
public function __construct(
|
||||
private IAppManager $appManager,
|
||||
private PsrLoggerAdapter $logger,
|
||||
IConfig $config,
|
||||
) {
|
||||
// Use values as keys for faster lookups
|
||||
$this->skippedClasses = array_fill_keys($config->getSystemValue('openmetrics_skipped_classes', []), true);
|
||||
}
|
||||
|
||||
public function export(): Generator {
|
||||
// Core exporters
|
||||
$exporters = [
|
||||
// Basic exporters
|
||||
Exporters\InstanceInfo::class,
|
||||
Exporters\AppsInfo::class,
|
||||
Exporters\AppsCount::class,
|
||||
Exporters\Maintenance::class,
|
||||
|
||||
// File exporters
|
||||
Exporters\FilesByType::class,
|
||||
|
||||
// Users exporters
|
||||
Exporters\ActiveUsers::class,
|
||||
Exporters\ActiveSessions::class,
|
||||
Exporters\UsersByBackend::class,
|
||||
|
||||
// Jobs
|
||||
Exporters\RunningJobs::class,
|
||||
];
|
||||
$exporters = array_filter($exporters, fn ($classname) => !isset($this->skippedClasses[$classname]));
|
||||
foreach ($exporters as $classname) {
|
||||
$exporter = $this->loadExporter($classname);
|
||||
if ($exporter !== null) {
|
||||
yield $exporter;
|
||||
}
|
||||
}
|
||||
|
||||
// Apps exporters
|
||||
foreach ($this->appManager->getEnabledApps() as $appId) {
|
||||
$appInfo = $this->appManager->getAppInfo($appId);
|
||||
if (!isset($appInfo[self::XML_ENTRY]) || !is_array($appInfo[self::XML_ENTRY])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($appInfo[self::XML_ENTRY] as $classname) {
|
||||
if (isset($this->skippedClasses[$classname])) {
|
||||
continue;
|
||||
}
|
||||
$exporter = $this->loadExporter($classname, $appId);
|
||||
if ($exporter !== null) {
|
||||
yield $exporter;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function loadExporter(string $classname, string $appId = 'core'): ?IMetricFamily {
|
||||
try {
|
||||
return Server::get($classname);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error(
|
||||
'Unable to build exporter {exporter}',
|
||||
[
|
||||
'app' => $appId,
|
||||
'exception' => $e,
|
||||
'exporter' => $classname,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
58
lib/private/OpenMetrics/Exporters/ActiveSessions.php
Normal file
58
lib/private/OpenMetrics/Exporters/ActiveSessions.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
|
||||
class ActiveSessions implements IMetricFamily {
|
||||
public function __construct(
|
||||
private IDBConnection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
public function name(): string {
|
||||
return 'active_sessions';
|
||||
}
|
||||
|
||||
public function type(): MetricType {
|
||||
return MetricType::gauge;
|
||||
}
|
||||
|
||||
public function unit(): string {
|
||||
return 'sessions';
|
||||
}
|
||||
|
||||
public function help(): string {
|
||||
return 'Number of active sessions';
|
||||
}
|
||||
|
||||
public function metrics(): Generator {
|
||||
$now = time();
|
||||
$timeFrames = [
|
||||
'Last 5 minutes' => $now - 5 * 60,
|
||||
'Last 15 minutes' => $now - 15 * 60,
|
||||
'Last hour' => $now - 60 * 60,
|
||||
'Last day' => $now - 24 * 60 * 60,
|
||||
];
|
||||
foreach ($timeFrames as $label => $time) {
|
||||
$queryBuilder = $this->connection->getQueryBuilder();
|
||||
$result = $queryBuilder->select($queryBuilder->func()->count('*'))
|
||||
->from('authtoken')
|
||||
->where($queryBuilder->expr()->gte('last_activity', $queryBuilder->createNamedParameter($time)))
|
||||
->executeQuery();
|
||||
|
||||
yield new Metric((int)$result->fetchOne(), ['time' => $label]);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
lib/private/OpenMetrics/Exporters/ActiveUsers.php
Normal file
58
lib/private/OpenMetrics/Exporters/ActiveUsers.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
|
||||
class ActiveUsers implements IMetricFamily {
|
||||
public function __construct(
|
||||
private IDBConnection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
public function name(): string {
|
||||
return 'active_users';
|
||||
}
|
||||
|
||||
public function type(): MetricType {
|
||||
return MetricType::gauge;
|
||||
}
|
||||
|
||||
public function unit(): string {
|
||||
return 'users';
|
||||
}
|
||||
|
||||
public function help(): string {
|
||||
return 'Number of active users';
|
||||
}
|
||||
|
||||
public function metrics(): Generator {
|
||||
$now = time();
|
||||
$timeFrames = [
|
||||
'Last 5 minutes' => $now - 5 * 60,
|
||||
'Last 15 minutes' => $now - 15 * 60,
|
||||
'Last hour' => $now - 60 * 60,
|
||||
'Last day' => $now - 24 * 60 * 60,
|
||||
];
|
||||
foreach ($timeFrames as $label => $time) {
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$result = $qb->select($qb->createFunction('COUNT(DISTINCT ' . $qb->getColumnName('uid') . ')'))
|
||||
->from('authtoken')
|
||||
->where($qb->expr()->gte('last_activity', $qb->createNamedParameter($time)))
|
||||
->executeQuery();
|
||||
|
||||
yield new Metric((int)$result->fetchOne(), ['time' => $label]);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
lib/private/OpenMetrics/Exporters/AppsCount.php
Normal file
62
lib/private/OpenMetrics/Exporters/AppsCount.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Export statistics about apps
|
||||
*/
|
||||
class AppsCount implements IMetricFamily {
|
||||
public function __construct(
|
||||
private IAppManager $appManager,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function name(): string {
|
||||
return 'apps_count';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function type(): MetricType {
|
||||
return MetricType::gauge;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unit(): string {
|
||||
return 'applications';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function help(): string {
|
||||
return 'Number of apps in Nextcloud';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function metrics(): Generator {
|
||||
$installedAppsCount = count($this->appManager->getAppInstalledVersions(false));
|
||||
$enabledAppsCount = count($this->appManager->getEnabledApps());
|
||||
$disabledAppsCount = $installedAppsCount - $enabledAppsCount;
|
||||
yield new Metric(
|
||||
$disabledAppsCount,
|
||||
['status' => 'disabled'],
|
||||
);
|
||||
yield new Metric(
|
||||
$enabledAppsCount,
|
||||
['status' => 'enabled'],
|
||||
);
|
||||
}
|
||||
}
|
||||
56
lib/private/OpenMetrics/Exporters/AppsInfo.php
Normal file
56
lib/private/OpenMetrics/Exporters/AppsInfo.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Export information about enabled applications
|
||||
*/
|
||||
class AppsInfo implements IMetricFamily {
|
||||
public function __construct(
|
||||
private IAppManager $appManager,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function name(): string {
|
||||
return 'apps_info';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function type(): MetricType {
|
||||
return MetricType::info;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unit(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function help(): string {
|
||||
return 'Enabled applications in Nextcloud';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function metrics(): Generator {
|
||||
yield new Metric(
|
||||
1,
|
||||
$this->appManager->getAppInstalledVersions(true),
|
||||
time()
|
||||
);
|
||||
}
|
||||
}
|
||||
58
lib/private/OpenMetrics/Exporters/Cached.php
Normal file
58
lib/private/OpenMetrics/Exporters/Cached.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Cached metrics
|
||||
*/
|
||||
abstract class Cached implements IMetricFamily {
|
||||
private readonly ICache $cache;
|
||||
|
||||
public function __construct(
|
||||
ICacheFactory $cacheFactory,
|
||||
) {
|
||||
$this->cache = $cacheFactory->createDistributed('openmetrics');
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of seconds to keep the results
|
||||
*/
|
||||
abstract public function getTTL(): int;
|
||||
|
||||
/**
|
||||
* Actually gather the metrics
|
||||
*
|
||||
* @see metrics
|
||||
*/
|
||||
abstract public function gatherMetrics(): Generator;
|
||||
|
||||
#[Override]
|
||||
public function metrics(): Generator {
|
||||
$cacheKey = static::class;
|
||||
if ($data = $this->cache->get($cacheKey)) {
|
||||
yield from unserialize($data);
|
||||
return;
|
||||
}
|
||||
|
||||
$data = [];
|
||||
foreach ($this->gatherMetrics() as $metric) {
|
||||
yield $metric;
|
||||
$data[] = $metric;
|
||||
}
|
||||
|
||||
$this->cache->set($cacheKey, serialize($data), $this->getTTL());
|
||||
}
|
||||
}
|
||||
80
lib/private/OpenMetrics/Exporters/FilesByType.php
Normal file
80
lib/private/OpenMetrics/Exporters/FilesByType.php
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OCP\Files\IMimeTypeLoader;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Export files count
|
||||
*
|
||||
* Cached exporter, refreshed every 30 minutes
|
||||
*/
|
||||
class FilesByType extends Cached {
|
||||
public function __construct(
|
||||
ICacheFactory $cacheFactory,
|
||||
private IDBConnection $connection,
|
||||
private IMimeTypeLoader $mimetypeLoader,
|
||||
) {
|
||||
parent::__construct($cacheFactory);
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function name(): string {
|
||||
return 'files';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function type(): MetricType {
|
||||
return MetricType::gauge;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unit(): string {
|
||||
return 'files';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function help(): string {
|
||||
return 'Number of files by type';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function getTTL(): int {
|
||||
return 30 * 60;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function gatherMetrics(): Generator {
|
||||
$qb = $this->connection->getQueryBuilder()->runAcrossAllShards();
|
||||
$metrics = $qb->select('mimetype', $qb->func()->count('*', 'count'))
|
||||
->from('filecache')
|
||||
->groupBy('mimetype')
|
||||
->executeQuery();
|
||||
|
||||
if ($metrics->rowCount() === 0) {
|
||||
yield new Metric(0);
|
||||
return;
|
||||
}
|
||||
$now = time();
|
||||
foreach ($metrics->iterateAssociative() as $count) {
|
||||
yield new Metric(
|
||||
$count['count'],
|
||||
['mimetype' => $this->mimetypeLoader->getMimetypeById($count['mimetype'])],
|
||||
$now,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
63
lib/private/OpenMetrics/Exporters/InstanceInfo.php
Normal file
63
lib/private/OpenMetrics/Exporters/InstanceInfo.php
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OC\SystemConfig;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
use OCP\ServerVersion;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Export some basic information about current instance
|
||||
*/
|
||||
class InstanceInfo implements IMetricFamily {
|
||||
public function __construct(
|
||||
private SystemConfig $systemConfig,
|
||||
private ServerVersion $serverVersion,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function name(): string {
|
||||
return 'instance_info';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function type(): MetricType {
|
||||
return MetricType::info;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unit(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function help(): string {
|
||||
return 'Basic information about Nextcloud';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function metrics(): Generator {
|
||||
yield new Metric(
|
||||
1,
|
||||
[
|
||||
'full version' => $this->serverVersion->getHumanVersion(),
|
||||
'major version' => (string)$this->serverVersion->getVersion()[0],
|
||||
'build' => $this->serverVersion->getBuild(),
|
||||
'installed' => $this->systemConfig->getValue('installed', false) ? '1' : '0',
|
||||
],
|
||||
time()
|
||||
);
|
||||
}
|
||||
}
|
||||
50
lib/private/OpenMetrics/Exporters/Maintenance.php
Normal file
50
lib/private/OpenMetrics/Exporters/Maintenance.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OC\SystemConfig;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
use OCP\Server;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Export maintenance state
|
||||
*/
|
||||
class Maintenance implements IMetricFamily {
|
||||
public function name(): string {
|
||||
return 'maintenance';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function type(): MetricType {
|
||||
return MetricType::info;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unit(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function help(): string {
|
||||
return 'Maintenance status of Nextcloud';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function metrics(): Generator {
|
||||
$systemConfig = Server::get(SystemConfig::class);
|
||||
yield new Metric(
|
||||
(bool)$systemConfig->getValue('maintenance', false)
|
||||
);
|
||||
}
|
||||
}
|
||||
67
lib/private/OpenMetrics/Exporters/RunningJobs.php
Normal file
67
lib/private/OpenMetrics/Exporters/RunningJobs.php
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Export the number of running jobs by type
|
||||
*/
|
||||
class RunningJobs implements IMetricFamily {
|
||||
public function __construct(
|
||||
private IDBConnection $connection,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function name(): string {
|
||||
return 'jobs_running';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function type(): MetricType {
|
||||
return MetricType::gauge;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unit(): string {
|
||||
return 'jobs';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function help(): string {
|
||||
return 'Number of running jobs';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function metrics(): Generator {
|
||||
$qb = $this->connection->getQueryBuilder();
|
||||
$result = $qb->select($qb->func()->count('*', 'nb'), 'class')
|
||||
->from('jobs')
|
||||
->where($qb->expr()->gt('reserved_at', $qb->createNamedParameter(0)))
|
||||
->groupBy('class')
|
||||
->executeQuery();
|
||||
|
||||
// If no result, return a metric with count '0'
|
||||
if ($result->rowCount() === 0) {
|
||||
yield new Metric(0);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($result->iterateAssociative() as $row) {
|
||||
yield new Metric($row['nb'], ['class' => $row['class']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
55
lib/private/OpenMetrics/Exporters/UsersByBackend.php
Normal file
55
lib/private/OpenMetrics/Exporters/UsersByBackend.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\OpenMetrics\Exporters;
|
||||
|
||||
use Generator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\OpenMetrics\Metric;
|
||||
use OCP\OpenMetrics\MetricType;
|
||||
use Override;
|
||||
|
||||
/**
|
||||
* Count users of each backend which supports it (mapped users only)
|
||||
*/
|
||||
class UsersByBackend implements IMetricFamily {
|
||||
public function __construct(
|
||||
private IUserManager $userManager,
|
||||
) {
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function name(): string {
|
||||
return 'users';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function type(): MetricType {
|
||||
return MetricType::gauge;
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function unit(): string {
|
||||
return 'users';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function help(): string {
|
||||
return 'Number of users by backend';
|
||||
}
|
||||
|
||||
#[Override]
|
||||
public function metrics(): Generator {
|
||||
$userCounts = $this->userManager->countUsers(true);
|
||||
foreach ($userCounts as $backend => $count) {
|
||||
yield new Metric($count, ['backend' => $backend]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -443,22 +443,23 @@ class Manager extends PublicEmitter implements IUserManager {
|
|||
/**
|
||||
* returns how many users per backend exist (if supported by backend)
|
||||
*
|
||||
* @param boolean $hasLoggedIn when true only users that have a lastLogin
|
||||
* entry in the preferences table will be affected
|
||||
* @return array<string, int> an array of backend class as key and count number as value
|
||||
*/
|
||||
public function countUsers() {
|
||||
public function countUsers(bool $onlyMappedUsers = false) {
|
||||
$userCountStatistics = [];
|
||||
foreach ($this->backends as $backend) {
|
||||
$name = $backend instanceof IUserBackend
|
||||
? $backend->getBackendName()
|
||||
: get_class($backend);
|
||||
|
||||
if ($onlyMappedUsers && $backend instanceof ICountMappedUsersBackend) {
|
||||
$userCountStatistics[$name] = $backend->countMappedUsers();
|
||||
continue;
|
||||
}
|
||||
if ($backend instanceof ICountUsersBackend || $backend->implementsActions(Backend::COUNT_USERS)) {
|
||||
/** @var ICountUsersBackend|IUserBackend $backend */
|
||||
$backendUsers = $backend->countUsers();
|
||||
if ($backendUsers !== false) {
|
||||
if ($backend instanceof IUserBackend) {
|
||||
$name = $backend->getBackendName();
|
||||
} else {
|
||||
$name = get_class($backend);
|
||||
}
|
||||
if (isset($userCountStatistics[$name])) {
|
||||
$userCountStatistics[$name] += $backendUsers;
|
||||
} else {
|
||||
|
|
@ -467,6 +468,7 @@ class Manager extends PublicEmitter implements IUserManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $userCountStatistics;
|
||||
}
|
||||
|
||||
|
|
|
|||
51
lib/public/AppFramework/Http/StreamTraversableResponse.php
Normal file
51
lib/public/AppFramework/Http/StreamTraversableResponse.php
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OCP\AppFramework\Http;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
use Override;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Class StreamResponse
|
||||
*
|
||||
* @since 33.0.0
|
||||
* @template S of Http::STATUS_*
|
||||
* @template H of array<string, mixed>
|
||||
* @template-extends Response<Http::STATUS_*, array<string, mixed>>
|
||||
*/
|
||||
class StreamTraversableResponse extends Response implements ICallbackResponse {
|
||||
/**
|
||||
* @param S $status
|
||||
* @param H $headers
|
||||
* @since 33.0.0
|
||||
*/
|
||||
public function __construct(
|
||||
private Traversable $generator,
|
||||
int $status = Http::STATUS_OK,
|
||||
array $headers = [],
|
||||
) {
|
||||
parent::__construct($status, $headers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Streams the generator output
|
||||
*
|
||||
* @param IOutput $output a small wrapper that handles output
|
||||
* @since 33.0.0
|
||||
*/
|
||||
#[Override]
|
||||
public function callback(IOutput $output): void {
|
||||
foreach ($this->generator as $content) {
|
||||
$output->setOutput($content);
|
||||
flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -162,8 +162,9 @@ interface IUserManager {
|
|||
*
|
||||
* @return array<string, int> an array of backend class name as key and count number as value
|
||||
* @since 8.0.0
|
||||
* @since 33.0.0 $onlyMappedUsers parameter
|
||||
*/
|
||||
public function countUsers();
|
||||
public function countUsers(bool $onlyMappedUsers = false);
|
||||
|
||||
/**
|
||||
* Get how many users exists in total, whithin limit
|
||||
|
|
|
|||
53
lib/public/OpenMetrics/IMetricFamily.php
Normal file
53
lib/public/OpenMetrics/IMetricFamily.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCP\OpenMetrics;
|
||||
|
||||
use Generator;
|
||||
use OCP\AppFramework\Attribute\Implementable;
|
||||
|
||||
/**
|
||||
* @since 33.0.0
|
||||
*/
|
||||
#[Implementable(since: '33.0.0')]
|
||||
interface IMetricFamily {
|
||||
/**
|
||||
* Family name (will be prefixed by nextcloud_)
|
||||
*
|
||||
* @since 33.0.0
|
||||
*/
|
||||
public function name(): string;
|
||||
|
||||
/**
|
||||
* Family metric type
|
||||
*
|
||||
* @since 33.0.0
|
||||
*/
|
||||
public function type(): MetricType;
|
||||
|
||||
/**
|
||||
* Family unit (can be empty string)
|
||||
* @since 33.0.0
|
||||
*/
|
||||
public function unit(): string;
|
||||
|
||||
/**
|
||||
* Family help text (can be empty string)
|
||||
* @since 33.0.0
|
||||
*/
|
||||
public function help(): string;
|
||||
|
||||
/**
|
||||
* List of metrics
|
||||
*
|
||||
* @return Generator<Metric>
|
||||
* @since 33.0.0
|
||||
*/
|
||||
public function metrics(): Generator;
|
||||
}
|
||||
27
lib/public/OpenMetrics/Metric.php
Normal file
27
lib/public/OpenMetrics/Metric.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCP\OpenMetrics;
|
||||
|
||||
/**
|
||||
* @since 33.0.0
|
||||
*/
|
||||
final readonly class Metric {
|
||||
public function __construct(
|
||||
public int|float|bool|MetricValue $value = false,
|
||||
/** @var string[] */
|
||||
public array $labels = [],
|
||||
public int|float|null $timestamp = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function label(string $name): ?string {
|
||||
return $this->labels[$name] ?? null;
|
||||
}
|
||||
}
|
||||
26
lib/public/OpenMetrics/MetricType.php
Normal file
26
lib/public/OpenMetrics/MetricType.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCP\OpenMetrics;
|
||||
|
||||
/**
|
||||
* Metrics types
|
||||
*
|
||||
* @since 33.0.0
|
||||
*/
|
||||
enum MetricType {
|
||||
case counter;
|
||||
case gauge;
|
||||
case histogram;
|
||||
case gaugehistogram;
|
||||
case stateset;
|
||||
case info;
|
||||
case summary;
|
||||
case unknown;
|
||||
}
|
||||
20
lib/public/OpenMetrics/MetricValue.php
Normal file
20
lib/public/OpenMetrics/MetricValue.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCP\OpenMetrics;
|
||||
|
||||
/**
|
||||
* Special values for metrics
|
||||
* @since 33.0.0
|
||||
*/
|
||||
enum MetricValue: string {
|
||||
case NOT_A_NUMBER = 'NaN';
|
||||
case POSITIVE_INFINITY = '+Inf';
|
||||
case NEGATIVE_INFINITY = '-Inf';
|
||||
}
|
||||
|
|
@ -16970,7 +16970,8 @@
|
|||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"fileId"
|
||||
"fileId",
|
||||
"expirationTime"
|
||||
],
|
||||
"properties": {
|
||||
"fileId": {
|
||||
|
|
|
|||
78
tests/Core/Controller/OpenMetricsControllerTest.php
Normal file
78
tests/Core/Controller/OpenMetricsControllerTest.php
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Tests\Core\Controller;
|
||||
|
||||
use OC\Core\Controller\OpenMetricsController;
|
||||
use OC\OpenMetrics\ExporterManager;
|
||||
use OCP\AppFramework\Http\IOutput;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\AppFramework\Http\StreamTraversableResponse;
|
||||
use OCP\IConfig;
|
||||
use OCP\IRequest;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class OpenMetricsControllerTest extends TestCase {
|
||||
private IRequest&MockObject $request;
|
||||
private IConfig&MockObject $config;
|
||||
private ExporterManager&MockObject $exporterManager;
|
||||
private LoggerInterface&MockObject $logger;
|
||||
private OpenMetricsController $controller;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->request = $this->createMock(IRequest::class);
|
||||
$this->request->method('getRemoteAddress')
|
||||
->willReturn('192.168.1.1');
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->exporterManager = $this->createMock(ExporterManager::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->controller = new OpenMetricsController('core', $this->request, $this->config, $this->exporterManager, $this->logger);
|
||||
}
|
||||
|
||||
public function testGetMetrics(): void {
|
||||
$output = $this->createMock(IOutput::class);
|
||||
$fullOutput = '';
|
||||
$output->method('setOutput')
|
||||
->willReturnCallback(function ($output) use (&$fullOutput) {
|
||||
$fullOutput .= $output;
|
||||
});
|
||||
$this->config->expects($this->once())
|
||||
->method('getSystemValue')
|
||||
->with('openmetrics_allowed_clients')
|
||||
->willReturn(['192.168.0.0/16']);
|
||||
$response = $this->controller->export();
|
||||
$this->assertInstanceOf(StreamTraversableResponse::class, $response);
|
||||
$this->assertEquals('200', $response->getStatus());
|
||||
$this->assertEquals('application/openmetrics-text; version=1.0.0; charset=utf-8', $response->getHeaders()['Content-Type']);
|
||||
$expected = <<<EXPECTED
|
||||
# TYPE nextcloud_exporter_duration gauge
|
||||
# UNIT nextcloud_exporter_duration seconds
|
||||
# HELP nextcloud_exporter_duration Exporter run time
|
||||
nextcloud_exporter_duration %f
|
||||
|
||||
# EOF
|
||||
|
||||
EXPECTED;
|
||||
$response->callback($output);
|
||||
$this->assertStringMatchesFormat($expected, $fullOutput);
|
||||
}
|
||||
|
||||
public function testGetMetricsFromForbiddenIp(): void {
|
||||
$this->config->expects($this->once())
|
||||
->method('getSystemValue')
|
||||
->with('openmetrics_allowed_clients')
|
||||
->willReturn(['1.2.3.4']);
|
||||
$response = $this->controller->export();
|
||||
$this->assertInstanceOf(Response::class, $response);
|
||||
$this->assertEquals('403', $response->getStatus());
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ namespace Test;
|
|||
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\Server;
|
||||
|
||||
/**
|
||||
|
|
@ -130,5 +131,14 @@ class InfoXmlTest extends TestCase {
|
|||
$this->assertInstanceOf($command, Server::get($command));
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($appInfo['openmetrics'])) {
|
||||
foreach ($appInfo['openmetrics'] as $class) {
|
||||
$this->assertTrue(class_exists($class), 'Asserting exporter "' . $class . '"exists');
|
||||
$exporter = Server::get($class);
|
||||
$this->assertInstanceOf($class, $exporter);
|
||||
$this->assertInstanceOf(IMetricFamily::class, $exporter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
tests/lib/OpenMetrics/ExporterManagerTest.php
Normal file
23
tests/lib/OpenMetrics/ExporterManagerTest.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics;
|
||||
|
||||
use OC\OpenMetrics\ExporterManager;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\Server;
|
||||
use Test\TestCase;
|
||||
|
||||
class ExporterManagerTest extends TestCase {
|
||||
public function testExport(): void {
|
||||
$exporter = Server::get(ExporterManager::class);
|
||||
$this->assertInstanceOf(ExporterManager::class, $exporter);
|
||||
foreach ($exporter->export() as $metric) {
|
||||
$this->assertInstanceOf(IMetricFamily::class, $metric);
|
||||
};
|
||||
}
|
||||
}
|
||||
32
tests/lib/OpenMetrics/Exporters/ActiveSessionsTest.php
Normal file
32
tests/lib/OpenMetrics/Exporters/ActiveSessionsTest.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\ActiveSessions;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\Server;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
|
||||
#[Group('DB')]
|
||||
class ActiveSessionsTest extends ExporterTestCase {
|
||||
protected function getExporter():IMetricFamily {
|
||||
return new ActiveSessions(Server::get(IDBConnection::class));
|
||||
}
|
||||
|
||||
public function testMetricsLabel(): void {
|
||||
$this->assertLabelsAre([
|
||||
['time' => 'Last 5 minutes'],
|
||||
['time' => 'Last 15 minutes'],
|
||||
['time' => 'Last hour'],
|
||||
['time' => 'Last day'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
32
tests/lib/OpenMetrics/Exporters/ActiveUsersTest.php
Normal file
32
tests/lib/OpenMetrics/Exporters/ActiveUsersTest.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\ActiveUsers;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\Server;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
|
||||
#[Group('DB')]
|
||||
class ActiveUsersTest extends ExporterTestCase {
|
||||
protected function getExporter():IMetricFamily {
|
||||
return new ActiveUsers(Server::get(IDBConnection::class));
|
||||
}
|
||||
|
||||
public function testMetricsLabel(): void {
|
||||
$this->assertLabelsAre([
|
||||
['time' => 'Last 5 minutes'],
|
||||
['time' => 'Last 15 minutes'],
|
||||
['time' => 'Last hour'],
|
||||
['time' => 'Last day'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
38
tests/lib/OpenMetrics/Exporters/AppsCountTest.php
Normal file
38
tests/lib/OpenMetrics/Exporters/AppsCountTest.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\AppsCount;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
|
||||
class AppsCountTest extends ExporterTestCase {
|
||||
private IAppManager $appManager;
|
||||
|
||||
protected function getExporter():IMetricFamily {
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->appManager->method('getAppInstalledVersions')
|
||||
->with(false)
|
||||
->willReturn(['app1', 'app2', 'app3', 'app4', 'app5']);
|
||||
$this->appManager->method('getEnabledApps')
|
||||
->willReturn(['app1', 'app2', 'app3']);
|
||||
return new AppsCount($this->appManager);
|
||||
}
|
||||
|
||||
public function testMetrics(): void {
|
||||
foreach ($this->metrics as $metric) {
|
||||
$expectedValue = match ($metric->label('status')) {
|
||||
'disabled' => 2,
|
||||
'enabled' => 3,
|
||||
};
|
||||
$this->assertEquals($expectedValue, $metric->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
tests/lib/OpenMetrics/Exporters/AppsInfoTest.php
Normal file
37
tests/lib/OpenMetrics/Exporters/AppsInfoTest.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\AppsInfo;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
|
||||
class AppsInfoTest extends ExporterTestCase {
|
||||
private IAppManager $appManager;
|
||||
private array $appList = [
|
||||
'appA' => '0.1.2',
|
||||
'appB' => '1.2.3 beta 4',
|
||||
];
|
||||
|
||||
protected function getExporter():IMetricFamily {
|
||||
$this->appManager = $this->createMock(IAppManager::class);
|
||||
$this->appManager->method('getAppInstalledVersions')
|
||||
->with(true)
|
||||
->willReturn($this->appList);
|
||||
|
||||
return new AppsInfo($this->appManager);
|
||||
}
|
||||
|
||||
public function testMetrics(): void {
|
||||
$this->assertCount(1, $this->metrics);
|
||||
$metric = array_pop($this->metrics);
|
||||
$this->assertSame($this->appList, $metric->labels);
|
||||
}
|
||||
}
|
||||
41
tests/lib/OpenMetrics/Exporters/ExporterTestCase.php
Normal file
41
tests/lib/OpenMetrics/Exporters/ExporterTestCase.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use Test\TestCase;
|
||||
|
||||
abstract class ExporterTestCase extends TestCase {
|
||||
protected IMetricFamily $exporter;
|
||||
/** @var IMetric[] */
|
||||
protected array $metrics;
|
||||
|
||||
abstract protected function getExporter(): IMetricFamily;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->exporter = $this->getExporter();
|
||||
$this->metrics = iterator_to_array($this->exporter->metrics());
|
||||
}
|
||||
|
||||
public function testNotEmptyData() {
|
||||
$this->assertNotEmpty($this->exporter->name());
|
||||
$this->assertNotEmpty($this->metrics);
|
||||
}
|
||||
|
||||
protected function assertLabelsAre(array $expectedLabels) {
|
||||
$foundLabels = [];
|
||||
foreach ($this->metrics as $metric) {
|
||||
$foundLabels[] = $metric->labels;
|
||||
}
|
||||
|
||||
$this->assertSame($foundLabels, $expectedLabels);
|
||||
}
|
||||
}
|
||||
29
tests/lib/OpenMetrics/Exporters/FilesByTypeTest.php
Normal file
29
tests/lib/OpenMetrics/Exporters/FilesByTypeTest.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\FilesByType;
|
||||
use OCP\Files\IMimeTypeLoader;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\Server;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
|
||||
#[Group('DB')]
|
||||
class FilesByTypeTest extends ExporterTestCase {
|
||||
protected function getExporter():IMetricFamily {
|
||||
return new FilesByType(
|
||||
Server::get(ICacheFactory::class),
|
||||
Server::get(IDBConnection::class),
|
||||
Server::get(IMimeTypeLoader::class),
|
||||
);
|
||||
}
|
||||
}
|
||||
42
tests/lib/OpenMetrics/Exporters/InstanceInfoTest.php
Normal file
42
tests/lib/OpenMetrics/Exporters/InstanceInfoTest.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\InstanceInfo;
|
||||
use OC\SystemConfig;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\ServerVersion;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class InstanceInfoTest extends ExporterTestCase {
|
||||
private SystemConfig&MockObject $systemConfig;
|
||||
private ServerVersion&MockObject $serverVersion;
|
||||
|
||||
protected function getExporter():IMetricFamily {
|
||||
$this->systemConfig = $this->createMock(SystemConfig::class);
|
||||
$this->serverVersion = $this->createMock(ServerVersion::class);
|
||||
$this->serverVersion->method('getHumanVersion')->willReturn('33.13.17 Gold');
|
||||
$this->serverVersion->method('getVersion')->willReturn([33, 13, 17]);
|
||||
$this->serverVersion->method('getBuild')->willReturn('dev');
|
||||
|
||||
return new InstanceInfo($this->systemConfig, $this->serverVersion);
|
||||
}
|
||||
|
||||
public function testMetrics(): void {
|
||||
$this->assertCount(1, $this->metrics);
|
||||
$metric = array_pop($this->metrics);
|
||||
$this->assertSame([
|
||||
'full version' => '33.13.17 Gold',
|
||||
'major version' => '33',
|
||||
'build' => 'dev',
|
||||
'installed' => '0',
|
||||
], $metric->labels);
|
||||
}
|
||||
}
|
||||
19
tests/lib/OpenMetrics/Exporters/MaintenanceTest.php
Normal file
19
tests/lib/OpenMetrics/Exporters/MaintenanceTest.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\Maintenance;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
|
||||
class MaintenanceTest extends ExporterTestCase {
|
||||
protected function getExporter():IMetricFamily {
|
||||
return new Maintenance();
|
||||
}
|
||||
}
|
||||
23
tests/lib/OpenMetrics/Exporters/RunningJobsTest.php
Normal file
23
tests/lib/OpenMetrics/Exporters/RunningJobsTest.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\RunningJobs;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use OCP\Server;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
|
||||
#[Group('DB')]
|
||||
class RunningJobsTest extends ExporterTestCase {
|
||||
protected function getExporter():IMetricFamily {
|
||||
return new RunningJobs(Server::get(IDBConnection::class));
|
||||
}
|
||||
}
|
||||
39
tests/lib/OpenMetrics/Exporters/UsersByBackendTest.php
Normal file
39
tests/lib/OpenMetrics/Exporters/UsersByBackendTest.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\OpenMetrics\Exporters;
|
||||
|
||||
use OC\OpenMetrics\Exporters\UsersByBackend;
|
||||
use OCP\IUserManager;
|
||||
use OCP\OpenMetrics\IMetricFamily;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class UsersByBackendTest extends ExporterTestCase {
|
||||
private IUserManager&MockObject $userManager;
|
||||
private array $backendList = [
|
||||
'backend A' => 42,
|
||||
'backend B' => 51,
|
||||
'backend C' => 0,
|
||||
];
|
||||
|
||||
|
||||
protected function getExporter():IMetricFamily {
|
||||
$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->userManager->method('countUsers')
|
||||
->with(true)
|
||||
->willReturn($this->backendList);
|
||||
return new UsersByBackend($this->userManager);
|
||||
}
|
||||
|
||||
public function testMetrics(): void {
|
||||
foreach ($this->metrics as $metric) {
|
||||
$this->assertEquals($this->backendList[$metric->label('backend')], $metric->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue