Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MM-56924: Connection invite metrics #675

Merged
merged 8 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,18 @@ func (a *API) siteStats(w http.ResponseWriter, r *http.Request) {
http.Error(w, "unable to get connected users count", http.StatusInternalServerError)
return
}
pendingInvites, err := a.p.store.GetInvitedCount()
if err != nil {
a.p.API.LogWarn("Failed to get invited users count", "error", err.Error())
http.Error(w, "unable to get invited users count", http.StatusInternalServerError)
return
}
whitelistedUsers, err := a.p.store.GetWhitelistCount()
if err != nil {
a.p.API.LogWarn("Failed to get whitelisted users count", "error", err.Error())
http.Error(w, "unable to get whitelisted users count", http.StatusInternalServerError)
return
}
receiving, err := a.p.store.GetActiveUsersReceivingCount(metricsActiveUsersRange)
if err != nil {
a.p.API.LogWarn("Failed to get users receiving count", "error", err.Error())
Expand All @@ -1095,10 +1107,14 @@ func (a *API) siteStats(w http.ResponseWriter, r *http.Request) {

siteStats := struct {
TotalConnectedUsers int64 `json:"total_connected_users"`
PendingInvitedUsers int64 `json:"pending_invited_users"`
CurrentWhitelistUsers int64 `json:"current_whitelist_users"`
UserReceivingMessages int64 `json:"total_users_receiving"`
UserSendingMessages int64 `json:"total_users_sending"`
}{
TotalConnectedUsers: connectedUsersCount,
PendingInvitedUsers: int64(pendingInvites),
CurrentWhitelistUsers: int64(whitelistedUsers),
UserReceivingMessages: receiving,
UserSendingMessages: sending,
}
Expand Down
23 changes: 20 additions & 3 deletions server/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ func TestGetSiteStats(t *testing.T) {

response, bodyString := sendRequest(t, sysadmin)
assert.Equal(t, http.StatusOK, response.StatusCode)
assert.JSONEq(t, `{"total_connected_users":0,"total_users_receiving":0, "total_users_sending":0}`, bodyString)
assert.JSONEq(t, `{"current_whitelist_users":0, "pending_invited_users":0, "total_connected_users":0, "total_users_receiving":0, "total_users_sending":0}`, bodyString)
})

t.Run("1 connected user", func(t *testing.T) {
Expand All @@ -1021,7 +1021,24 @@ func TestGetSiteStats(t *testing.T) {

response, bodyString := sendRequest(t, sysadmin)
assert.Equal(t, http.StatusOK, response.StatusCode)
assert.JSONEq(t, `{"total_connected_users":1,"total_users_receiving":1, "total_users_sending":1}`, bodyString)
assert.JSONEq(t, `{"current_whitelist_users":0, "pending_invited_users":0, "total_connected_users":1,"total_users_receiving":1, "total_users_sending":1}`, bodyString)
})

t.Run("1 invited user, 2 whitelisted users", func(t *testing.T) {
th.Reset(t)
sysadmin := th.SetupSysadmin(t, team)

user1 := th.SetupUser(t, team)
user2 := th.SetupUser(t, team)
user3 := th.SetupUser(t, team)

th.MarkUserWhitelisted(t, user1.Id)
th.MarkUserWhitelisted(t, user2.Id)
th.MarkUserInvited(t, user3.Id)

response, bodyString := sendRequest(t, sysadmin)
assert.Equal(t, http.StatusOK, response.StatusCode)
assert.JSONEq(t, `{"current_whitelist_users":2, "pending_invited_users":1, "total_connected_users":0,"total_users_receiving":0, "total_users_sending":0}`, bodyString)
})

t.Run("10 connected users", func(t *testing.T) {
Expand Down Expand Up @@ -1050,7 +1067,7 @@ func TestGetSiteStats(t *testing.T) {

response, bodyString := sendRequest(t, sysadmin)
assert.Equal(t, http.StatusOK, response.StatusCode)
assert.JSONEq(t, `{"total_connected_users":10,"total_users_receiving":5, "total_users_sending":2}`, bodyString)
assert.JSONEq(t, `{"current_whitelist_users":0, "pending_invited_users":0, "total_connected_users":10,"total_users_receiving":5, "total_users_sending":2}`, bodyString)
})
}

Expand Down
110 changes: 84 additions & 26 deletions server/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ type Metrics interface {
IncrementHTTPErrors()
ObserveOAuthTokenInvalidated()
ObserveChangeEventQueueRejected()
ObserveWhitelistLimit(limit int)

ObserveChangeEvent(changeType string, discardedReason string)
ObserveLifecycleEvent(lifecycleEventType, discardedReason string)
Expand All @@ -86,6 +85,11 @@ type Metrics interface {
ObserveSubscription(action string)

ObserveConnectedUsers(count int64)
ObserveConnectedUsersLimit(count int64)
ObservePendingInvites(count int64)
ObservePendingInvitesLimit(count int64)
ObserveWhitelistedUsers(count int64)

ObserveSyntheticUsers(count int64)
ObserveLinkedChannels(count int64)
ObserveUpstreamUsers(count int64)
Expand Down Expand Up @@ -115,9 +119,9 @@ type Metrics interface {
}

type InstanceInfo struct {
InstallationID string
WhiteListLimit int
PluginVersion string
InstallationID string
ConnectedUsersLimit int
PluginVersion string
}

// metrics used to instrumentate metrics in prometheus.
Expand All @@ -127,7 +131,6 @@ type metrics struct {
pluginStartTime prometheus.Gauge
pluginInfo prometheus.Gauge
goroutineFailuresTotal prometheus.Counter
whitelistLimit prometheus.Gauge

apiTime *prometheus.HistogramVec

Expand All @@ -148,10 +151,16 @@ type metrics struct {
syncMsgReactionDelayTime *prometheus.HistogramVec
syncMsgFileDelayTime *prometheus.HistogramVec

connectedUsers prometheus.Gauge
syntheticUsers prometheus.Gauge
linkedChannels prometheus.Gauge
upstreamUsers prometheus.Gauge
connectedUsers prometheus.Gauge
connectedUsersLimit prometheus.Gauge
pendingInvites prometheus.Gauge
pendingInvitesLimit prometheus.Gauge
whitelistedUsers prometheus.Gauge

syntheticUsers prometheus.Gauge
linkedChannels prometheus.Gauge
upstreamUsers prometheus.Gauge

activeUsersSending prometheus.Gauge
activeUsersReceiving prometheus.Gauge
mattermostPrimary prometheus.Gauge
Expand Down Expand Up @@ -193,16 +202,6 @@ func NewMetrics(info InstanceInfo) Metrics {
m.pluginStartTime.SetToCurrentTime()
m.registry.MustRegister(m.pluginStartTime)

m.whitelistLimit = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: MetricsSubsystemApp,
Name: "whitelist_limit",
Help: "The maximum number of users allowed to connect.",
ConstLabels: additionalLabels,
})
m.whitelistLimit.Set(float64(info.WhiteListLimit))
m.registry.MustRegister(m.whitelistLimit)

m.pluginInfo = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: MetricsSubsystemSystem,
Expand Down Expand Up @@ -358,11 +357,48 @@ func NewMetrics(info InstanceInfo) Metrics {
Namespace: MetricsNamespace,
Subsystem: MetricsSubsystemApp,
Name: "connected_users",
Help: "The total number of Mattermost users connected to MS Teams users.",
Help: "The total number of users connected to MS Teams users.",
ConstLabels: additionalLabels,
})
m.registry.MustRegister(m.connectedUsers)

m.connectedUsersLimit = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: MetricsSubsystemApp,
Name: "connected_users_limit",
Help: "The maximum number of users allowed to connect.",
ConstLabels: additionalLabels,
})
m.connectedUsersLimit.Set(float64(info.ConnectedUsersLimit))
m.registry.MustRegister(m.connectedUsersLimit)

m.pendingInvites = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: MetricsSubsystemApp,
Name: "pending_invites",
Help: "The total number of users with pending connection invites.",
ConstLabels: additionalLabels,
})
m.registry.MustRegister(m.pendingInvites)

