@@ -25,52 +25,52 @@ discard block |
||
| 25 | 25 | use Test\TestCase; |
| 26 | 26 | |
| 27 | 27 | class OpenMetricsControllerTest extends TestCase { |
| 28 | - private IRequest&MockObject $request; |
|
| 29 | - private IConfig&MockObject $config; |
|
| 30 | - private ExporterManager&MockObject $exporterManager; |
|
| 31 | - private LoggerInterface&MockObject $logger; |
|
| 32 | - private OpenMetricsController $controller; |
|
| 28 | + private IRequest&MockObject $request; |
|
| 29 | + private IConfig&MockObject $config; |
|
| 30 | + private ExporterManager&MockObject $exporterManager; |
|
| 31 | + private LoggerInterface&MockObject $logger; |
|
| 32 | + private OpenMetricsController $controller; |
|
| 33 | 33 | |
| 34 | - protected function setUp(): void { |
|
| 35 | - parent::setUp(); |
|
| 36 | - $this->request = $this->createMock(IRequest::class); |
|
| 37 | - $this->request->method('getRemoteAddress') |
|
| 38 | - ->willReturn('192.168.1.1'); |
|
| 39 | - $this->config = $this->createMock(IConfig::class); |
|
| 40 | - $this->exporterManager = $this->createMock(ExporterManager::class); |
|
| 41 | - $this->exporterManager->method('export')->willReturnCallback([$this, 'getFakeMetrics']); |
|
| 42 | - $this->logger = $this->createMock(LoggerInterface::class); |
|
| 43 | - $this->controller = new OpenMetricsController('core', $this->request, $this->config, $this->exporterManager, $this->logger); |
|
| 44 | - } |
|
| 34 | + protected function setUp(): void { |
|
| 35 | + parent::setUp(); |
|
| 36 | + $this->request = $this->createMock(IRequest::class); |
|
| 37 | + $this->request->method('getRemoteAddress') |
|
| 38 | + ->willReturn('192.168.1.1'); |
|
| 39 | + $this->config = $this->createMock(IConfig::class); |
|
| 40 | + $this->exporterManager = $this->createMock(ExporterManager::class); |
|
| 41 | + $this->exporterManager->method('export')->willReturnCallback([$this, 'getFakeMetrics']); |
|
| 42 | + $this->logger = $this->createMock(LoggerInterface::class); |
|
| 43 | + $this->controller = new OpenMetricsController('core', $this->request, $this->config, $this->exporterManager, $this->logger); |
|
| 44 | + } |
|
| 45 | 45 | |
| 46 | - public function getFakeMetrics(): Generator { |
|
| 47 | - $metric = $this->createMock(IMetricFamily::class); |
|
| 48 | - $metric->method('type')->willReturn(MetricType::gauge); |
|
| 49 | - $metric->method('unit')->willReturn('fake'); |
|
| 50 | - $metric->method('name')->willReturn('fake_count'); |
|
| 51 | - $metric->method('help')->willReturn('A fake count used for tests'); |
|
| 52 | - $metric->method('metrics')->willReturnCallback(function () { |
|
| 53 | - yield new Metric(42, ['type' => 'used']); |
|
| 54 | - yield new Metric(24, ['type' => 'unused']); |
|
| 55 | - }); |
|
| 56 | - yield $metric; |
|
| 57 | - } |
|
| 58 | - public function testGetMetrics(): void { |
|
| 59 | - $output = $this->createMock(IOutput::class); |
|
| 60 | - $fullOutput = ''; |
|
| 61 | - $output->method('setOutput') |
|
| 62 | - ->willReturnCallback(function ($output) use (&$fullOutput) { |
|
| 63 | - $fullOutput .= $output; |
|
| 64 | - }); |
|
| 65 | - $this->config->expects($this->once()) |
|
| 66 | - ->method('getSystemValue') |
|
| 67 | - ->with('openmetrics_allowed_clients') |
|
| 68 | - ->willReturn(['192.168.0.0/16']); |
|
| 69 | - $response = $this->controller->export(); |
|
| 70 | - $this->assertInstanceOf(StreamTraversableResponse::class, $response); |
|
| 71 | - $this->assertEquals('200', $response->getStatus()); |
|
| 72 | - $this->assertEquals('application/openmetrics-text; version=1.0.0; charset=utf-8', $response->getHeaders()['Content-Type']); |
|
| 73 | - $expected = <<<EXPECTED |
|
| 46 | + public function getFakeMetrics(): Generator { |
|
| 47 | + $metric = $this->createMock(IMetricFamily::class); |
|
| 48 | + $metric->method('type')->willReturn(MetricType::gauge); |
|
| 49 | + $metric->method('unit')->willReturn('fake'); |
|
| 50 | + $metric->method('name')->willReturn('fake_count'); |
|
| 51 | + $metric->method('help')->willReturn('A fake count used for tests'); |
|
| 52 | + $metric->method('metrics')->willReturnCallback(function () { |
|
| 53 | + yield new Metric(42, ['type' => 'used']); |
|
| 54 | + yield new Metric(24, ['type' => 'unused']); |
|
| 55 | + }); |
|
| 56 | + yield $metric; |
|
| 57 | + } |
|
| 58 | + public function testGetMetrics(): void { |
|
| 59 | + $output = $this->createMock(IOutput::class); |
|
| 60 | + $fullOutput = ''; |
|
| 61 | + $output->method('setOutput') |
|
| 62 | + ->willReturnCallback(function ($output) use (&$fullOutput) { |
|
| 63 | + $fullOutput .= $output; |
|
| 64 | + }); |
|
| 65 | + $this->config->expects($this->once()) |
|
| 66 | + ->method('getSystemValue') |
|
| 67 | + ->with('openmetrics_allowed_clients') |
|
| 68 | + ->willReturn(['192.168.0.0/16']); |
|
| 69 | + $response = $this->controller->export(); |
|
| 70 | + $this->assertInstanceOf(StreamTraversableResponse::class, $response); |
|
| 71 | + $this->assertEquals('200', $response->getStatus()); |
|
| 72 | + $this->assertEquals('application/openmetrics-text; version=1.0.0; charset=utf-8', $response->getHeaders()['Content-Type']); |
|
| 73 | + $expected = <<<EXPECTED |
|
| 74 | 74 | # TYPE nextcloud_fake_count gauge |
| 75 | 75 | # UNIT nextcloud_fake_count fake |
| 76 | 76 | # HELP nextcloud_fake_count A fake count used for tests |
@@ -83,17 +83,17 @@ discard block |
||
| 83 | 83 | # EOF |
| 84 | 84 | |
| 85 | 85 | EXPECTED; |
| 86 | - $response->callback($output); |
|
| 87 | - $this->assertStringMatchesFormat($expected, $fullOutput); |
|
| 88 | - } |
|
| 86 | + $response->callback($output); |
|
| 87 | + $this->assertStringMatchesFormat($expected, $fullOutput); |
|
| 88 | + } |
|
| 89 | 89 | |
| 90 | - public function testGetMetricsFromForbiddenIp(): void { |
|
| 91 | - $this->config->expects($this->once()) |
|
| 92 | - ->method('getSystemValue') |
|
| 93 | - ->with('openmetrics_allowed_clients') |
|
| 94 | - ->willReturn(['1.2.3.4']); |
|
| 95 | - $response = $this->controller->export(); |
|
| 96 | - $this->assertInstanceOf(Response::class, $response); |
|
| 97 | - $this->assertEquals('403', $response->getStatus()); |
|
| 98 | - } |
|
| 90 | + public function testGetMetricsFromForbiddenIp(): void { |
|
| 91 | + $this->config->expects($this->once()) |
|
| 92 | + ->method('getSystemValue') |
|
| 93 | + ->with('openmetrics_allowed_clients') |
|
| 94 | + ->willReturn(['1.2.3.4']); |
|
| 95 | + $response = $this->controller->export(); |
|
| 96 | + $this->assertInstanceOf(Response::class, $response); |
|
| 97 | + $this->assertEquals('403', $response->getStatus()); |
|
| 98 | + } |
|
| 99 | 99 | } |
@@ -49,7 +49,7 @@ discard block |
||
| 49 | 49 | $metric->method('unit')->willReturn('fake'); |
| 50 | 50 | $metric->method('name')->willReturn('fake_count'); |
| 51 | 51 | $metric->method('help')->willReturn('A fake count used for tests'); |
| 52 | - $metric->method('metrics')->willReturnCallback(function () { |
|
| 52 | + $metric->method('metrics')->willReturnCallback(function() { |
|
| 53 | 53 | yield new Metric(42, ['type' => 'used']); |
| 54 | 54 | yield new Metric(24, ['type' => 'unused']); |
| 55 | 55 | }); |
@@ -59,7 +59,7 @@ discard block |
||
| 59 | 59 | $output = $this->createMock(IOutput::class); |
| 60 | 60 | $fullOutput = ''; |
| 61 | 61 | $output->method('setOutput') |
| 62 | - ->willReturnCallback(function ($output) use (&$fullOutput) { |
|
| 62 | + ->willReturnCallback(function($output) use (&$fullOutput) { |
|
| 63 | 63 | $fullOutput .= $output; |
| 64 | 64 | }); |
| 65 | 65 | $this->config->expects($this->once()) |
@@ -13,64 +13,64 @@ |
||
| 13 | 13 | use Test\TestCase; |
| 14 | 14 | |
| 15 | 15 | abstract class ExporterTestCase extends TestCase { |
| 16 | - protected IMetricFamily $exporter; |
|
| 17 | - /** @var IMetric[] */ |
|
| 18 | - protected array $metrics; |
|
| 16 | + protected IMetricFamily $exporter; |
|
| 17 | + /** @var IMetric[] */ |
|
| 18 | + protected array $metrics; |
|
| 19 | 19 | |
| 20 | - abstract protected function getExporter(): IMetricFamily; |
|
| 20 | + abstract protected function getExporter(): IMetricFamily; |
|
| 21 | 21 | |
| 22 | - protected function setUp(): void { |
|
| 23 | - parent::setUp(); |
|
| 24 | - $this->exporter = $this->getExporter(); |
|
| 25 | - $this->metrics = iterator_to_array($this->exporter->metrics()); |
|
| 26 | - } |
|
| 22 | + protected function setUp(): void { |
|
| 23 | + parent::setUp(); |
|
| 24 | + $this->exporter = $this->getExporter(); |
|
| 25 | + $this->metrics = iterator_to_array($this->exporter->metrics()); |
|
| 26 | + } |
|
| 27 | 27 | |
| 28 | - public function testNotEmptyData(): void { |
|
| 29 | - $this->assertNotEmpty($this->metrics); |
|
| 30 | - } |
|
| 28 | + public function testNotEmptyData(): void { |
|
| 29 | + $this->assertNotEmpty($this->metrics); |
|
| 30 | + } |
|
| 31 | 31 | |
| 32 | - public function testValidExporterName(): void { |
|
| 33 | - $exporterName = $this->exporter->name(); |
|
| 34 | - $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $exporterName, ); |
|
| 32 | + public function testValidExporterName(): void { |
|
| 33 | + $exporterName = $this->exporter->name(); |
|
| 34 | + $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $exporterName, ); |
|
| 35 | 35 | |
| 36 | - $unit = $this->exporter->unit(); |
|
| 37 | - if ($unit === '') { |
|
| 38 | - return; |
|
| 39 | - } |
|
| 40 | - // Unit name must follow metric name format |
|
| 41 | - $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $unit); |
|
| 42 | - // Unit name must be a suffix in exporter name |
|
| 43 | - $this->assertMatchesRegularExpression( |
|
| 44 | - '/(^|_)' . $unit . '$/', |
|
| 45 | - $exporterName, |
|
| 46 | - 'Metric name "' . $exporterName . '" must contains unit "' . $unit . '" as a suffix', |
|
| 47 | - ); |
|
| 48 | - } |
|
| 36 | + $unit = $this->exporter->unit(); |
|
| 37 | + if ($unit === '') { |
|
| 38 | + return; |
|
| 39 | + } |
|
| 40 | + // Unit name must follow metric name format |
|
| 41 | + $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $unit); |
|
| 42 | + // Unit name must be a suffix in exporter name |
|
| 43 | + $this->assertMatchesRegularExpression( |
|
| 44 | + '/(^|_)' . $unit . '$/', |
|
| 45 | + $exporterName, |
|
| 46 | + 'Metric name "' . $exporterName . '" must contains unit "' . $unit . '" as a suffix', |
|
| 47 | + ); |
|
| 48 | + } |
|
| 49 | 49 | |
| 50 | - public function testValidLabelKey(): void { |
|
| 51 | - $labelNames = []; |
|
| 52 | - foreach ($this->metrics as $metric) { |
|
| 53 | - foreach ($metric->labels as $label => $value) { |
|
| 54 | - $labelNames[$label] = $label; |
|
| 55 | - } |
|
| 56 | - } |
|
| 50 | + public function testValidLabelKey(): void { |
|
| 51 | + $labelNames = []; |
|
| 52 | + foreach ($this->metrics as $metric) { |
|
| 53 | + foreach ($metric->labels as $label => $value) { |
|
| 54 | + $labelNames[$label] = $label; |
|
| 55 | + } |
|
| 56 | + } |
|
| 57 | 57 | |
| 58 | - if (empty($labelNames)) { |
|
| 59 | - $this->expectNotToPerformAssertions(); |
|
| 60 | - return; |
|
| 61 | - } |
|
| 62 | - foreach ($labelNames as $label) { |
|
| 63 | - $this->assertMatchesRegularExpression('/^[a-z_][a-z0-9_]*$/i', $label); |
|
| 64 | - } |
|
| 58 | + if (empty($labelNames)) { |
|
| 59 | + $this->expectNotToPerformAssertions(); |
|
| 60 | + return; |
|
| 61 | + } |
|
| 62 | + foreach ($labelNames as $label) { |
|
| 63 | + $this->assertMatchesRegularExpression('/^[a-z_][a-z0-9_]*$/i', $label); |
|
| 64 | + } |
|
| 65 | 65 | |
| 66 | - } |
|
| 66 | + } |
|
| 67 | 67 | |
| 68 | - protected function assertLabelsAre(array $expectedLabels): void { |
|
| 69 | - $foundLabels = []; |
|
| 70 | - foreach ($this->metrics as $metric) { |
|
| 71 | - $foundLabels[] = $metric->labels; |
|
| 72 | - } |
|
| 68 | + protected function assertLabelsAre(array $expectedLabels): void { |
|
| 69 | + $foundLabels = []; |
|
| 70 | + foreach ($this->metrics as $metric) { |
|
| 71 | + $foundLabels[] = $metric->labels; |
|
| 72 | + } |
|
| 73 | 73 | |
| 74 | - $this->assertSame($foundLabels, $expectedLabels); |
|
| 75 | - } |
|
| 74 | + $this->assertSame($foundLabels, $expectedLabels); |
|
| 75 | + } |
|
| 76 | 76 | } |
@@ -31,7 +31,7 @@ discard block |
||
| 31 | 31 | |
| 32 | 32 | public function testValidExporterName(): void { |
| 33 | 33 | $exporterName = $this->exporter->name(); |
| 34 | - $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $exporterName, ); |
|
| 34 | + $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $exporterName,); |
|
| 35 | 35 | |
| 36 | 36 | $unit = $this->exporter->unit(); |
| 37 | 37 | if ($unit === '') { |
@@ -41,9 +41,9 @@ discard block |
||
| 41 | 41 | $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $unit); |
| 42 | 42 | // Unit name must be a suffix in exporter name |
| 43 | 43 | $this->assertMatchesRegularExpression( |
| 44 | - '/(^|_)' . $unit . '$/', |
|
| 44 | + '/(^|_)'.$unit.'$/', |
|
| 45 | 45 | $exporterName, |
| 46 | - 'Metric name "' . $exporterName . '" must contains unit "' . $unit . '" as a suffix', |
|
| 46 | + 'Metric name "'.$exporterName.'" must contains unit "'.$unit.'" as a suffix', |
|
| 47 | 47 | ); |
| 48 | 48 | } |
| 49 | 49 | |
@@ -20,48 +20,48 @@ |
||
| 20 | 20 | * Export the number of running jobs by type |
| 21 | 21 | */ |
| 22 | 22 | class RunningJobs implements IMetricFamily { |
| 23 | - public function __construct( |
|
| 24 | - private IDBConnection $connection, |
|
| 25 | - ) { |
|
| 26 | - } |
|
| 23 | + public function __construct( |
|
| 24 | + private IDBConnection $connection, |
|
| 25 | + ) { |
|
| 26 | + } |
|
| 27 | 27 | |
| 28 | - #[Override] |
|
| 29 | - public function name(): string { |
|
| 30 | - return 'running_jobs'; |
|
| 31 | - } |
|
| 28 | + #[Override] |
|
| 29 | + public function name(): string { |
|
| 30 | + return 'running_jobs'; |
|
| 31 | + } |
|
| 32 | 32 | |
| 33 | - #[Override] |
|
| 34 | - public function type(): MetricType { |
|
| 35 | - return MetricType::gauge; |
|
| 36 | - } |
|
| 33 | + #[Override] |
|
| 34 | + public function type(): MetricType { |
|
| 35 | + return MetricType::gauge; |
|
| 36 | + } |
|
| 37 | 37 | |
| 38 | - #[Override] |
|
| 39 | - public function unit(): string { |
|
| 40 | - return 'jobs'; |
|
| 41 | - } |
|
| 38 | + #[Override] |
|
| 39 | + public function unit(): string { |
|
| 40 | + return 'jobs'; |
|
| 41 | + } |
|
| 42 | 42 | |
| 43 | - #[Override] |
|
| 44 | - public function help(): string { |
|
| 45 | - return 'Number of running jobs'; |
|
| 46 | - } |
|
| 43 | + #[Override] |
|
| 44 | + public function help(): string { |
|
| 45 | + return 'Number of running jobs'; |
|
| 46 | + } |
|
| 47 | 47 | |
| 48 | - #[Override] |
|
| 49 | - public function metrics(): Generator { |
|
| 50 | - $qb = $this->connection->getQueryBuilder(); |
|
| 51 | - $result = $qb->select($qb->func()->count('*', 'nb'), 'class') |
|
| 52 | - ->from('jobs') |
|
| 53 | - ->where($qb->expr()->gt('reserved_at', $qb->createNamedParameter(0))) |
|
| 54 | - ->groupBy('class') |
|
| 55 | - ->executeQuery(); |
|
| 48 | + #[Override] |
|
| 49 | + public function metrics(): Generator { |
|
| 50 | + $qb = $this->connection->getQueryBuilder(); |
|
| 51 | + $result = $qb->select($qb->func()->count('*', 'nb'), 'class') |
|
| 52 | + ->from('jobs') |
|
| 53 | + ->where($qb->expr()->gt('reserved_at', $qb->createNamedParameter(0))) |
|
| 54 | + ->groupBy('class') |
|
| 55 | + ->executeQuery(); |
|
| 56 | 56 | |
| 57 | - // If no result, return a metric with count '0' |
|
| 58 | - if ($result->rowCount() === 0) { |
|
| 59 | - yield new Metric(0); |
|
| 60 | - return; |
|
| 61 | - } |
|
| 57 | + // If no result, return a metric with count '0' |
|
| 58 | + if ($result->rowCount() === 0) { |
|
| 59 | + yield new Metric(0); |
|
| 60 | + return; |
|
| 61 | + } |
|
| 62 | 62 | |
| 63 | - foreach ($result->iterateAssociative() as $row) { |
|
| 64 | - yield new Metric($row['nb'], ['class' => $row['class']]); |
|
| 65 | - } |
|
| 66 | - } |
|
| 63 | + foreach ($result->iterateAssociative() as $row) { |
|
| 64 | + yield new Metric($row['nb'], ['class' => $row['class']]); |
|
| 65 | + } |
|
| 66 | + } |
|
| 67 | 67 | } |
@@ -20,43 +20,43 @@ |
||
| 20 | 20 | * Export statistics about apps |
| 21 | 21 | */ |
| 22 | 22 | class AppsCount implements IMetricFamily { |
| 23 | - public function __construct( |
|
| 24 | - private IAppManager $appManager, |
|
| 25 | - ) { |
|
| 26 | - } |
|
| 27 | - |
|
| 28 | - #[Override] |
|
| 29 | - public function name(): string { |
|
| 30 | - return 'installed_applications'; |
|
| 31 | - } |
|
| 32 | - |
|
| 33 | - #[Override] |
|
| 34 | - public function type(): MetricType { |
|
| 35 | - return MetricType::gauge; |
|
| 36 | - } |
|
| 37 | - |
|
| 38 | - #[Override] |
|
| 39 | - public function unit(): string { |
|
| 40 | - return 'applications'; |
|
| 41 | - } |
|
| 42 | - |
|
| 43 | - #[Override] |
|
| 44 | - public function help(): string { |
|
| 45 | - return 'Number of applications installed in Nextcloud'; |
|
| 46 | - } |
|
| 47 | - |
|
| 48 | - #[Override] |
|
| 49 | - public function metrics(): Generator { |
|
| 50 | - $installedAppsCount = count($this->appManager->getAppInstalledVersions(false)); |
|
| 51 | - $enabledAppsCount = count($this->appManager->getEnabledApps()); |
|
| 52 | - $disabledAppsCount = $installedAppsCount - $enabledAppsCount; |
|
| 53 | - yield new Metric( |
|
| 54 | - $disabledAppsCount, |
|
| 55 | - ['status' => 'disabled'], |
|
| 56 | - ); |
|
| 57 | - yield new Metric( |
|
| 58 | - $enabledAppsCount, |
|
| 59 | - ['status' => 'enabled'], |
|
| 60 | - ); |
|
| 61 | - } |
|
| 23 | + public function __construct( |
|
| 24 | + private IAppManager $appManager, |
|
| 25 | + ) { |
|
| 26 | + } |
|
| 27 | + |
|
| 28 | + #[Override] |
|
| 29 | + public function name(): string { |
|
| 30 | + return 'installed_applications'; |
|
| 31 | + } |
|
| 32 | + |
|
| 33 | + #[Override] |
|
| 34 | + public function type(): MetricType { |
|
| 35 | + return MetricType::gauge; |
|
| 36 | + } |
|
| 37 | + |
|
| 38 | + #[Override] |
|
| 39 | + public function unit(): string { |
|
| 40 | + return 'applications'; |
|
| 41 | + } |
|
| 42 | + |
|
| 43 | + #[Override] |
|
| 44 | + public function help(): string { |
|
| 45 | + return 'Number of applications installed in Nextcloud'; |
|
| 46 | + } |
|
| 47 | + |
|
| 48 | + #[Override] |
|
| 49 | + public function metrics(): Generator { |
|
| 50 | + $installedAppsCount = count($this->appManager->getAppInstalledVersions(false)); |
|
| 51 | + $enabledAppsCount = count($this->appManager->getEnabledApps()); |
|
| 52 | + $disabledAppsCount = $installedAppsCount - $enabledAppsCount; |
|
| 53 | + yield new Metric( |
|
| 54 | + $disabledAppsCount, |
|
| 55 | + ['status' => 'disabled'], |
|
| 56 | + ); |
|
| 57 | + yield new Metric( |
|
| 58 | + $enabledAppsCount, |
|
| 59 | + ['status' => 'enabled'], |
|
| 60 | + ); |
|
| 61 | + } |
|
| 62 | 62 | } |
@@ -31,58 +31,58 @@ discard block |
||
| 31 | 31 | * @package OC\Core\Controller |
| 32 | 32 | */ |
| 33 | 33 | class OpenMetricsController extends Controller { |
| 34 | - public function __construct( |
|
| 35 | - string $appName, |
|
| 36 | - IRequest $request, |
|
| 37 | - private IConfig $config, |
|
| 38 | - private ExporterManager $exporterManager, |
|
| 39 | - private LoggerInterface $logger, |
|
| 40 | - ) { |
|
| 41 | - parent::__construct($appName, $request); |
|
| 42 | - } |
|
| 43 | - |
|
| 44 | - #[NoCSRFRequired] |
|
| 45 | - #[PublicPage] |
|
| 46 | - #[FrontpageRoute(verb: 'GET', url: '/metrics')] |
|
| 47 | - public function export(): Http\Response { |
|
| 48 | - if (!$this->isRemoteAddressAllowed()) { |
|
| 49 | - return new Http\Response(Http::STATUS_FORBIDDEN); |
|
| 50 | - } |
|
| 51 | - |
|
| 52 | - return new Http\StreamTraversableResponse( |
|
| 53 | - $this->generate(), |
|
| 54 | - Http::STATUS_OK, |
|
| 55 | - [ |
|
| 56 | - 'Content-Type' => 'application/openmetrics-text; version=1.0.0; charset=utf-8', |
|
| 57 | - ] |
|
| 58 | - ); |
|
| 59 | - } |
|
| 60 | - |
|
| 61 | - private function isRemoteAddressAllowed(): bool { |
|
| 62 | - $clientAddress = new Address($this->request->getRemoteAddress()); |
|
| 63 | - $allowedRanges = $this->config->getSystemValue('openmetrics_allowed_clients', ['127.0.0.0/16', '::1/128']); |
|
| 64 | - if (!is_array($allowedRanges)) { |
|
| 65 | - $this->logger->warning('Invalid configuration for "openmetrics_allowed_clients"'); |
|
| 66 | - return false; |
|
| 67 | - } |
|
| 68 | - |
|
| 69 | - foreach ($allowedRanges as $range) { |
|
| 70 | - $range = new Range($range); |
|
| 71 | - if ($range->contains($clientAddress)) { |
|
| 72 | - return true; |
|
| 73 | - } |
|
| 74 | - } |
|
| 75 | - |
|
| 76 | - return false; |
|
| 77 | - } |
|
| 78 | - |
|
| 79 | - private function generate(): \Generator { |
|
| 80 | - foreach ($this->exporterManager->export() as $family) { |
|
| 81 | - yield $this->formatFamily($family); |
|
| 82 | - } |
|
| 83 | - |
|
| 84 | - $elapsed = (string)(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']); |
|
| 85 | - yield <<<SUMMARY |
|
| 34 | + public function __construct( |
|
| 35 | + string $appName, |
|
| 36 | + IRequest $request, |
|
| 37 | + private IConfig $config, |
|
| 38 | + private ExporterManager $exporterManager, |
|
| 39 | + private LoggerInterface $logger, |
|
| 40 | + ) { |
|
| 41 | + parent::__construct($appName, $request); |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + #[NoCSRFRequired] |
|
| 45 | + #[PublicPage] |
|
| 46 | + #[FrontpageRoute(verb: 'GET', url: '/metrics')] |
|
| 47 | + public function export(): Http\Response { |
|
| 48 | + if (!$this->isRemoteAddressAllowed()) { |
|
| 49 | + return new Http\Response(Http::STATUS_FORBIDDEN); |
|
| 50 | + } |
|
| 51 | + |
|
| 52 | + return new Http\StreamTraversableResponse( |
|
| 53 | + $this->generate(), |
|
| 54 | + Http::STATUS_OK, |
|
| 55 | + [ |
|
| 56 | + 'Content-Type' => 'application/openmetrics-text; version=1.0.0; charset=utf-8', |
|
| 57 | + ] |
|
| 58 | + ); |
|
| 59 | + } |
|
| 60 | + |
|
| 61 | + private function isRemoteAddressAllowed(): bool { |
|
| 62 | + $clientAddress = new Address($this->request->getRemoteAddress()); |
|
| 63 | + $allowedRanges = $this->config->getSystemValue('openmetrics_allowed_clients', ['127.0.0.0/16', '::1/128']); |
|
| 64 | + if (!is_array($allowedRanges)) { |
|
| 65 | + $this->logger->warning('Invalid configuration for "openmetrics_allowed_clients"'); |
|
| 66 | + return false; |
|
| 67 | + } |
|
| 68 | + |
|
| 69 | + foreach ($allowedRanges as $range) { |
|
| 70 | + $range = new Range($range); |
|
| 71 | + if ($range->contains($clientAddress)) { |
|
| 72 | + return true; |
|
| 73 | + } |
|
| 74 | + } |
|
| 75 | + |
|
| 76 | + return false; |
|
| 77 | + } |
|
| 78 | + |
|
| 79 | + private function generate(): \Generator { |
|
| 80 | + foreach ($this->exporterManager->export() as $family) { |
|
| 81 | + yield $this->formatFamily($family); |
|
| 82 | + } |
|
| 83 | + |
|
| 84 | + $elapsed = (string)(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']); |
|
| 85 | + yield <<<SUMMARY |
|
| 86 | 86 | # TYPE nextcloud_exporter_run_seconds gauge |
| 87 | 87 | # UNIT nextcloud_exporter_run_seconds seconds |
| 88 | 88 | # HELP nextcloud_exporter_run_seconds Exporter run time |
@@ -90,64 +90,64 @@ discard block |
||
| 90 | 90 | # EOF |
| 91 | 91 | |
| 92 | 92 | SUMMARY; |
| 93 | - } |
|
| 94 | - |
|
| 95 | - private function formatFamily(IMetricFamily $family): string { |
|
| 96 | - $output = ''; |
|
| 97 | - $name = $family->name(); |
|
| 98 | - if ($family->type() !== MetricType::unknown) { |
|
| 99 | - $output = '# TYPE nextcloud_' . $name . ' ' . $family->type()->name . "\n"; |
|
| 100 | - } |
|
| 101 | - if ($family->unit() !== '') { |
|
| 102 | - $output .= '# UNIT nextcloud_' . $name . ' ' . $family->unit() . "\n"; |
|
| 103 | - } |
|
| 104 | - if ($family->help() !== '') { |
|
| 105 | - $output .= '# HELP nextcloud_' . $name . ' ' . $family->help() . "\n"; |
|
| 106 | - } |
|
| 107 | - foreach ($family->metrics() as $metric) { |
|
| 108 | - $output .= 'nextcloud_' . $name . $this->formatLabels($metric) . ' ' . $this->formatValue($metric); |
|
| 109 | - if ($metric->timestamp !== null) { |
|
| 110 | - $output .= ' ' . $this->formatTimestamp($metric); |
|
| 111 | - } |
|
| 112 | - $output .= "\n"; |
|
| 113 | - } |
|
| 114 | - |
|
| 115 | - return $output; |
|
| 116 | - } |
|
| 117 | - |
|
| 118 | - private function formatLabels(Metric $metric): string { |
|
| 119 | - if (empty($metric->labels)) { |
|
| 120 | - return ''; |
|
| 121 | - } |
|
| 122 | - |
|
| 123 | - $labels = []; |
|
| 124 | - foreach ($metric->labels as $label => $value) { |
|
| 125 | - $labels[] .= $label . '=' . $this->escapeString((string)$value); |
|
| 126 | - } |
|
| 127 | - |
|
| 128 | - return '{' . implode(',', $labels) . '}'; |
|
| 129 | - } |
|
| 130 | - |
|
| 131 | - private function escapeString(string $string): string { |
|
| 132 | - return json_encode( |
|
| 133 | - $string, |
|
| 134 | - JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR, |
|
| 135 | - 1 |
|
| 136 | - ); |
|
| 137 | - } |
|
| 138 | - |
|
| 139 | - private function formatValue(Metric $metric): string { |
|
| 140 | - if (is_bool($metric->value)) { |
|
| 141 | - return $metric->value ? '1' : '0'; |
|
| 142 | - } |
|
| 143 | - if ($metric->value instanceof MetricValue) { |
|
| 144 | - return $metric->value->value; |
|
| 145 | - } |
|
| 146 | - |
|
| 147 | - return (string)$metric->value; |
|
| 148 | - } |
|
| 149 | - |
|
| 150 | - private function formatTimestamp(Metric $metric): string { |
|
| 151 | - return (string)$metric->timestamp; |
|
| 152 | - } |
|
| 93 | + } |
|
| 94 | + |
|
| 95 | + private function formatFamily(IMetricFamily $family): string { |
|
| 96 | + $output = ''; |
|
| 97 | + $name = $family->name(); |
|
| 98 | + if ($family->type() !== MetricType::unknown) { |
|
| 99 | + $output = '# TYPE nextcloud_' . $name . ' ' . $family->type()->name . "\n"; |
|
| 100 | + } |
|
| 101 | + if ($family->unit() !== '') { |
|
| 102 | + $output .= '# UNIT nextcloud_' . $name . ' ' . $family->unit() . "\n"; |
|
| 103 | + } |
|
| 104 | + if ($family->help() !== '') { |
|
| 105 | + $output .= '# HELP nextcloud_' . $name . ' ' . $family->help() . "\n"; |
|
| 106 | + } |
|
| 107 | + foreach ($family->metrics() as $metric) { |
|
| 108 | + $output .= 'nextcloud_' . $name . $this->formatLabels($metric) . ' ' . $this->formatValue($metric); |
|
| 109 | + if ($metric->timestamp !== null) { |
|
| 110 | + $output .= ' ' . $this->formatTimestamp($metric); |
|
| 111 | + } |
|
| 112 | + $output .= "\n"; |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + return $output; |
|
| 116 | + } |
|
| 117 | + |
|
| 118 | + private function formatLabels(Metric $metric): string { |
|
| 119 | + if (empty($metric->labels)) { |
|
| 120 | + return ''; |
|
| 121 | + } |
|
| 122 | + |
|
| 123 | + $labels = []; |
|
| 124 | + foreach ($metric->labels as $label => $value) { |
|
| 125 | + $labels[] .= $label . '=' . $this->escapeString((string)$value); |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + return '{' . implode(',', $labels) . '}'; |
|
| 129 | + } |
|
| 130 | + |
|
| 131 | + private function escapeString(string $string): string { |
|
| 132 | + return json_encode( |
|
| 133 | + $string, |
|
| 134 | + JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR, |
|
| 135 | + 1 |
|
| 136 | + ); |
|
| 137 | + } |
|
| 138 | + |
|
| 139 | + private function formatValue(Metric $metric): string { |
|
| 140 | + if (is_bool($metric->value)) { |
|
| 141 | + return $metric->value ? '1' : '0'; |
|
| 142 | + } |
|
| 143 | + if ($metric->value instanceof MetricValue) { |
|
| 144 | + return $metric->value->value; |
|
| 145 | + } |
|
| 146 | + |
|
| 147 | + return (string)$metric->value; |
|
| 148 | + } |
|
| 149 | + |
|
| 150 | + private function formatTimestamp(Metric $metric): string { |
|
| 151 | + return (string)$metric->timestamp; |
|
| 152 | + } |
|
| 153 | 153 | } |
@@ -81,7 +81,7 @@ discard block |
||
| 81 | 81 | yield $this->formatFamily($family); |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | - $elapsed = (string)(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']); |
|
| 84 | + $elapsed = (string) (microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']); |
|
| 85 | 85 | yield <<<SUMMARY |
| 86 | 86 | # TYPE nextcloud_exporter_run_seconds gauge |
| 87 | 87 | # UNIT nextcloud_exporter_run_seconds seconds |
@@ -96,18 +96,18 @@ discard block |
||
| 96 | 96 | $output = ''; |
| 97 | 97 | $name = $family->name(); |
| 98 | 98 | if ($family->type() !== MetricType::unknown) { |
| 99 | - $output = '# TYPE nextcloud_' . $name . ' ' . $family->type()->name . "\n"; |
|
| 99 | + $output = '# TYPE nextcloud_'.$name.' '.$family->type()->name."\n"; |
|
| 100 | 100 | } |
| 101 | 101 | if ($family->unit() !== '') { |
| 102 | - $output .= '# UNIT nextcloud_' . $name . ' ' . $family->unit() . "\n"; |
|
| 102 | + $output .= '# UNIT nextcloud_'.$name.' '.$family->unit()."\n"; |
|
| 103 | 103 | } |
| 104 | 104 | if ($family->help() !== '') { |
| 105 | - $output .= '# HELP nextcloud_' . $name . ' ' . $family->help() . "\n"; |
|
| 105 | + $output .= '# HELP nextcloud_'.$name.' '.$family->help()."\n"; |
|
| 106 | 106 | } |
| 107 | 107 | foreach ($family->metrics() as $metric) { |
| 108 | - $output .= 'nextcloud_' . $name . $this->formatLabels($metric) . ' ' . $this->formatValue($metric); |
|
| 108 | + $output .= 'nextcloud_'.$name.$this->formatLabels($metric).' '.$this->formatValue($metric); |
|
| 109 | 109 | if ($metric->timestamp !== null) { |
| 110 | - $output .= ' ' . $this->formatTimestamp($metric); |
|
| 110 | + $output .= ' '.$this->formatTimestamp($metric); |
|
| 111 | 111 | } |
| 112 | 112 | $output .= "\n"; |
| 113 | 113 | } |
@@ -122,10 +122,10 @@ discard block |
||
| 122 | 122 | |
| 123 | 123 | $labels = []; |
| 124 | 124 | foreach ($metric->labels as $label => $value) { |
| 125 | - $labels[] .= $label . '=' . $this->escapeString((string)$value); |
|
| 125 | + $labels[] .= $label.'='.$this->escapeString((string) $value); |
|
| 126 | 126 | } |
| 127 | 127 | |
| 128 | - return '{' . implode(',', $labels) . '}'; |
|
| 128 | + return '{'.implode(',', $labels).'}'; |
|
| 129 | 129 | } |
| 130 | 130 | |
| 131 | 131 | private function escapeString(string $string): string { |
@@ -144,10 +144,10 @@ discard block |
||
| 144 | 144 | return $metric->value->value; |
| 145 | 145 | } |
| 146 | 146 | |
| 147 | - return (string)$metric->value; |
|
| 147 | + return (string) $metric->value; |
|
| 148 | 148 | } |
| 149 | 149 | |
| 150 | 150 | private function formatTimestamp(Metric $metric): string { |
| 151 | - return (string)$metric->timestamp; |
|
| 151 | + return (string) $metric->timestamp; |
|
| 152 | 152 | } |
| 153 | 153 | } |