Skip to content

Commit f486e7d

Browse files
committed
chore: fine tune slack notifications for temporal interceptors
Signed-off-by: Uroš Marolt <uros@marolt.me>
1 parent c501253 commit f486e7d

3 files changed

Lines changed: 47 additions & 11 deletions

File tree

services/archetypes/worker/src/activities/activityInterceptor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class ActivityMonitoringInterceptor implements ActivityInboundCallsInterc
2929
runId: this.ctx.info.workflowExecution.runId,
3030
})
3131

32-
if (this.ctx.info.attempt > 10) {
32+
if (this.ctx.info.attempt % 50 === 0) {
3333
const message = `Activity \`${this.ctx.info.activityType}\` with id \`${this.ctx.info.activityId}\` was retried ${this.ctx.info.attempt} times!\n\n*Workflow:* ${this.ctx.info.workflowType}\n*Workflow ID:* ${this.ctx.info.workflowExecution.workflowId}\n*Task Queue:* ${this.ctx.info.taskQueue}`
3434

3535
// Fire and forget - don't await to avoid blocking the activity

services/archetypes/worker/src/activities/index.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,8 @@ async function telemetryIncrement(
2020
telemetry.increment(name, value, tags)
2121
}
2222

23-
async function slackNotify(message: string, persona?: SlackPersona) {
24-
await sendSlackNotificationAsync(
25-
SlackChannel.ALERTS,
26-
persona ?? SlackPersona.ERROR_REPORTER,
27-
'Temporal Alert',
28-
message,
29-
)
23+
async function slackNotify(message: string, persona: SlackPersona) {
24+
await sendSlackNotificationAsync(SlackChannel.ALERTS, persona, 'Temporal Alert', message)
3025
log.info('Slack notification sent from Temporal activity')
3126
}
3227

services/archetypes/worker/src/interceptors.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,48 @@
11
import {
2+
ActivityFailure,
3+
ApplicationFailure,
24
Next,
35
WorkflowExecuteInput,
46
WorkflowInboundCallsInterceptor,
57
proxyActivities,
68
workflowInfo,
79
} from '@temporalio/workflow'
810

11+
import { SlackPersona } from '@crowd/slack'
12+
913
import * as activities from './activities'
1014

1115
const activity = proxyActivities<typeof activities>({
1216
startToCloseTimeout: '10 seconds',
1317
})
1418

19+
/**
20+
* Extract detailed error information when an activity reaches retry limit
21+
*/
22+
function getActivityRetryLimitDetails(err: ActivityFailure): string {
23+
let details = `*Activity:* \`${err.activityType}\`\n`
24+
details += `*Activity ID:* \`${err.activityId || 'N/A'}\`\n`
25+
details += `*Retry State:* ${err.retryState}\n\n`
26+
27+
// Get the root cause error message and type
28+
if (err.cause) {
29+
details += `*Error:* ${err.cause.message}\n`
30+
31+
// If it's an ApplicationFailure, get the type (e.g., AxiosError)
32+
if (err.cause instanceof ApplicationFailure && err.cause.type) {
33+
details += `*Error Type:* ${err.cause.type}\n`
34+
}
35+
36+
// Add stack trace (first 10 lines for context)
37+
if (err.cause.stack) {
38+
const stackLines = err.cause.stack.split('\n').slice(0, 10)
39+
details += `\n*Stack Trace (first 10 lines):*\n\`\`\`\n${stackLines.join('\n')}\n\`\`\``
40+
}
41+
}
42+
43+
return details
44+
}
45+
1546
export class WorkflowMonitoringInterceptor implements WorkflowInboundCallsInterceptor {
1647
async execute(
1748
input: WorkflowExecuteInput,
@@ -35,9 +66,19 @@ export class WorkflowMonitoringInterceptor implements WorkflowInboundCallsInterc
3566
} catch (err) {
3667
if (err.message !== 'Workflow continued as new') {
3768
await activity.telemetryIncrement('temporal.workflow_execution_error', 1, tags)
38-
await activity.slackNotify(
39-
`Workflow ${info.workflowType} with id ${info.workflowId} failed with error: ${err.message}!`,
40-
)
69+
70+
// Only send detailed notification if it's an activity that reached retry limit
71+
if (err instanceof ActivityFailure && err.retryState === 'MAXIMUM_ATTEMPTS_REACHED') {
72+
const errorDetails = getActivityRetryLimitDetails(err)
73+
const message = `*Workflow Failed: Activity Retry Limit Reached*\n\n*Workflow:* \`${info.workflowType}\`\n*Workflow ID:* \`${info.workflowId}\`\n*Run ID:* \`${info.runId}\`\n\n${errorDetails}`
74+
75+
await activity.slackNotify(message, SlackPersona.ERROR_REPORTER)
76+
} else {
77+
// For other errors, send a simpler notification
78+
const message = `*Workflow Failed*\n\n*Workflow:* \`${info.workflowType}\`\n*Workflow ID:* \`${info.workflowId}\`\n*Run ID:* \`${info.runId}\`\n*Error:* ${err.message}`
79+
80+
await activity.slackNotify(message, SlackPersona.ERROR_REPORTER)
81+
}
4182
}
4283

4384
throw err

0 commit comments

Comments
 (0)