From 503a938fdc64f4fc76e84bfcbd5ff2df6a2b0dbf Mon Sep 17 00:00:00 2001 From: Spencer Amarantides Date: Thu, 12 Oct 2023 09:38:16 -0400 Subject: [PATCH] Update log alerts (#6847) ## Summary The log alerts and comment alerts were not updated to the new styling. Give them an update: Log alert Screenshot 2023-10-11 at 2 25 12 PM Screenshot 2023-10-11 at 2 33 16 PM Comment alert Screenshot 2023-10-11 at 2 59 34 PM ## How did you test this change? 1) Create a new log alert that is easy to fire 2) Add a Discord and Slack channel to alert 3) Run the log alert watcher locally: `make start-log-alerts-watch` - [ ] Confirm alert is sent to Slack - [ ] Confirm alert is sent to Discord 4) Comment on a session - [ ] Confirm alert is sent to appropriate Slack channel 5) Reply to comment - [ ] Confirm alert is sent to appropriate Slack channel ## Are there any deployment considerations? N/A ## Does this work require review from our design team? Yes --- .../alerts/integrations/discord/messages.go | 1 + backend/jobs/log-alerts/log-alerts.go | 11 ++- backend/model/model.go | 40 +++++++++-- backend/private-graph/graph/resolver.go | 72 ++++++++++--------- 4 files changed, 78 insertions(+), 46 deletions(-) diff --git a/backend/alerts/integrations/discord/messages.go b/backend/alerts/integrations/discord/messages.go index c81bbb37eb7d..891ee9249b59 100644 --- a/backend/alerts/integrations/discord/messages.go +++ b/backend/alerts/integrations/discord/messages.go @@ -441,6 +441,7 @@ func (bot *Bot) SendLogAlert(channelId string, payload integrations.LogAlertPayl embed := newMessageEmbed() embed.Title = "Highlight Log Alert" + embed.Color = RED_ALERT embed.Description = fmt.Sprintf("*%s* is currently %s the threshold.", payload.Name, aboveStr) embed.Fields = fields diff --git a/backend/jobs/log-alerts/log-alerts.go b/backend/jobs/log-alerts/log-alerts.go index 53fe039e70c3..34fa01b4e0f9 100644 --- a/backend/jobs/log-alerts/log-alerts.go +++ b/backend/jobs/log-alerts/log-alerts.go @@ -152,19 +152,18 @@ func processLogAlert(ctx context.Context, DB *gorm.DB, TDB timeseries.DB, MailCl if alert.Query != "" { queryStr = fmt.Sprintf(`for query *%s* `, alert.Query) } - message := fmt.Sprintf( - "🚨 *%s* fired!\nLog count %swas %s the threshold.\n"+ + body := fmt.Sprintf( + "Log count %swas %s the threshold.\n"+ "_Count_: %d | _Threshold_: %d", - alert.Name, queryStr, aboveStr, count, alert.CountThreshold, ) - log.WithContext(ctx).Info(message) + log.WithContext(ctx).WithField("alert_id", alert.ID).Info(fmt.Sprintf("Firing alert for %s", alert.Name)) - if err := alert.SendSlackAlert(ctx, DB, &model.SendSlackAlertForLogAlertInput{Message: message, Workspace: &workspace, StartDate: start, EndDate: end}); err != nil { + if err := alert.SendSlackAlert(ctx, DB, &model.SendSlackAlertForLogAlertInput{Body: body, Workspace: &workspace, StartDate: start, EndDate: end}); err != nil { log.WithContext(ctx).Error("error sending slack alert for metric monitor", err) } @@ -190,7 +189,7 @@ func processLogAlert(ctx context.Context, DB *gorm.DB, TDB timeseries.DB, MailCl if alert.Query != "" { queryStr = fmt.Sprintf(`for query %s `, alert.Query) } - message = fmt.Sprintf( + message := fmt.Sprintf( "%s fired! Log count %sis currently %s the threshold.
"+ "Count: %d | Threshold: %d"+ "

"+ diff --git a/backend/model/model.go b/backend/model/model.go index efd9e759ec0c..69fde4894b56 100644 --- a/backend/model/model.go +++ b/backend/model/model.go @@ -2599,7 +2599,7 @@ func (obj *MetricMonitor) SendSlackAlert(ctx context.Context, input *SendSlackAl } type SendSlackAlertForLogAlertInput struct { - Message string + Body string Workspace *Workspace StartDate time.Time EndDate time.Time @@ -2637,12 +2637,42 @@ func (obj *LogAlert) SendSlackAlert(ctx context.Context, db *gorm.DB, input *Sen alertUrl := GetLogAlertURL(obj.ProjectID, obj.Query, input.StartDate, input.EndDate) + previewText := fmt.Sprintf("%s fired!", obj.Name) + + var headerBlockSet []slack.Block + headerBlock := slack.NewTextBlockObject(slack.MarkdownType, fmt.Sprintf("*%s* fired!", obj.Name), false, false) + headerBlockSet = append(headerBlockSet, slack.NewSectionBlock(headerBlock, nil, nil)) + + var bodyBlockSet []slack.Block + logBlock := slack.NewTextBlockObject(slack.MarkdownType, input.Body, false, false) + + var actionBlocks []slack.BlockElement + button := slack.NewButtonBlockElement( + "", + "click", + slack.NewTextBlockObject( + slack.PlainTextType, + "View Logs", + false, + false, + ), + ) + button.URL = alertUrl + actionBlocks = append(actionBlocks, button) + + bodyBlockSet = append(bodyBlockSet, slack.NewSectionBlock(logBlock, nil, nil)) + bodyBlockSet = append(bodyBlockSet, slack.NewActionBlock("", actionBlocks...)) + + attachment := &slack.Attachment{ + Color: RED_ALERT, + Blocks: slack.Blocks{BlockSet: bodyBlockSet}, + } + log.WithContext(ctx).Info("Sending Slack Alert for Log Alert") // send message for _, channel := range channels { if channel.WebhookChannel != nil { - message := fmt.Sprintf("%s\n<%s|View Logs>", input.Message, alertUrl) slackChannelId := *channel.WebhookChannelID slackChannelName := *channel.WebhookChannel @@ -2655,12 +2685,12 @@ func (obj *LogAlert) SendSlackAlert(ctx context.Context, db *gorm.DB, input *Sen log.WithContext(ctx).WithFields(log.Fields{"project_id": obj.ProjectID}).Error(e.Wrap(err, "failed to join slack channel while sending welcome message")) } } - _, _, err := slackClient.PostMessage(slackChannelId, slack.MsgOptionText(message, false), + _, _, err := slackClient.PostMessage(slackChannelId, slack.MsgOptionText(previewText, false), slack.MsgOptionBlocks(headerBlockSet...), slack.MsgOptionAttachments(*attachment), slack.MsgOptionDisableLinkUnfurl(), /** Disables showing a preview of any links that are in the Slack message.*/ slack.MsgOptionDisableMediaUnfurl(), /** Disables showing a preview of any links that are in the Slack message.*/ ) if err != nil { - log.WithContext(ctx).WithFields(log.Fields{"workspace_id": input.Workspace.ID, "message": fmt.Sprintf("%+v", message)}). + log.WithContext(ctx).WithFields(log.Fields{"workspace_id": input.Workspace.ID, "message": previewText}). Error(e.Wrap(err, "error sending slack msg via bot api for welcome message")) } @@ -2882,7 +2912,7 @@ func (obj *Alert) sendSlackAlert(ctx context.Context, db *gorm.DB, alertID int, var headerBlock *slack.TextBlockObject if input.FirstErrorAlert { previewText = fmt.Sprintf("New Error Alert: %s", previewEvent) - headerBlock = slack.NewTextBlockObject(slack.MarkdownType, fmt.Sprintf("*❇️ New Error Alert: %d Recent Occurrences*", *input.ErrorsCount), false, false) + headerBlock = slack.NewTextBlockObject(slack.MarkdownType, fmt.Sprintf("*New Error Alert: %d Recent Occurrences ❇️*", *input.ErrorsCount), false, false) attachmentColor = YELLOW_ALERT } else { previewText = fmt.Sprintf("Error Alert: %s", previewEvent) diff --git a/backend/private-graph/graph/resolver.go b/backend/private-graph/graph/resolver.go index 376ae91fb81b..b416964f602d 100644 --- a/backend/private-graph/graph/resolver.go +++ b/backend/private-graph/graph/resolver.go @@ -931,13 +931,15 @@ func (r *Resolver) SendEmailAlert( return nil } -func (r *Resolver) CreateSlackBlocks(admin *model.Admin, viewLink, commentText, action string, subjectScope string, additionalContext *string) (blockSet slack.Blocks) { +func (r *Resolver) CreateSlackBlocks(admin *model.Admin, viewLink, commentText, action string, subjectScope string, additionalContext *string) ([]slack.Block, *slack.Attachment) { determiner := "a" if subjectScope == "error" { determiner = "an" } // Header + var headerBlockSet []slack.Block + message := fmt.Sprintf("*You were %s in %s %s comment.*", action, determiner, subjectScope) if admin.Email != nil && *admin.Email != "" { message = fmt.Sprintf("*%s %s you in %s %s comment.*", *admin.Email, action, determiner, subjectScope) @@ -946,38 +948,30 @@ func (r *Resolver) CreateSlackBlocks(admin *model.Admin, viewLink, commentText, message = fmt.Sprintf("*%s %s you in %s %s comment.*", *admin.Name, action, determiner, subjectScope) } - blockSet.BlockSet = append(blockSet.BlockSet, - slack.NewSectionBlock( - &slack.TextBlockObject{Type: slack.MarkdownType, Text: message}, - nil, nil, - ), - ) + headerBlock := slack.NewTextBlockObject(slack.MarkdownType, message, false, false) + headerBlockSet = append(headerBlockSet, slack.NewSectionBlock(headerBlock, nil, nil)) // comment message - blockSet.BlockSet = append(blockSet.BlockSet, - slack.NewSectionBlock( - &slack.TextBlockObject{Type: slack.MarkdownType, Text: fmt.Sprintf("> %s", commentText)}, - nil, nil, - ), - ) + var bodyBlockSet []slack.Block + commentBlock := slack.NewTextBlockObject(slack.MarkdownType, fmt.Sprintf("> %s", commentText), false, false) + + detailsString := "" // info on the session/error if subjectScope == "error" { - blockSet.BlockSet = append(blockSet.BlockSet, - slack.NewSectionBlock( - &slack.TextBlockObject{Type: slack.MarkdownType, Text: fmt.Sprintf("*Error* %s\n %s", viewLink, *additionalContext)}, - nil, nil, - ), - ) + detailsString = fmt.Sprintf("*Error* %s", viewLink) } else if subjectScope == "session" { - blockSet.BlockSet = append(blockSet.BlockSet, - slack.NewSectionBlock( - &slack.TextBlockObject{Type: slack.MarkdownType, Text: fmt.Sprintf("*Session* %s\n%s", viewLink, *additionalContext)}, - nil, nil, - ), - ) + detailsString = fmt.Sprintf("*Session* %s", viewLink) + } + if additionalContext != nil { + detailsString = fmt.Sprintf("%s\n%s", detailsString, *additionalContext) } + detailsBlock := slack.NewTextBlockObject(slack.MarkdownType, detailsString, false, false) + + // button + var actionBlocks []slack.BlockElement + button := slack.NewButtonBlockElement( "", "click", @@ -988,14 +982,20 @@ func (r *Resolver) CreateSlackBlocks(admin *model.Admin, viewLink, commentText, false, ), ) - button.WithStyle("primary") button.URL = viewLink - blockSet.BlockSet = append(blockSet.BlockSet, - slack.NewActionBlock("action_block", button), - ) - blockSet.BlockSet = append(blockSet.BlockSet, slack.NewDividerBlock()) - return + actionBlocks = append(actionBlocks, button) + + bodyBlockSet = append(bodyBlockSet, slack.NewSectionBlock(commentBlock, nil, nil)) + bodyBlockSet = append(bodyBlockSet, slack.NewSectionBlock(detailsBlock, nil, nil)) + bodyBlockSet = append(bodyBlockSet, slack.NewActionBlock("", actionBlocks...)) + + attachment := &slack.Attachment{ + Color: "#6c37f4", + Blocks: slack.Blocks{BlockSet: bodyBlockSet}, + } + + return headerBlockSet, attachment } func (r *Resolver) SendSlackThreadReply(ctx context.Context, workspace *model.Workspace, admin *model.Admin, viewLink, commentText, action string, subjectScope string, threadIDs []int) error { @@ -1003,14 +1003,15 @@ func (r *Resolver) SendSlackThreadReply(ctx context.Context, workspace *model.Wo return nil } slackClient := slack.New(*workspace.SlackAccessToken) - blocks := r.CreateSlackBlocks(admin, viewLink, commentText, action, subjectScope, nil) + headerBlock, bodyAttachment := r.CreateSlackBlocks(admin, viewLink, commentText, action, subjectScope, nil) for _, threadID := range threadIDs { thread := &model.CommentSlackThread{} if err := r.DB.Where(&model.CommentSlackThread{Model: model.Model{ID: threadID}}).Take(&thread).Error; err != nil { return e.Wrap(err, "error querying slack thread") } opts := []slack.MsgOption{ - slack.MsgOptionBlocks(blocks.BlockSet...), + slack.MsgOptionBlocks(headerBlock...), + slack.MsgOptionAttachments(*bodyAttachment), slack.MsgOptionDisableLinkUnfurl(), /** Disables showing a preview of any links that are in the Slack message.*/ slack.MsgOptionDisableMediaUnfurl(), /** Disables showing a preview of any media that are in the Slack message.*/ slack.MsgOptionTS(thread.ThreadTS), @@ -1031,7 +1032,7 @@ func (r *Resolver) SendSlackAlertToUser(ctx context.Context, workspace *model.Wo } slackClient := slack.New(*workspace.SlackAccessToken) - blocks := r.CreateSlackBlocks(admin, viewLink, commentText, action, subjectScope, additionalContext) + headerBlock, bodyAttachment := r.CreateSlackBlocks(admin, viewLink, commentText, action, subjectScope, additionalContext) // Prepare to upload the screenshot to the user's Slack workspace. // We do this instead of upload it to S3 or somewhere else to defer authorization checks to Slack. @@ -1071,7 +1072,8 @@ func (r *Resolver) SendSlackAlertToUser(ctx context.Context, workspace *model.Wo log.WithContext(ctx).Warn(e.Wrap(err, "failed to join slack channel")) } opts := []slack.MsgOption{ - slack.MsgOptionBlocks(blocks.BlockSet...), + slack.MsgOptionBlocks(headerBlock...), + slack.MsgOptionAttachments(*bodyAttachment), slack.MsgOptionDisableLinkUnfurl(), /** Disables showing a preview of any links that are in the Slack message.*/ slack.MsgOptionDisableMediaUnfurl(), /** Disables showing a preview of any media that are in the Slack message.*/ }