m.pendingInvitesLimit = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: MetricsSubsystemApp,
Name: "pending_invites_limit",
Help: "The maximum number of pending connection invites.",
ConstLabels: additionalLabels,
})
m.registry.MustRegister(m.pendingInvitesLimit)

m.whitelistedUsers = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: MetricsSubsystemApp,
Name: "whitelisted_users",
Help: "The total number of users whitelisted for connection invites or new connections.",
ConstLabels: additionalLabels,
})
m.registry.MustRegister(m.whitelistedUsers)

m.syntheticUsers = prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: MetricsNamespace,
Subsystem: MetricsSubsystemApp,
Expand Down Expand Up @@ -514,24 +550,46 @@ func (m *metrics) ObserveGoroutineFailure() {
}
}

func (m *metrics) ObserveWhitelistLimit(limit int) {
if m != nil {
m.whitelistLimit.Set(float64(limit))
}
}

func (m *metrics) ObserveAPIEndpointDuration(handler, method, statusCode string, elapsed float64) {
if m != nil {
m.apiTime.With(prometheus.Labels{"handler": handler, "method": method, "status_code": statusCode}).Observe(elapsed)
}
}

// START CONNECT FLOW METRICS

func (m *metrics) ObserveConnectedUsers(count int64) {
if m != nil {
m.connectedUsers.Set(float64(count))
}
}

