diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts index 6ccd6ae66a..6b3f7b6d57 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts @@ -34,7 +34,7 @@ import { EntityProviderConnection, EntityProviderMutation, } from './types'; -import { Counter, Histogram, Summary } from 'prom-client'; +import { Counter, Summary } from 'prom-client'; class Connection implements EntityProviderConnection { readonly validateEntityEnvelope = entityEnvelopeSchemaValidator(); diff --git a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts b/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts index cfc46409e8..ead857576c 100644 --- a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts +++ b/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts @@ -24,6 +24,7 @@ import type { Logger } from 'winston'; import { Transaction } from '../../database'; import { DeferredEntity } from '../processing/types'; import { RefreshIntervalFunction } from '../refresh'; +import { initDatabaseMetrics } from './metrics'; import { DbRefreshStateReferencesRow, DbRefreshStateRow, @@ -51,7 +52,9 @@ export class DefaultProcessingDatabase implements ProcessingDatabase { logger: Logger; refreshInterval: RefreshIntervalFunction; }, - ) {} + ) { + initDatabaseMetrics(options.database); + } async updateProcessedEntity( txOpaque: Transaction, diff --git a/plugins/catalog-backend/src/next/database/metrics.ts b/plugins/catalog-backend/src/next/database/metrics.ts new file mode 100644 index 0000000000..c4a5c22ab2 --- /dev/null +++ b/plugins/catalog-backend/src/next/database/metrics.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Knex } from 'knex'; +import { Gauge } from 'prom-client'; +import { DbLocationsRow } from '../../database/types'; +import { DbRefreshStateRow, DbRelationsRow } from './tables'; + +export function initDatabaseMetrics(knex: Knex) { + const seen = new Set(); + return { + entities_count: new Gauge({ + name: 'catalog_entities_count', + help: 'Total amount of entities in the catalog', + labelNames: ['kind'], + async collect() { + const result = await knex('refresh_state').select( + 'entity_ref', + ); + const results = result + .map(row => row.entity_ref.split(':')[0]) + .reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map()); + + results.forEach((value, key) => { + seen.add(key); + this.set({ kind: key }, value); + }); + + // Set all the entities that were not seen to 0 and delete them from the seen set. + seen.forEach(key => { + if (!results.has(key)) { + this.set({ kind: key }, 0); + seen.delete(key); + } + }); + }, + }), + registered_locations: new Gauge({ + name: 'catalog_registered_locations_count', + help: 'Total amount of registered locations in the catalog', + async collect() { + const total = await knex('locations').count(); + this.set(total[0]['count(*)'] as number); + }, + }), + relations: new Gauge({ + name: 'catalog_relations_count', + help: 'Total amount of relations between entities', + async collect() { + const total = await knex('relations').count(); + this.set(total[0]['count(*)'] as number); + }, + }), + }; +}