diff --git a/.changeset/evil-seals-smell.md b/.changeset/evil-seals-smell.md new file mode 100644 index 0000000000..a533720434 --- /dev/null +++ b/.changeset/evil-seals-smell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Migrated OpenTelemetry metrics to use the `MetricsService` from `@backstage/backend-plugin-api/alpha` instead of the raw `@opentelemetry/api` meter. diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index ec7bdba97c..3ff1c1b9c2 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -78,7 +78,6 @@ "@backstage/plugin-scaffolder-common": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "@backstage/types": "workspace:^", - "@opentelemetry/api": "^1.9.0", "@types/luxon": "^3.0.0", "express": "^4.22.0", "fs-extra": "^11.2.0", diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index 17997da485..6cef0d3cde 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -63,6 +63,7 @@ import { import { actionsServiceRef, actionsRegistryServiceRef, + metricsServiceRef, } from '@backstage/backend-plugin-api/alpha'; import { createScaffolderActions } from './actions'; @@ -151,6 +152,7 @@ export const scaffolderPlugin = createBackendPlugin({ actionsRegistry: actionsServiceRef, actionsRegistryService: actionsRegistryServiceRef, scaffolderService: scaffolderServiceRef, + metrics: metricsServiceRef, }, async init({ logger, @@ -168,6 +170,7 @@ export const scaffolderPlugin = createBackendPlugin({ actionsRegistry, actionsRegistryService, scaffolderService, + metrics, }) { const log = loggerToWinstonLogger(logger); const integrations = ScmIntegrations.fromConfig(config); @@ -244,6 +247,7 @@ export const scaffolderPlugin = createBackendPlugin({ events, auditor, actionsRegistry, + metrics, }); httpRouter.use(router); }, diff --git a/plugins/scaffolder-backend/src/scaffolder/dryrun/createDryRunner.ts b/plugins/scaffolder-backend/src/scaffolder/dryrun/createDryRunner.ts index e01e76064e..1c56248081 100644 --- a/plugins/scaffolder-backend/src/scaffolder/dryrun/createDryRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/dryrun/createDryRunner.ts @@ -19,6 +19,7 @@ import { BackstageCredentials, LoggerService, } from '@backstage/backend-plugin-api'; +import type { MetricsService } from '@backstage/backend-plugin-api/alpha'; import type { UserEntity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { ScmIntegrations } from '@backstage/integration'; @@ -81,6 +82,7 @@ export type TemplateTesterCreateOptions = { additionalTemplateGlobals?: Record; permissions?: PermissionEvaluator; config?: Config; + metrics: MetricsService; }; /** diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts index c063e38cb2..6b07936e57 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts @@ -39,7 +39,10 @@ import { mockCredentials, mockServices, } from '@backstage/backend-test-utils'; -import { actionsRegistryServiceMock } from '@backstage/backend-test-utils/alpha'; +import { + actionsRegistryServiceMock, + metricsServiceMock, +} from '@backstage/backend-test-utils/alpha'; describe('NunjucksWorkflowRunner', () => { let actionRegistry: TemplateActionRegistry; @@ -249,6 +252,7 @@ describe('NunjucksWorkflowRunner', () => { logger, permissions: mockedPermissionApi, config, + metrics: metricsServiceMock.mock(), }); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index 589ad2e204..a6a2eb2483 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -23,7 +23,6 @@ import { TaskStep, } from '@backstage/plugin-scaffolder-common'; import { JsonArray, JsonObject, JsonValue } from '@backstage/types'; -import { metrics } from '@opentelemetry/api'; import fs from 'fs-extra'; import { validate as validateJsonSchema } from 'jsonschema'; import nunjucks from 'nunjucks'; @@ -42,6 +41,7 @@ import type { LoggerService, PermissionsService, } from '@backstage/backend-plugin-api'; +import type { MetricsService } from '@backstage/backend-plugin-api/alpha'; import { UserEntity } from '@backstage/catalog-model'; import { AuthorizeResult, @@ -78,6 +78,7 @@ type NunjucksWorkflowRunnerOptions = { additionalTemplateGlobals?: Record; permissions?: PermissionsService; config?: Config; + metrics: MetricsService; }; type TemplateContext = { @@ -188,6 +189,8 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { secrets?: Record; } = { parameters: {}, secrets: {} }; + private readonly tracker: ReturnType; + constructor(options: NunjucksWorkflowRunnerOptions) { this.options = options; this.defaultTemplateFilters = convertFiltersToRecord( @@ -195,10 +198,9 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { integrations: this.options.integrations, }), ); + this.tracker = scaffoldingTracker(options.metrics); } - private readonly tracker = scaffoldingTracker(); - async getEnvironmentConfig(): Promise<{ parameters: JsonObject; secrets?: TaskSecrets; @@ -700,7 +702,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { } } -function scaffoldingTracker() { +function scaffoldingTracker(metrics: MetricsService) { // prom-client metrics are deprecated in favour of OpenTelemetry metrics. const promTaskCount = createCounterMetric({ name: 'scaffolder_task_count', @@ -723,23 +725,22 @@ function scaffoldingTracker() { labelNames: ['template', 'step', 'result'], }); - const meter = metrics.getMeter('default'); - const taskCount = meter.createCounter('scaffolder.task.count', { - description: 'Count of task runs', + const taskCount = metrics.createCounter('scaffolder.task.count', { + description: 'Total number of scaffolder tasks executed', }); - const taskDuration = meter.createHistogram('scaffolder.task.duration', { - description: 'Duration of a task run', - unit: 'seconds', + const taskDuration = metrics.createHistogram('scaffolder.task.duration', { + description: 'Time taken to complete a scaffolder task end-to-end', + unit: 's', }); - const stepCount = meter.createCounter('scaffolder.step.count', { - description: 'Count of step runs', + const stepCount = metrics.createCounter('scaffolder.step.count', { + description: 'Total number of individual scaffolder action steps executed', }); - const stepDuration = meter.createHistogram('scaffolder.step.duration', { - description: 'Duration of a step runs', - unit: 'seconds', + const stepDuration = metrics.createHistogram('scaffolder.step.duration', { + description: 'Time taken to complete a single scaffolder action step', + unit: 's', }); async function taskStart(task: TaskContext) { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts index 578786cc65..71ed6b9eb4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts @@ -36,6 +36,7 @@ import { WorkflowRunner } from './types'; import ObservableImpl from 'zen-observable'; import waitForExpect from 'wait-for-expect'; import { mockServices } from '@backstage/backend-test-utils'; +import { metricsServiceMock } from '@backstage/backend-test-utils/alpha'; import { loggerToWinstonLogger } from '../../util/loggerToWinstonLogger'; jest.mock('./NunjucksWorkflowRunner'); @@ -93,6 +94,7 @@ describe('TaskWorker', () => { integrations, taskBroker: broker, actionRegistry, + metrics: metricsServiceMock.mock(), }); await broker.dispatch({ @@ -124,6 +126,7 @@ describe('TaskWorker', () => { integrations, taskBroker: broker, actionRegistry, + metrics: metricsServiceMock.mock(), }); const { taskId } = await broker.dispatch({ @@ -174,6 +177,7 @@ describe('TaskWorker', () => { }, }, }), + metrics: metricsServiceMock.mock(), }); await taskWorker.runOneTask({ @@ -261,6 +265,7 @@ describe('Concurrent TaskWorker', () => { taskBroker: broker, actionRegistry, concurrentTasksLimit: expectedConcurrentTasks, + metrics: metricsServiceMock.mock(), }); taskWorker.start(); @@ -307,6 +312,7 @@ describe('Cancellable TaskWorker', () => { integrations, taskBroker, actionRegistry, + metrics: metricsServiceMock.mock(), }); const steps = [...Array(10)].map(n => ({ diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index b98badd9f6..fdae61a84b 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -15,6 +15,7 @@ */ import { AuditorService, LoggerService } from '@backstage/backend-plugin-api'; +import type { MetricsService } from '@backstage/backend-plugin-api/alpha'; import { assertError, InputError, stringifyError } from '@backstage/errors'; import { ScmIntegrations } from '@backstage/integration'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; @@ -78,6 +79,7 @@ export type CreateWorkerOptions = { additionalTemplateGlobals?: Record; permissions?: PermissionEvaluator; gracefulShutdown?: boolean; + metrics: MetricsService; }; /** @@ -123,6 +125,7 @@ export class TaskWorker { additionalTemplateGlobals, permissions, gracefulShutdown, + metrics, } = options; const workflowRunner = new NunjucksWorkflowRunner({ @@ -135,6 +138,7 @@ export class TaskWorker { additionalTemplateGlobals, permissions, config, + metrics, }); return new TaskWorker({ diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index e778645983..9342385e15 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -58,7 +58,10 @@ import { import { createDefaultFilters } from '../lib/templating/filters/createDefaultFilters'; import { createRouter } from './router'; import { DatabaseTaskStore } from '../scaffolder/tasks/DatabaseTaskStore'; -import { actionsRegistryServiceMock } from '@backstage/backend-test-utils/alpha'; +import { + actionsRegistryServiceMock, + metricsServiceMock, +} from '@backstage/backend-test-utils/alpha'; import { ActionsService } from '@backstage/backend-plugin-api/alpha'; function createDatabase(): DatabaseService { @@ -229,6 +232,7 @@ const createTestRouter = async ( createDebugLogAction(), ], actionsRegistry: overrides.actionsRegistry ?? actionsRegistryServiceMock(), + metrics: metricsServiceMock.mock(), }); router.use(mockErrorHandler()); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 9e84b38e39..a393df253e 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -131,7 +131,10 @@ import { scaffolderTaskRules, scaffolderTemplateRules, } from './rules'; -import { ActionsService } from '@backstage/backend-plugin-api/alpha'; +import { + ActionsService, + MetricsService, +} from '@backstage/backend-plugin-api/alpha'; /** * RouterOptions @@ -165,6 +168,7 @@ export interface RouterOptions { auditor?: AuditorService; autocompleteHandlers?: Record; actionsRegistry: ActionsService; + metrics: MetricsService; } function isSupportedTemplate(entity: TemplateEntityV1beta3) { @@ -256,6 +260,7 @@ export async function createRouter( httpAuth, auditor, actionsRegistry, + metrics, } = options; const concurrentTasksLimit = @@ -344,6 +349,7 @@ export async function createRouter( concurrentTasksLimit, permissions, gracefulShutdown, + metrics, ...templateExtensions, }); @@ -375,6 +381,7 @@ export async function createRouter( workingDirectory, permissions, config, + metrics, ...templateExtensions, }); diff --git a/yarn.lock b/yarn.lock index 5036df2285..a681fdee50 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6818,7 +6818,6 @@ __metadata: "@backstage/plugin-scaffolder-node-test-utils": "workspace:^" "@backstage/repo-tools": "workspace:^" "@backstage/types": "workspace:^" - "@opentelemetry/api": "npm:^1.9.0" "@types/express": "npm:^4.17.6" "@types/fs-extra": "npm:^11.0.0" "@types/luxon": "npm:^3.0.0"