diff --git a/server/channels/api4/system.go b/server/channels/api4/system.go index c6837816b4..d19d47f401 100644 --- a/server/channels/api4/system.go +++ b/server/channels/api4/system.go @@ -673,13 +673,13 @@ func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) { } if ack.NotificationType == model.PushTypeMessage { - c.App.CountNotificationAck(model.NotificationTypePush) + c.App.CountNotificationAck(model.NotificationTypePush, ack.ClientPlatform) } err := c.App.SendAckToPushProxy(&ack) if ack.IsIdLoaded { if err != nil { - c.App.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxySendError) + c.App.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxySendError, ack.ClientPlatform) c.App.NotificationsLog().Error("Notification ack not sent to push proxy", mlog.String("type", model.NotificationTypePush), mlog.String("status", model.NotificationStatusError), @@ -693,7 +693,7 @@ func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) { mlog.Err(err), ) } else { - c.App.CountNotificationReason(model.NotificationStatusSuccess, model.NotificationTypePush, model.NotificationReason("")) + c.App.CountNotificationReason(model.NotificationStatusSuccess, model.NotificationTypePush, model.NotificationReason(""), ack.ClientPlatform) } // Return post data only when PostId is passed. if ack.PostId != "" && ack.NotificationType == model.PushTypeMessage { @@ -725,7 +725,7 @@ func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) { return } - c.App.CountNotificationReason(model.NotificationStatusSuccess, model.NotificationTypePush, model.NotificationReason("")) + c.App.CountNotificationReason(model.NotificationStatusSuccess, model.NotificationTypePush, model.NotificationReason(""), ack.ClientPlatform) ReturnStatusOK(w) } diff --git a/server/channels/app/app_iface.go b/server/channels/app/app_iface.go index 23253a91b0..45f6513de3 100644 --- a/server/channels/app/app_iface.go +++ b/server/channels/app/app_iface.go @@ -511,9 +511,9 @@ type AppIface interface { ConvertGroupMessageToChannel(c request.CTX, convertedByUserId string, gmConversionRequest *model.GroupMessageConversionRequestBody) (*model.Channel, *model.AppError) CopyFileInfos(rctx request.CTX, userID string, fileIDs []string) ([]string, *model.AppError) CopyWranglerPostlist(c request.CTX, wpl *model.WranglerPostList, targetChannel *model.Channel) (*model.Post, *model.AppError) - CountNotification(notificationType model.NotificationType) - CountNotificationAck(notificationType model.NotificationType) - CountNotificationReason(notificationStatus model.NotificationStatus, notificationType model.NotificationType, notificationReason model.NotificationReason) + CountNotification(notificationType model.NotificationType, platform string) + CountNotificationAck(notificationType model.NotificationType, platform string) + CountNotificationReason(notificationStatus model.NotificationStatus, notificationType model.NotificationType, notificationReason model.NotificationReason, platform string) CreateChannel(c request.CTX, channel *model.Channel, addMember bool) (*model.Channel, *model.AppError) CreateChannelBookmark(c request.CTX, newBookmark *model.ChannelBookmark, connectionId string) (*model.ChannelBookmarkWithFileInfo, *model.AppError) CreateChannelWithUser(c request.CTX, channel *model.Channel, userID string) (*model.Channel, *model.AppError) diff --git a/server/channels/app/expirynotify.go b/server/channels/app/expirynotify.go index 81f04ed46f..196a24c1b9 100644 --- a/server/channels/app/expirynotify.go +++ b/server/channels/app/expirynotify.go @@ -24,7 +24,7 @@ func (a *App) NotifySessionsExpired() error { // Get all mobile sessions that expired within the last hour. sessions, err := a.ch.srv.Store().Session().GetSessionsExpired(OneHourMillis, true, true) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Cannot get sessions expired", mlog.String("type", model.NotificationTypePush), mlog.String("status", model.NotificationStatusError), @@ -47,7 +47,7 @@ func (a *App) NotifySessionsExpired() error { errPush := a.sendToPushProxy(tmpMessage, session) if errPush != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxySendError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxySendError, tmpMessage.Platform) a.NotificationsLog().Error("Failed to send to push proxy", mlog.String("type", model.NotificationTypePush), mlog.String("status", model.NotificationStatusNotSent), diff --git a/server/channels/app/notification.go b/server/channels/app/notification.go index f59739f22d..bc571ff454 100644 --- a/server/channels/app/notification.go +++ b/server/channels/app/notification.go @@ -97,7 +97,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea pResult := <-pchan if pResult.NErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Error fetching profiles", mlog.String("sender_id", sender.Id), mlog.String("post_id", post.Id), @@ -111,7 +111,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea cmnResult := <-cmnchan if cmnResult.NErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Error fetching notify props", mlog.String("sender_id", sender.Id), mlog.String("post_id", post.Id), @@ -127,7 +127,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea if tchan != nil { tResult := <-tchan if tResult.NErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Error fetching thread followers", mlog.String("sender_id", sender.Id), mlog.String("post_id", post.Id), @@ -146,7 +146,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea if gchan != nil { gResult := <-gchan if gResult.NErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Error fetching group mentions", mlog.String("sender_id", sender.Id), mlog.String("post_id", post.Id), @@ -173,7 +173,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea group := groups[groupID] anyUsersMentionedByGroup, err := a.insertGroupMentions(sender.Id, group, channel, profileMap, mentions) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Failed to populate group mentions", mlog.String("sender_id", sender.Id), mlog.String("post_id", post.Id), @@ -382,7 +382,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea for _, id := range emailRecipients { if profileMap[id] == nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeEmail, model.NotificationReasonMissingProfile) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeEmail, model.NotificationReasonMissingProfile, model.NotificationNoPlatform) a.NotificationsLog().Error("Missing profile", mlog.String("type", model.NotificationTypeEmail), mlog.String("post_id", post.Id), @@ -396,7 +396,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea //If email verification is required and user email is not verified don't send email. if *a.Config().EmailSettings.RequireEmailVerification && !profileMap[id].EmailVerified { - a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypeEmail, model.NotificationReasonEmailNotVerified) + a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypeEmail, model.NotificationReasonEmailNotVerified, model.NotificationNoPlatform) a.NotificationsLog().Debug("Email not verified", mlog.String("type", model.NotificationTypeEmail), mlog.String("post_id", post.Id), @@ -415,7 +415,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea c.Logger().Warn("Unable to get the sender user profile image.", mlog.String("user_id", sender.Id), mlog.Err(err)) } if err := a.sendNotificationEmail(c, notification, profileMap[id], team, senderProfileImage); err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeEmail, model.NotificationReasonEmailSendError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeEmail, model.NotificationReasonEmailSendError, model.NotificationNoPlatform) a.NotificationsLog().Error("Error sending email notification", mlog.String("type", model.NotificationTypeEmail), mlog.String("post_id", post.Id), @@ -448,7 +448,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea // Check for channel-wide mentions in channels that have too many members for those to work if int64(len(profileMap)) > *a.Config().TeamSettings.MaxNotificationsPerChannel { - a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypeAll, model.NotificationReasonTooManyUsersInChannel) + a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypeAll, model.NotificationReasonTooManyUsersInChannel, model.NotificationNoPlatform) a.NotificationsLog().Debug("Too many users to notify - will send ephemeral message", mlog.String("sender_id", sender.Id), mlog.String("post_id", post.Id), @@ -504,7 +504,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea for _, id := range mentionedUsersList { if profileMap[id] == nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonMissingProfile) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonMissingProfile, model.NotificationNoPlatform) a.NotificationsLog().Error("Missing profile", mlog.String("type", model.NotificationTypePush), mlog.String("post_id", post.Id), @@ -556,7 +556,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea for _, id := range allActivityPushUserIds { if profileMap[id] == nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonMissingProfile) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonMissingProfile, model.NotificationNoPlatform) a.NotificationsLog().Error("Missing profile", mlog.String("type", model.NotificationTypePush), mlog.String("post_id", post.Id), @@ -600,7 +600,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea for _, id := range notificationsForCRT.Push { if profileMap[id] == nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonMissingProfile) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonMissingProfile, model.NotificationNoPlatform) a.NotificationsLog().Error("Missing profile", mlog.String("type", model.NotificationTypePush), mlog.String("post_id", post.Id), @@ -627,7 +627,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea model.CommentsNotifyCRT, ) } else { - a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypePush, statusReason) + a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypePush, statusReason, model.NotificationNoPlatform) a.NotificationsLog().Debug("Notification not sent - status", mlog.String("type", model.NotificationTypePush), mlog.String("post_id", post.Id), @@ -703,7 +703,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea published, err := a.publishWebsocketEventForPermalinkPost(c, post, message) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Couldn't send websocket notification for permalink post", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -718,7 +718,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea removePermalinkMetadataFromPost(post) postJSON, jsonErr := post.ToJSON() if jsonErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonParseError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonParseError, model.NotificationNoPlatform) a.NotificationsLog().Error("JSON parse error", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -743,7 +743,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea // This also sometimes happens when bots, which will never show up in the map, reply to threads // Their own post goes through this and they get "notified", which we don't need to count as an error if they can't if uid != post.UserId { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonMissingProfile) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonMissingProfile, model.NotificationNoPlatform) a.NotificationsLog().Error("Missing profile", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -761,7 +761,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea if threadMembership == nil { tm, err := a.Srv().Store().Thread().GetMembershipForUser(uid, post.RootId) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Missing thread membership", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -774,7 +774,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea return nil, errors.Wrapf(err, "Missing thread membership for participant in notifications. user_id=%q thread_id=%q", uid, post.RootId) } if tm == nil { - a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypeWebsocket, model.NotificationReasonMissingThreadMembership) + a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypeWebsocket, model.NotificationReasonMissingThreadMembership, model.NotificationNoPlatform) a.NotificationsLog().Warn("Missing thread membership", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -789,7 +789,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea } userThread, err := a.Srv().Store().Thread().GetThreadForUser(threadMembership, true, a.IsPostPriorityEnabled()) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Missing thread", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -823,7 +823,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea // should set unread mentions, and unread replies to 0 _, err = a.Srv().Store().Thread().MaintainMembership(uid, post.RootId, opts) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Failed to update thread membership", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -843,7 +843,7 @@ func (a *App) SendNotifications(c request.CTX, post *model.Post, team *model.Tea sanitizedPost, err := a.SanitizePostMetadataForUser(c, userThread.Post, uid) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonParseError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonParseError, model.NotificationNoPlatform) a.NotificationsLog().Error("Failed to sanitize metadata", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -1735,26 +1735,27 @@ func ShouldAckWebsocketNotification(channelType model.ChannelType, userNotificat return false } -func (a *App) CountNotification(notificationType model.NotificationType) { +func (a *App) CountNotification(notificationType model.NotificationType, platform string) { if a.notificationMetricsDisabled() { return } - a.Metrics().IncrementNotificationCounter(notificationType) + a.Metrics().IncrementNotificationCounter(notificationType, platform) } -func (a *App) CountNotificationAck(notificationType model.NotificationType) { +func (a *App) CountNotificationAck(notificationType model.NotificationType, platform string) { if a.notificationMetricsDisabled() { return } - a.Metrics().IncrementNotificationAckCounter(notificationType) + a.Metrics().IncrementNotificationAckCounter(notificationType, platform) } func (a *App) CountNotificationReason( notificationStatus model.NotificationStatus, notificationType model.NotificationType, notificationReason model.NotificationReason, + platform string, ) { if a.notificationMetricsDisabled() { return @@ -1762,13 +1763,13 @@ func (a *App) CountNotificationReason( switch notificationStatus { case model.NotificationStatusSuccess: - a.Metrics().IncrementNotificationSuccessCounter(notificationType) + a.Metrics().IncrementNotificationSuccessCounter(notificationType, platform) case model.NotificationStatusError: - a.Metrics().IncrementNotificationErrorCounter(notificationType, notificationReason) + a.Metrics().IncrementNotificationErrorCounter(notificationType, notificationReason, platform) case model.NotificationStatusNotSent: - a.Metrics().IncrementNotificationNotSentCounter(notificationType, notificationReason) + a.Metrics().IncrementNotificationNotSentCounter(notificationType, notificationReason, platform) case model.NotificationStatusUnsupported: - a.Metrics().IncrementNotificationUnsupportedCounter(notificationType, notificationReason) + a.Metrics().IncrementNotificationUnsupportedCounter(notificationType, notificationReason, platform) } } diff --git a/server/channels/app/notification_push.go b/server/channels/app/notification_push.go index 98ed6403db..ceae686b4a 100644 --- a/server/channels/app/notification_push.go +++ b/server/channels/app/notification_push.go @@ -117,7 +117,7 @@ func (a *App) sendPushNotificationToAllSessions(rctx request.CTX, msg *model.Pus sessions, appErr := a.getMobileAppSessions(userID) if appErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Failed to send mobile app sessions", mlog.String("type", model.NotificationTypePush), mlog.String("status", model.NotificationStatusError), @@ -129,7 +129,7 @@ func (a *App) sendPushNotificationToAllSessions(rctx request.CTX, msg *model.Pus } if msg == nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonParseError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonParseError, model.NotificationNoPlatform) a.NotificationsLog().Error("Failed to parse push notification", mlog.String("type", model.NotificationTypePush), mlog.String("status", model.NotificationStatusError), @@ -148,7 +148,7 @@ func (a *App) sendPushNotificationToAllSessions(rctx request.CTX, msg *model.Pus for _, session := range sessions { // Don't send notifications to this session if it's expired or we want to skip it if session.IsExpired() || (skipSessionId != "" && skipSessionId == session.Id) { - a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypePush, model.NotificationReasonSessionExpired) + a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypePush, model.NotificationReasonSessionExpired, model.NotificationNoPlatform) a.NotificationsLog().Debug("Session expired or skipped", mlog.String("type", model.NotificationTypePush), mlog.String("status", model.NotificationStatusNotSent), @@ -184,7 +184,7 @@ func (a *App) sendPushNotificationToAllSessions(rctx request.CTX, msg *model.Pus err = a.sendToPushProxy(tmpMessage, session) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxySendError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxySendError, tmpMessage.Platform) a.NotificationsLog().Error("Failed to send to push proxy", mlog.String("type", model.NotificationTypePush), mlog.String("status", model.NotificationStatusNotSent), @@ -212,7 +212,7 @@ func (a *App) sendPushNotificationToAllSessions(rctx request.CTX, msg *model.Pus } if msg.Type == model.PushTypeMessage { - a.CountNotification(model.NotificationTypePush) + a.CountNotification(model.NotificationTypePush, tmpMessage.Platform) } } @@ -586,7 +586,7 @@ func (a *App) getMobileAppSessions(userID string) ([]*model.Session, *model.AppE func (a *App) ShouldSendPushNotification(user *model.User, channelNotifyProps model.StringMap, wasMentioned bool, status *model.Status, post *model.Post, isGM bool) bool { if notifyPropsAllowedReason := DoesNotifyPropsAllowPushNotification(user, channelNotifyProps, post, wasMentioned, isGM); notifyPropsAllowedReason != "" { - a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypePush, notifyPropsAllowedReason) + a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypePush, notifyPropsAllowedReason, model.NotificationNoPlatform) a.NotificationsLog().Debug("Notification not sent - notify props", mlog.String("type", model.NotificationTypePush), mlog.String("post_id", post.Id), @@ -599,7 +599,7 @@ func (a *App) ShouldSendPushNotification(user *model.User, channelNotifyProps mo } if statusAllowedReason := DoesStatusAllowPushNotification(user.NotifyProps, status, post.ChannelId, false); statusAllowedReason != "" { - a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypePush, statusAllowedReason) + a.CountNotificationReason(model.NotificationStatusNotSent, model.NotificationTypePush, statusAllowedReason, model.NotificationNoPlatform) a.NotificationsLog().Debug("Notification not sent - status", mlog.String("type", model.NotificationTypePush), mlog.String("post_id", post.Id), @@ -726,7 +726,7 @@ func (a *App) SendTestPushNotification(deviceID string) string { pushResponse, err := a.rawSendToPushProxy(msg) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxySendError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxySendError, msg.Platform) a.NotificationsLog().Error("Failed to send test notification to push proxy", mlog.String("type", model.NotificationTypePush), mlog.String("push_type", msg.Type), @@ -742,7 +742,7 @@ func (a *App) SendTestPushNotification(deviceID string) string { case model.PushStatusRemove: return "false" case model.PushStatusFail: - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxyError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypePush, model.NotificationReasonPushProxyError, msg.Platform) a.NotificationsLog().Error("Push proxy failed to send test notification", mlog.String("type", model.NotificationTypePush), mlog.String("push_type", msg.Type), diff --git a/server/channels/app/opentracing/opentracing_layer.go b/server/channels/app/opentracing/opentracing_layer.go index 5281d0ee67..96232c6320 100644 --- a/server/channels/app/opentracing/opentracing_layer.go +++ b/server/channels/app/opentracing/opentracing_layer.go @@ -1967,7 +1967,7 @@ func (a *OpenTracingAppLayer) CopyWranglerPostlist(c request.CTX, wpl *model.Wra return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) CountNotification(notificationType model.NotificationType) { +func (a *OpenTracingAppLayer) CountNotification(notificationType model.NotificationType, platform string) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CountNotification") @@ -1979,10 +1979,10 @@ func (a *OpenTracingAppLayer) CountNotification(notificationType model.Notificat }() defer span.Finish() - a.app.CountNotification(notificationType) + a.app.CountNotification(notificationType, platform) } -func (a *OpenTracingAppLayer) CountNotificationAck(notificationType model.NotificationType) { +func (a *OpenTracingAppLayer) CountNotificationAck(notificationType model.NotificationType, platform string) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CountNotificationAck") @@ -1994,10 +1994,10 @@ func (a *OpenTracingAppLayer) CountNotificationAck(notificationType model.Notifi }() defer span.Finish() - a.app.CountNotificationAck(notificationType) + a.app.CountNotificationAck(notificationType, platform) } -func (a *OpenTracingAppLayer) CountNotificationReason(notificationStatus model.NotificationStatus, notificationType model.NotificationType, notificationReason model.NotificationReason) { +func (a *OpenTracingAppLayer) CountNotificationReason(notificationStatus model.NotificationStatus, notificationType model.NotificationType, notificationReason model.NotificationReason, platform string) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.CountNotificationReason") @@ -2009,7 +2009,7 @@ func (a *OpenTracingAppLayer) CountNotificationReason(notificationStatus model.N }() defer span.Finish() - a.app.CountNotificationReason(notificationStatus, notificationType, notificationReason) + a.app.CountNotificationReason(notificationStatus, notificationType, notificationReason, platform) } func (a *OpenTracingAppLayer) CreateBot(rctx request.CTX, bot *model.Bot) (*model.Bot, *model.AppError) { diff --git a/server/channels/app/post.go b/server/channels/app/post.go index 9539d23f05..a97d349388 100644 --- a/server/channels/app/post.go +++ b/server/channels/app/post.go @@ -357,7 +357,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel if rpost.RootId != "" { if appErr := a.ResolvePersistentNotification(c, parentPostList.Posts[post.RootId], rpost.UserId); appErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonResolvePersistentNotificationError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonResolvePersistentNotificationError, model.NotificationNoPlatform) a.NotificationsLog().Error("Error resolving persistent notification", mlog.String("sender_id", rpost.UserId), mlog.String("post_id", post.RootId), @@ -488,7 +488,7 @@ func (a *App) handlePostEvents(c request.CTX, post *model.Post, user *model.User if channel.TeamId != "" { t, err := a.Srv().Store().Team().Get(channel.TeamId) if err != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Missing team", mlog.String("post_id", post.Id), mlog.String("status", model.NotificationStatusError), @@ -783,7 +783,7 @@ func (a *App) publishWebsocketEventForPermalinkPost(c request.CTX, post *model.P } if !model.IsValidId(previewedPostID) { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonParseError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonParseError, model.NotificationNoPlatform) a.NotificationsLog().Error("Invalid post prop id for permalink post", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -798,7 +798,7 @@ func (a *App) publishWebsocketEventForPermalinkPost(c request.CTX, post *model.P previewedPost, err := a.GetSinglePost(c, previewedPostID, false) if err != nil { if err.StatusCode == http.StatusNotFound { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("permalink post not found", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -815,7 +815,7 @@ func (a *App) publishWebsocketEventForPermalinkPost(c request.CTX, post *model.P userIDs, nErr := a.Srv().Store().Channel().GetAllChannelMemberIdsByChannelId(post.ChannelId) if nErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Cannot get channel members", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), @@ -830,7 +830,7 @@ func (a *App) publishWebsocketEventForPermalinkPost(c request.CTX, post *model.P permalinkPreviewedChannel, err := a.GetChannel(c, previewedPost.ChannelId) if err != nil { if err.StatusCode == http.StatusNotFound { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonFetchError, model.NotificationNoPlatform) a.NotificationsLog().Error("Cannot get channel", mlog.String("type", model.NotificationTypeWebsocket), mlog.String("post_id", post.Id), diff --git a/server/channels/app/post_acknowledgements.go b/server/channels/app/post_acknowledgements.go index 12971f76b3..b39b456eb0 100644 --- a/server/channels/app/post_acknowledgements.go +++ b/server/channels/app/post_acknowledgements.go @@ -42,7 +42,7 @@ func (a *App) SaveAcknowledgementForPost(c request.CTX, postID, userID string) ( } if appErr := a.ResolvePersistentNotification(c, post, userID); appErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonResolvePersistentNotificationError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeWebsocket, model.NotificationReasonResolvePersistentNotificationError, model.NotificationNoPlatform) a.NotificationsLog().Error("Error resolving persistent notification", mlog.String("sender_id", userID), mlog.String("post_id", post.RootId), diff --git a/server/channels/app/reaction.go b/server/channels/app/reaction.go index a4c9dd51d4..56048158f3 100644 --- a/server/channels/app/reaction.go +++ b/server/channels/app/reaction.go @@ -67,7 +67,7 @@ func (a *App) SaveReactionForPost(c request.CTX, reaction *model.Reaction) (*mod if post.RootId == "" { if appErr := a.ResolvePersistentNotification(c, post, reaction.UserId); appErr != nil { - a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonResolvePersistentNotificationError) + a.CountNotificationReason(model.NotificationStatusError, model.NotificationTypeAll, model.NotificationReasonResolvePersistentNotificationError, model.NotificationNoPlatform) a.NotificationsLog().Error("Error resolving persistent notification", mlog.String("sender_id", reaction.UserId), mlog.String("post_id", post.RootId), diff --git a/server/channels/app/web_broadcast_hooks.go b/server/channels/app/web_broadcast_hooks.go index 51b926c8f8..772c2efd3b 100644 --- a/server/channels/app/web_broadcast_hooks.go +++ b/server/channels/app/web_broadcast_hooks.go @@ -139,7 +139,7 @@ func incrementWebsocketCounter(wc *platform.WebConn) { return } - wc.Platform.Metrics().IncrementNotificationCounter(model.NotificationTypeWebsocket) + wc.Platform.Metrics().IncrementNotificationCounter(model.NotificationTypeWebsocket, model.NotificationNoPlatform) } // getTypedArg returns a correctly typed hook argument with the given key, reinterpreting the type using JSON encoding diff --git a/server/channels/wsapi/system.go b/server/channels/wsapi/system.go index d221505434..83045a127f 100644 --- a/server/channels/wsapi/system.go +++ b/server/channels/wsapi/system.go @@ -36,7 +36,7 @@ func (api *API) websocketNotificationAck(req *model.WebSocketRequest) (map[strin ) // Count metrics for websocket acks - api.App.CountNotificationAck(model.NotificationTypeWebsocket) + api.App.CountNotificationAck(model.NotificationTypeWebsocket, model.NotificationNoPlatform) status := req.Data["status"] reason := req.Data["reason"] @@ -57,6 +57,7 @@ func (api *API) websocketNotificationAck(req *model.WebSocketRequest) (map[strin notificationStatus, model.NotificationTypeWebsocket, notificationReason, + model.NotificationNoPlatform, ) return nil, nil diff --git a/server/einterfaces/metrics.go b/server/einterfaces/metrics.go index ba2cdd859e..42b545c455 100644 --- a/server/einterfaces/metrics.go +++ b/server/einterfaces/metrics.go @@ -96,12 +96,12 @@ type MetricsInterface interface { SetReplicaLagAbsolute(node string, value float64) SetReplicaLagTime(node string, value float64) - IncrementNotificationCounter(notificationType model.NotificationType) - IncrementNotificationAckCounter(notificationType model.NotificationType) - IncrementNotificationSuccessCounter(notificationType model.NotificationType) - IncrementNotificationErrorCounter(notificationType model.NotificationType, errorReason model.NotificationReason) - IncrementNotificationNotSentCounter(notificationType model.NotificationType, notSentReason model.NotificationReason) - IncrementNotificationUnsupportedCounter(notificationType model.NotificationType, notSentReason model.NotificationReason) + IncrementNotificationCounter(notificationType model.NotificationType, platform string) + IncrementNotificationAckCounter(notificationType model.NotificationType, platform string) + IncrementNotificationSuccessCounter(notificationType model.NotificationType, platform string) + IncrementNotificationErrorCounter(notificationType model.NotificationType, errorReason model.NotificationReason, platform string) + IncrementNotificationNotSentCounter(notificationType model.NotificationType, notSentReason model.NotificationReason, platform string) + IncrementNotificationUnsupportedCounter(notificationType model.NotificationType, notSentReason model.NotificationReason, platform string) ObserveClientTimeToFirstByte(platform, agent string, elapsed float64) ObserveClientFirstContentfulPaint(platform, agent string, elapsed float64) diff --git a/server/einterfaces/mocks/MetricsInterface.go b/server/einterfaces/mocks/MetricsInterface.go index bcbac52bee..59fa8387da 100644 --- a/server/einterfaces/mocks/MetricsInterface.go +++ b/server/einterfaces/mocks/MetricsInterface.go @@ -168,34 +168,34 @@ func (_m *MetricsInterface) IncrementMemCacheMissCounterSession() { _m.Called() } -// IncrementNotificationAckCounter provides a mock function with given fields: notificationType -func (_m *MetricsInterface) IncrementNotificationAckCounter(notificationType model.NotificationType) { - _m.Called(notificationType) +// IncrementNotificationAckCounter provides a mock function with given fields: notificationType, platform +func (_m *MetricsInterface) IncrementNotificationAckCounter(notificationType model.NotificationType, platform string) { + _m.Called(notificationType, platform) } -// IncrementNotificationCounter provides a mock function with given fields: notificationType -func (_m *MetricsInterface) IncrementNotificationCounter(notificationType model.NotificationType) { - _m.Called(notificationType) +// IncrementNotificationCounter provides a mock function with given fields: notificationType, platform +func (_m *MetricsInterface) IncrementNotificationCounter(notificationType model.NotificationType, platform string) { + _m.Called(notificationType, platform) } -// IncrementNotificationErrorCounter provides a mock function with given fields: notificationType, errorReason -func (_m *MetricsInterface) IncrementNotificationErrorCounter(notificationType model.NotificationType, errorReason model.NotificationReason) { - _m.Called(notificationType, errorReason) +// IncrementNotificationErrorCounter provides a mock function with given fields: notificationType, errorReason, platform +func (_m *MetricsInterface) IncrementNotificationErrorCounter(notificationType model.NotificationType, errorReason model.NotificationReason, platform string) { + _m.Called(notificationType, errorReason, platform) } -// IncrementNotificationNotSentCounter provides a mock function with given fields: notificationType, notSentReason -func (_m *MetricsInterface) IncrementNotificationNotSentCounter(notificationType model.NotificationType, notSentReason model.NotificationReason) { - _m.Called(notificationType, notSentReason) +// IncrementNotificationNotSentCounter provides a mock function with given fields: notificationType, notSentReason, platform +func (_m *MetricsInterface) IncrementNotificationNotSentCounter(notificationType model.NotificationType, notSentReason model.NotificationReason, platform string) { + _m.Called(notificationType, notSentReason, platform) } -// IncrementNotificationSuccessCounter provides a mock function with given fields: notificationType -func (_m *MetricsInterface) IncrementNotificationSuccessCounter(notificationType model.NotificationType) { - _m.Called(notificationType) +// IncrementNotificationSuccessCounter provides a mock function with given fields: notificationType, platform +func (_m *MetricsInterface) IncrementNotificationSuccessCounter(notificationType model.NotificationType, platform string) { + _m.Called(notificationType, platform) } -// IncrementNotificationUnsupportedCounter provides a mock function with given fields: notificationType, notSentReason -func (_m *MetricsInterface) IncrementNotificationUnsupportedCounter(notificationType model.NotificationType, notSentReason model.NotificationReason) { - _m.Called(notificationType, notSentReason) +// IncrementNotificationUnsupportedCounter provides a mock function with given fields: notificationType, notSentReason, platform +func (_m *MetricsInterface) IncrementNotificationUnsupportedCounter(notificationType model.NotificationType, notSentReason model.NotificationReason, platform string) { + _m.Called(notificationType, notSentReason, platform) } // IncrementPostBroadcast provides a mock function with given fields: diff --git a/server/enterprise/metrics/metrics.go b/server/enterprise/metrics/metrics.go index 546f76c9bb..7fab048b9c 100644 --- a/server/enterprise/metrics/metrics.go +++ b/server/enterprise/metrics/metrics.go @@ -1076,7 +1076,7 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf Help: "Total number of notification events", ConstLabels: additionalLabels, }, - []string{"type"}, + []string{"type", "platform"}, ) m.Registry.MustRegister(m.NotificationTotalCounters) @@ -1088,7 +1088,7 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf Help: "Total number of notification events acknowledged", ConstLabels: additionalLabels, }, - []string{"type"}, + []string{"type", "platform"}, ) m.Registry.MustRegister(m.NotificationAckCounters) @@ -1100,7 +1100,7 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf Help: "Total number of successfully sent notifications", ConstLabels: additionalLabels, }, - []string{"type"}, + []string{"type", "platform"}, ) m.Registry.MustRegister(m.NotificationSuccessCounters) @@ -1112,7 +1112,7 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf Help: "Total number of errors that stop the notification flow", ConstLabels: additionalLabels, }, - []string{"type", "reason"}, + []string{"type", "reason", "platform"}, ) m.Registry.MustRegister(m.NotificationErrorCounters) @@ -1124,7 +1124,7 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf Help: "Total number of notifications the system deliberately did not send", ConstLabels: additionalLabels, }, - []string{"type", "reason"}, + []string{"type", "reason", "platform"}, ) m.Registry.MustRegister(m.NotificationNotSentCounters) @@ -1136,7 +1136,7 @@ func New(ps *platform.PlatformService, driver, dataSource string) *MetricsInterf Help: "Total number of untrackable notifications due to an unsupported app version", ConstLabels: additionalLabels, }, - []string{"type", "reason"}, + []string{"type", "reason", "platform"}, ) m.Registry.MustRegister(m.NotificationUnsupportedCounters) @@ -1730,28 +1730,41 @@ func (mi *MetricsInterfaceImpl) SetReplicaLagTime(node string, value float64) { mi.DbReplicaLagGaugeTime.With(prometheus.Labels{"node": node}).Set(value) } -func (mi *MetricsInterfaceImpl) IncrementNotificationCounter(notificationType model.NotificationType) { - mi.NotificationTotalCounters.With(prometheus.Labels{"type": string(notificationType)}).Inc() +func normalizeNotificationPlatform(platform string) string { + switch platform { + case "apple_rn-v2", "apple_rnbeta-v2", "ios": + return "ios" + case "android_rn-v2", "android": + return "android" + case model.NotificationNoPlatform: + return model.NotificationNoPlatform + default: + return "unknown" + } } -func (mi *MetricsInterfaceImpl) IncrementNotificationAckCounter(notificationType model.NotificationType) { - mi.NotificationAckCounters.With(prometheus.Labels{"type": string(notificationType)}).Inc() +func (mi *MetricsInterfaceImpl) IncrementNotificationCounter(notificationType model.NotificationType, platform string) { + mi.NotificationTotalCounters.With(prometheus.Labels{"type": string(notificationType), "platform": normalizeNotificationPlatform(platform)}).Inc() } -func (mi *MetricsInterfaceImpl) IncrementNotificationSuccessCounter(notificationType model.NotificationType) { - mi.NotificationSuccessCounters.With(prometheus.Labels{"type": string(notificationType)}).Inc() +func (mi *MetricsInterfaceImpl) IncrementNotificationAckCounter(notificationType model.NotificationType, platform string) { + mi.NotificationAckCounters.With(prometheus.Labels{"type": string(notificationType), "platform": normalizeNotificationPlatform(platform)}).Inc() } -func (mi *MetricsInterfaceImpl) IncrementNotificationErrorCounter(notificationType model.NotificationType, errorReason model.NotificationReason) { - mi.NotificationErrorCounters.With(prometheus.Labels{"type": string(notificationType), "reason": string(errorReason)}).Inc() +func (mi *MetricsInterfaceImpl) IncrementNotificationSuccessCounter(notificationType model.NotificationType, platform string) { + mi.NotificationSuccessCounters.With(prometheus.Labels{"type": string(notificationType), "platform": normalizeNotificationPlatform(platform)}).Inc() } -func (mi *MetricsInterfaceImpl) IncrementNotificationNotSentCounter(notificationType model.NotificationType, notSentReason model.NotificationReason) { - mi.NotificationNotSentCounters.With(prometheus.Labels{"type": string(notificationType), "reason": string(notSentReason)}).Inc() +func (mi *MetricsInterfaceImpl) IncrementNotificationErrorCounter(notificationType model.NotificationType, errorReason model.NotificationReason, platform string) { + mi.NotificationErrorCounters.With(prometheus.Labels{"type": string(notificationType), "reason": string(errorReason), "platform": normalizeNotificationPlatform(platform)}).Inc() } -func (mi *MetricsInterfaceImpl) IncrementNotificationUnsupportedCounter(notificationType model.NotificationType, notSentReason model.NotificationReason) { - mi.NotificationUnsupportedCounters.With(prometheus.Labels{"type": string(notificationType), "reason": string(notSentReason)}).Inc() +func (mi *MetricsInterfaceImpl) IncrementNotificationNotSentCounter(notificationType model.NotificationType, notSentReason model.NotificationReason, platform string) { + mi.NotificationNotSentCounters.With(prometheus.Labels{"type": string(notificationType), "reason": string(notSentReason), "platform": normalizeNotificationPlatform(platform)}).Inc() +} + +func (mi *MetricsInterfaceImpl) IncrementNotificationUnsupportedCounter(notificationType model.NotificationType, notSentReason model.NotificationReason, platform string) { + mi.NotificationUnsupportedCounters.With(prometheus.Labels{"type": string(notificationType), "reason": string(notSentReason), "platform": normalizeNotificationPlatform(platform)}).Inc() } func (mi *MetricsInterfaceImpl) IncrementHTTPWebSockets(originClient string) { diff --git a/server/public/model/notification.go b/server/public/model/notification.go index c959aa5fda..3029cfc3e0 100644 --- a/server/public/model/notification.go +++ b/server/public/model/notification.go @@ -18,6 +18,8 @@ const ( NotificationTypeWebsocket NotificationType = "websocket" NotificationTypePush NotificationType = "push" + NotificationNoPlatform = "no_platform" + NotificationReasonFetchError NotificationReason = "fetch_error" NotificationReasonParseError NotificationReason = "json_parse_error" NotificationReasonPushProxyError NotificationReason = "push_proxy_error"