From e7c6c32f264b68e4af9711a711fe3072b4658a73 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 14 Mar 2026 13:48:07 +0100 Subject: [PATCH 1/2] Refactor Slack Notifications Backend Module to use MetricsApi Signed-off-by: Gabriel Dugny --- .changeset/solid-bats-flash.md | 5 ++++ .../package.json | 1 - .../lib/SlackNotificationProcessor.test.ts | 29 +++++++++++++++++++ .../src/lib/SlackNotificationProcessor.ts | 17 +++++++---- .../src/module.ts | 5 +++- yarn.lock | 1 - 6 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 .changeset/solid-bats-flash.md diff --git a/.changeset/solid-bats-flash.md b/.changeset/solid-bats-flash.md new file mode 100644 index 0000000000..c14603ec50 --- /dev/null +++ b/.changeset/solid-bats-flash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend-module-slack': patch +--- + +The Slack notification processor now uses the `MetricsService` to create metrics, providing plugin-scoped attribution. diff --git a/plugins/notifications-backend-module-slack/package.json b/plugins/notifications-backend-module-slack/package.json index 7cc7b280ca..55759c0428 100644 --- a/plugins/notifications-backend-module-slack/package.json +++ b/plugins/notifications-backend-module-slack/package.json @@ -42,7 +42,6 @@ "@backstage/plugin-notifications-common": "workspace:^", "@backstage/plugin-notifications-node": "workspace:^", "@backstage/types": "workspace:^", - "@opentelemetry/api": "^1.9.0", "@slack/bolt": "^3.21.4", "@slack/types": "^2.14.0", "@slack/web-api": "^7.5.0", diff --git a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts index ce7898566b..0393070b3f 100644 --- a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts +++ b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.test.ts @@ -15,6 +15,7 @@ */ import { mockServices } from '@backstage/backend-test-utils'; +import { metricsServiceMock } from '@backstage/backend-test-utils/alpha'; import { SlackNotificationProcessor } from './SlackNotificationProcessor'; import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; import { KnownBlock, WebClient } from '@slack/web-api'; @@ -125,6 +126,7 @@ const DEFAULT_ENTITIES_RESPONSE = { describe('SlackNotificationProcessor', () => { const logger = mockServices.logger.mock(); const auth = mockServices.auth(); + const metrics = metricsServiceMock.mock(); const config = mockServices.rootConfig({ data: { app: { @@ -157,6 +159,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -224,6 +227,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, blockKitRenderer: () => customBlocks, })[0]; @@ -256,6 +260,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -331,6 +336,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -365,6 +371,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -410,6 +417,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -465,6 +473,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -529,6 +538,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -584,6 +594,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -639,6 +650,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -694,6 +706,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -750,6 +763,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -809,6 +823,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -863,6 +878,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -921,6 +937,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -959,6 +976,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -982,6 +1000,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: [DEFAULT_ENTITIES_RESPONSE.items[2]], }), + metrics, slack, })[0]; @@ -1021,6 +1040,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -1066,6 +1086,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -1125,6 +1146,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -1204,6 +1226,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -1298,6 +1321,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, }, )[0]; @@ -1375,6 +1399,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -1441,6 +1466,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -1481,6 +1507,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -1520,6 +1547,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, })[0]; @@ -1568,6 +1596,7 @@ describe('SlackNotificationProcessor', () => { catalog: catalogServiceMock({ entities: DEFAULT_ENTITIES_RESPONSE.items, }), + metrics, slack, }, )[0]; diff --git a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts index de084df464..d20587f2a1 100644 --- a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts +++ b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts @@ -15,6 +15,10 @@ */ import { AuthService, LoggerService } from '@backstage/backend-plugin-api'; +import { + MetricsService, + MetricsServiceCounter, +} from '@backstage/backend-plugin-api/alpha'; import { Entity, isUserEntity, @@ -30,7 +34,6 @@ import { NotificationSendOptions, } from '@backstage/plugin-notifications-node'; import { durationToMilliseconds } from '@backstage/types'; -import { Counter, metrics } from '@opentelemetry/api'; import { ChatPostMessageArguments, WebClient } from '@slack/web-api'; import DataLoader from 'dataloader'; import pThrottle from 'p-throttle'; @@ -48,8 +51,8 @@ export class SlackNotificationProcessor implements NotificationProcessor { private readonly sendNotifications: ( opts: ChatPostMessageArguments[], ) => Promise; - private readonly messagesSent: Counter; - private readonly messagesFailed: Counter; + private readonly messagesSent: MetricsServiceCounter; + private readonly messagesFailed: MetricsServiceCounter; private readonly broadcastChannels?: string[]; private readonly broadcastRoutes?: BroadcastRoute[]; private readonly entityLoader: DataLoader; @@ -64,6 +67,7 @@ export class SlackNotificationProcessor implements NotificationProcessor { auth: AuthService; logger: LoggerService; catalog: CatalogService; + metrics: MetricsService; slack?: WebClient; broadcastChannels?: string[]; blockKitRenderer?: SlackBlockKitRenderer; @@ -103,6 +107,7 @@ export class SlackNotificationProcessor implements NotificationProcessor { auth: AuthService; logger: LoggerService; catalog: CatalogService; + metrics: MetricsService; broadcastChannels?: string[]; broadcastRoutes?: BroadcastRoute[]; username?: string; @@ -114,6 +119,7 @@ export class SlackNotificationProcessor implements NotificationProcessor { auth, catalog, logger, + metrics, slack, broadcastChannels, broadcastRoutes, @@ -159,14 +165,13 @@ export class SlackNotificationProcessor implements NotificationProcessor { }, ); - const meter = metrics.getMeter('default'); - this.messagesSent = meter.createCounter( + this.messagesSent = metrics.createCounter( 'notifications.processors.slack.sent.count', { description: 'Number of messages sent to Slack successfully', }, ); - this.messagesFailed = meter.createCounter( + this.messagesFailed = metrics.createCounter( 'notifications.processors.slack.error.count', { description: 'Number of messages that failed to send to Slack', diff --git a/plugins/notifications-backend-module-slack/src/module.ts b/plugins/notifications-backend-module-slack/src/module.ts index e3d6aebee5..88f0272eb3 100644 --- a/plugins/notifications-backend-module-slack/src/module.ts +++ b/plugins/notifications-backend-module-slack/src/module.ts @@ -17,6 +17,7 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; +import { metricsServiceRef } from '@backstage/backend-plugin-api/alpha'; import { notificationsProcessingExtensionPoint } from '@backstage/plugin-notifications-node'; import { SlackNotificationProcessor } from './lib/SlackNotificationProcessor'; import { catalogServiceRef } from '@backstage/plugin-catalog-node'; @@ -52,13 +53,15 @@ export const notificationsModuleSlack = createBackendModule({ logger: coreServices.logger, catalog: catalogServiceRef, notifications: notificationsProcessingExtensionPoint, + metrics: metricsServiceRef, }, - async init({ auth, config, logger, catalog, notifications }) { + async init({ auth, config, logger, catalog, notifications, metrics }) { notifications.addProcessor( SlackNotificationProcessor.fromConfig(config, { auth, logger, catalog, + metrics, blockKitRenderer, }), ); diff --git a/yarn.lock b/yarn.lock index 32b09cc5e3..03d6e900de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5910,7 +5910,6 @@ __metadata: "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" "@faker-js/faker": "npm:^10.0.0" - "@opentelemetry/api": "npm:^1.9.0" "@slack/bolt": "npm:^3.21.4" "@slack/types": "npm:^2.14.0" "@slack/web-api": "npm:^7.5.0" From 25e672c581f8369e78fc58d7e60f56784c50e9b5 Mon Sep 17 00:00:00 2001 From: Gabriel Dugny Date: Sat, 14 Mar 2026 14:19:11 +0100 Subject: [PATCH 2/2] Add metric unit Signed-off-by: Gabriel Dugny --- .changeset/solid-bats-flash.md | 2 +- .../src/lib/SlackNotificationProcessor.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.changeset/solid-bats-flash.md b/.changeset/solid-bats-flash.md index c14603ec50..0a4918fcc2 100644 --- a/.changeset/solid-bats-flash.md +++ b/.changeset/solid-bats-flash.md @@ -2,4 +2,4 @@ '@backstage/plugin-notifications-backend-module-slack': patch --- -The Slack notification processor now uses the `MetricsService` to create metrics, providing plugin-scoped attribution. +The Slack notification processor now uses the `MetricsService` to create metrics, providing plugin-scoped attribution. `{message}` unit has also been added. diff --git a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts index d20587f2a1..28846fcbb5 100644 --- a/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts +++ b/plugins/notifications-backend-module-slack/src/lib/SlackNotificationProcessor.ts @@ -169,12 +169,14 @@ export class SlackNotificationProcessor implements NotificationProcessor { 'notifications.processors.slack.sent.count', { description: 'Number of messages sent to Slack successfully', + unit: '{message}', }, ); this.messagesFailed = metrics.createCounter( 'notifications.processors.slack.error.count', { description: 'Number of messages that failed to send to Slack', + unit: '{message}', }, );