func (m *metrics) ObserveConnectedUsersLimit(limit int64) {
if m != nil {
m.connectedUsersLimit.Set(float64(limit))
}
}

func (m *metrics) ObservePendingInvites(count int64) {
if m != nil {
m.pendingInvites.Set(float64(count))
}
}

func (m *metrics) ObservePendingInvitesLimit(count int64) {
if m != nil {
m.pendingInvitesLimit.Set(float64(count))
}
}

func (m *metrics) ObserveWhitelistedUsers(count int64) {
if m != nil {
m.whitelistedUsers.Set(float64(count))
}
}

// END CONNECT FLOW METRICS

func (m *metrics) ObserveChangeEvent(changeType string, discardedReason string) {
if m != nil {
m.changeEventsTotal.With(prometheus.Labels{"change_type": changeType, "discarded_reason": discardedReason}).Inc()
Expand Down
21 changes: 18 additions & 3 deletions server/metrics/mocks/Metrics.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 18 additions & 1 deletion server/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,8 @@ func (p *Plugin) start(isRestart bool) {
}
}

p.metricsService.ObserveWhitelistLimit(p.configuration.ConnectedUsersAllowed)
p.metricsService.ObserveConnectedUsersLimit(int64(p.configuration.ConnectedUsersAllowed))
p.metricsService.ObservePendingInvitesLimit(int64(p.configuration.ConnectedUsersMaxPendingInvites))

// We don't restart the activity handler since it's stateless.
if !isRestart {
Expand Down Expand Up @@ -942,6 +943,22 @@ func (p *Plugin) updateMetrics() {
getData: p.store.GetConnectedUsersCount,
observeData: p.GetMetrics().ObserveConnectedUsers,
},
{
name: "invited users",
getData: func() (int64, error) {
val, err := p.store.GetInvitedCount()
return int64(val), err
},
observeData: p.GetMetrics().ObservePendingInvites,
},
{
name: "whitelisted users",
getData: func() (int64, error) {
val, err := p.store.GetWhitelistCount()
return int64(val), err
},
observeData: p.GetMetrics().ObserveWhitelistedUsers,
},
{
name: "linked channels",
getData: p.store.GetLinkedChannelsCount,
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {id as pluginId} from './manifest';

export interface SiteStats {
total_connected_users: number;
pending_invited_users: number;
current_whitelist_users: number;
total_users_receiving: number;
total_users_sending: number;
}
Expand Down
Loading