diff --git a/packages/backend/src/apps/postman/__tests__/actions/send-transactional-email.test.ts b/packages/backend/src/apps/postman/__tests__/actions/send-transactional-email.test.ts index 46bb781fd..ae8aacac7 100644 --- a/packages/backend/src/apps/postman/__tests__/actions/send-transactional-email.test.ts +++ b/packages/backend/src/apps/postman/__tests__/actions/send-transactional-email.test.ts @@ -73,6 +73,10 @@ vi.mock('@/models/email-suppression-entry', () => ({ }, })) +vi.mock('@/helpers/metrics', () => ({ + incrementMetric: vi.fn(), +})) + describe('send transactional email', () => { let $: IGlobalVariable diff --git a/packages/backend/src/apps/postman/common/email-helper.ts b/packages/backend/src/apps/postman/common/email-helper.ts index 78a8737f1..e3e2c425e 100644 --- a/packages/backend/src/apps/postman/common/email-helper.ts +++ b/packages/backend/src/apps/postman/common/email-helper.ts @@ -8,6 +8,7 @@ import appConfig from '@/config/app' import HttpError from '@/errors/http' import { getLdFlagValue } from '@/helpers/launch-darkly' import logger from '@/helpers/logger' +import { incrementMetric } from '@/helpers/metrics' import { getSesClient, shouldUseSes } from '@/helpers/ses-email-helper' import EmailSuppressionEntry from '@/models/email-suppression-entry' @@ -158,6 +159,7 @@ async function sendViaSes( }), }), ) + incrementMetric('ses.email.sent') // TODO: remove this log once the SES rollout is verified and stable. logger.info('Email sent via SES', { diff --git a/packages/backend/src/helpers/metrics.ts b/packages/backend/src/helpers/metrics.ts new file mode 100644 index 000000000..597f03079 --- /dev/null +++ b/packages/backend/src/helpers/metrics.ts @@ -0,0 +1,10 @@ +import tracer from './tracer' + +export function incrementMetric( + name: string, + tags: Record = {}, +): void { + tracer.dogstatsd.increment(name, 1, { + ...tags, + }) +} diff --git a/packages/backend/src/helpers/process-ses-event.ts b/packages/backend/src/helpers/process-ses-event.ts index 9e9a2dcec..dad6d594f 100644 --- a/packages/backend/src/helpers/process-ses-event.ts +++ b/packages/backend/src/helpers/process-ses-event.ts @@ -1,4 +1,5 @@ import logger from '@/helpers/logger' +import { incrementMetric } from '@/helpers/metrics' import { SesEvent, SesEventType } from '@/helpers/ses-event-parser' import EmailSuppressionEntry from '@/models/email-suppression-entry' @@ -28,6 +29,11 @@ export async function processSesEvent(data: SesEventInput): Promise { if (sesEvent.eventType === SesEventType.Bounce) { const { bounceType, bounceSubType, bouncedRecipients } = sesEvent.bounce + incrementMetric('ses.email.bounce', { + bounce_type: bounceType, + bounce_sub_type: bounceSubType ?? 'unknown', + }) + if (bounceType === 'Permanent') { // TODO: add micro-optimisation for upsertSuppression to blacklist multiple recipient emails in phase 2 for (const recipient of bouncedRecipients) { @@ -64,6 +70,10 @@ export async function processSesEvent(data: SesEventInput): Promise { if (sesEvent.eventType === SesEventType.Complaint) { const { complainedRecipients, complaintFeedbackType } = sesEvent.complaint + incrementMetric('ses.email.complaint', { + complaint_feedback_type: complaintFeedbackType ?? 'other', + }) + if (complaintFeedbackType === 'not-spam') { // Auto-whitelist: recipient marked the email as not-spam const emails = complainedRecipients.map((r) => r.emailAddress) diff --git a/packages/backend/src/helpers/ses-email-helper.ts b/packages/backend/src/helpers/ses-email-helper.ts index fb6bce954..bce77cec1 100644 --- a/packages/backend/src/helpers/ses-email-helper.ts +++ b/packages/backend/src/helpers/ses-email-helper.ts @@ -4,6 +4,7 @@ import { fromTemporaryCredentials } from '@aws-sdk/credential-providers' import appConfig from '@/config/app' import logger from './logger' +import { incrementMetric } from './metrics' let sesClient: SESv2Client | null = null @@ -82,6 +83,7 @@ export async function sendEmailViaSes({ }), }), ) + incrementMetric('ses.email.sent') } catch (e) { logger.error('Error sending email via SES', { error: e,