add daily active counts to stats (#38842)

* add daily active counts to stats

* standardize on int64, update tests
This commit is contained in:
Dan Cech 2021-09-13 10:29:35 -04:00 committed by GitHub
parent baff8fe39d
commit 9dfd469afc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 36 deletions

View File

@ -52,6 +52,9 @@ func (uss *UsageStatsService) GetUsageReport(ctx context.Context) (UsageReport,
metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards
metrics["stats.users.count"] = statsQuery.Result.Users metrics["stats.users.count"] = statsQuery.Result.Users
metrics["stats.admins.count"] = statsQuery.Result.Admins
metrics["stats.editors.count"] = statsQuery.Result.Editors
metrics["stats.viewers.count"] = statsQuery.Result.Viewers
metrics["stats.orgs.count"] = statsQuery.Result.Orgs metrics["stats.orgs.count"] = statsQuery.Result.Orgs
metrics["stats.playlist.count"] = statsQuery.Result.Playlists metrics["stats.playlist.count"] = statsQuery.Result.Playlists
metrics["stats.plugins.apps.count"] = uss.PluginManager.AppCount() metrics["stats.plugins.apps.count"] = uss.PluginManager.AppCount()
@ -59,6 +62,15 @@ func (uss *UsageStatsService) GetUsageReport(ctx context.Context) (UsageReport,
metrics["stats.plugins.datasources.count"] = uss.PluginManager.DataSourceCount() metrics["stats.plugins.datasources.count"] = uss.PluginManager.DataSourceCount()
metrics["stats.alerts.count"] = statsQuery.Result.Alerts metrics["stats.alerts.count"] = statsQuery.Result.Alerts
metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers
metrics["stats.active_admins.count"] = statsQuery.Result.ActiveAdmins
metrics["stats.active_editors.count"] = statsQuery.Result.ActiveEditors
metrics["stats.active_viewers.count"] = statsQuery.Result.ActiveViewers
metrics["stats.active_sessions.count"] = statsQuery.Result.ActiveSessions
metrics["stats.daily_active_users.count"] = statsQuery.Result.DailyActiveUsers
metrics["stats.daily_active_admins.count"] = statsQuery.Result.DailyActiveAdmins
metrics["stats.daily_active_editors.count"] = statsQuery.Result.DailyActiveEditors
metrics["stats.daily_active_viewers.count"] = statsQuery.Result.DailyActiveViewers
metrics["stats.daily_active_sessions.count"] = statsQuery.Result.DailyActiveSessions
metrics["stats.datasources.count"] = statsQuery.Result.Datasources metrics["stats.datasources.count"] = statsQuery.Result.Datasources
metrics["stats.stars.count"] = statsQuery.Result.Stars metrics["stats.stars.count"] = statsQuery.Result.Stars
metrics["stats.folders.count"] = statsQuery.Result.Folders metrics["stats.folders.count"] = statsQuery.Result.Folders

View File

@ -47,7 +47,19 @@ func TestMetrics(t *testing.T) {
Dashboards: 1, Dashboards: 1,
Datasources: 2, Datasources: 2,
Users: 3, Users: 3,
Admins: 31,
Editors: 32,
Viewers: 33,
ActiveUsers: 4, ActiveUsers: 4,
ActiveAdmins: 21,
ActiveEditors: 22,
ActiveViewers: 23,
ActiveSessions: 24,
DailyActiveUsers: 25,
DailyActiveAdmins: 26,
DailyActiveEditors: 27,
DailyActiveViewers: 28,
DailyActiveSessions: 29,
Orgs: 5, Orgs: 5,
Playlists: 6, Playlists: 6,
Alerts: 7, Alerts: 7,
@ -298,6 +310,9 @@ func TestMetrics(t *testing.T) {
metrics := j.Get("metrics") metrics := j.Get("metrics")
assert.Equal(t, getSystemStatsQuery.Result.Dashboards, metrics.Get("stats.dashboards.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Dashboards, metrics.Get("stats.dashboards.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Users, metrics.Get("stats.users.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Users, metrics.Get("stats.users.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Admins, metrics.Get("stats.admins.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Editors, metrics.Get("stats.editors.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Viewers, metrics.Get("stats.viewers.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Orgs, metrics.Get("stats.orgs.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Orgs, metrics.Get("stats.orgs.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Playlists, metrics.Get("stats.playlist.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Playlists, metrics.Get("stats.playlist.count").MustInt64())
assert.Equal(t, uss.PluginManager.AppCount(), metrics.Get("stats.plugins.apps.count").MustInt()) assert.Equal(t, uss.PluginManager.AppCount(), metrics.Get("stats.plugins.apps.count").MustInt())
@ -305,6 +320,15 @@ func TestMetrics(t *testing.T) {
assert.Equal(t, uss.PluginManager.DataSourceCount(), metrics.Get("stats.plugins.datasources.count").MustInt()) assert.Equal(t, uss.PluginManager.DataSourceCount(), metrics.Get("stats.plugins.datasources.count").MustInt())
assert.Equal(t, getSystemStatsQuery.Result.Alerts, metrics.Get("stats.alerts.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Alerts, metrics.Get("stats.alerts.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.ActiveUsers, metrics.Get("stats.active_users.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.ActiveUsers, metrics.Get("stats.active_users.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.ActiveAdmins, metrics.Get("stats.active_admins.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.ActiveEditors, metrics.Get("stats.active_editors.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.ActiveViewers, metrics.Get("stats.active_viewers.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.ActiveSessions, metrics.Get("stats.active_sessions.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.DailyActiveUsers, metrics.Get("stats.daily_active_users.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.DailyActiveAdmins, metrics.Get("stats.daily_active_admins.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.DailyActiveEditors, metrics.Get("stats.daily_active_editors.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.DailyActiveViewers, metrics.Get("stats.daily_active_viewers.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.DailyActiveSessions, metrics.Get("stats.daily_active_sessions.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Datasources, metrics.Get("stats.datasources.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Datasources, metrics.Get("stats.datasources.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Stars, metrics.Get("stats.stars.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Stars, metrics.Get("stats.stars.count").MustInt64())
assert.Equal(t, getSystemStatsQuery.Result.Folders, metrics.Get("stats.folders.count").MustInt64()) assert.Equal(t, getSystemStatsQuery.Result.Folders, metrics.Get("stats.folders.count").MustInt64())

View File

@ -5,6 +5,7 @@ type SystemStats struct {
Datasources int64 Datasources int64
Users int64 Users int64
ActiveUsers int64 ActiveUsers int64
DailyActiveUsers int64
Orgs int64 Orgs int64
Playlists int64 Playlists int64
Alerts int64 Alerts int64
@ -25,14 +26,17 @@ type SystemStats struct {
DashboardsViewersCanAdmin int64 DashboardsViewersCanAdmin int64
FoldersViewersCanEdit int64 FoldersViewersCanEdit int64
FoldersViewersCanAdmin int64 FoldersViewersCanAdmin int64
Admins int64
Admins int Editors int64
Editors int Viewers int64
Viewers int ActiveAdmins int64
ActiveAdmins int ActiveEditors int64
ActiveEditors int ActiveViewers int64
ActiveViewers int ActiveSessions int64
ActiveSessions int DailyActiveAdmins int64
DailyActiveEditors int64
DailyActiveViewers int64
DailyActiveSessions int64
} }
type DataSourceStats struct { type DataSourceStats struct {
@ -68,23 +72,28 @@ type GetAlertNotifierUsageStatsQuery struct {
} }
type AdminStats struct { type AdminStats struct {
Orgs int `json:"orgs"` Orgs int64 `json:"orgs"`
Dashboards int `json:"dashboards"` Dashboards int64 `json:"dashboards"`
Snapshots int `json:"snapshots"` Snapshots int64 `json:"snapshots"`
Tags int `json:"tags"` Tags int64 `json:"tags"`
Datasources int `json:"datasources"` Datasources int64 `json:"datasources"`
Playlists int `json:"playlists"` Playlists int64 `json:"playlists"`
Stars int `json:"stars"` Stars int64 `json:"stars"`
Alerts int `json:"alerts"` Alerts int64 `json:"alerts"`
Users int `json:"users"` Users int64 `json:"users"`
Admins int `json:"admins"` Admins int64 `json:"admins"`
Editors int `json:"editors"` Editors int64 `json:"editors"`
Viewers int `json:"viewers"` Viewers int64 `json:"viewers"`
ActiveUsers int `json:"activeUsers"` ActiveUsers int64 `json:"activeUsers"`
ActiveAdmins int `json:"activeAdmins"` ActiveAdmins int64 `json:"activeAdmins"`
ActiveEditors int `json:"activeEditors"` ActiveEditors int64 `json:"activeEditors"`
ActiveViewers int `json:"activeViewers"` ActiveViewers int64 `json:"activeViewers"`
ActiveSessions int `json:"activeSessions"` ActiveSessions int64 `json:"activeSessions"`
DailyActiveUsers int64 `json:"dailyActiveUsers"`
DailyActiveAdmins int64 `json:"dailyActiveAdmins"`
DailyActiveEditors int64 `json:"dailyActiveEditors"`
DailyActiveViewers int64 `json:"dailyActiveViewers"`
DailyActiveSessions int64 `json:"dailyActiveSessions"`
} }
type GetAdminStatsQuery struct { type GetAdminStatsQuery struct {

View File

@ -19,6 +19,7 @@ func init() {
} }
const activeUserTimeLimit = time.Hour * 24 * 30 const activeUserTimeLimit = time.Hour * 24 * 30
const dailyActiveUserTimeLimit = time.Hour * 24
func GetAlertNotifiersUsageStats(ctx context.Context, query *models.GetAlertNotifierUsageStatsQuery) error { func GetAlertNotifiersUsageStats(ctx context.Context, query *models.GetAlertNotifierUsageStatsQuery) error {
var rawSQL = `SELECT COUNT(*) AS count, type FROM ` + dialect.Quote("alert_notification") + ` GROUP BY type` var rawSQL = `SELECT COUNT(*) AS count, type FROM ` + dialect.Quote("alert_notification") + ` GROUP BY type`
@ -54,6 +55,9 @@ func GetSystemStats(query *models.GetSystemStatsQuery) error {
activeUserDeadlineDate := time.Now().Add(-activeUserTimeLimit) activeUserDeadlineDate := time.Now().Add(-activeUserTimeLimit)
sb.Write(`(SELECT COUNT(*) FROM `+dialect.Quote("user")+` WHERE last_seen_at > ?) AS active_users,`, activeUserDeadlineDate) sb.Write(`(SELECT COUNT(*) FROM `+dialect.Quote("user")+` WHERE last_seen_at > ?) AS active_users,`, activeUserDeadlineDate)
dailyActiveUserDeadlineDate := time.Now().Add(-dailyActiveUserTimeLimit)
sb.Write(`(SELECT COUNT(*) FROM `+dialect.Quote("user")+` WHERE last_seen_at > ?) AS daily_active_users,`, dailyActiveUserDeadlineDate)
sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("dashboard")+` WHERE is_folder = ?) AS dashboards,`, dialect.BooleanStr(false)) sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("dashboard")+` WHERE is_folder = ?) AS dashboards,`, dialect.BooleanStr(false))
sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("dashboard")+` WHERE is_folder = ?) AS folders,`, dialect.BooleanStr(true)) sb.Write(`(SELECT COUNT(id) FROM `+dialect.Quote("dashboard")+` WHERE is_folder = ?) AS folders,`, dialect.BooleanStr(true))
@ -112,7 +116,10 @@ func roleCounterSQL() string {
strconv.FormatInt(userStatsCache.total.Viewers, 10) + ` AS viewers, ` + strconv.FormatInt(userStatsCache.total.Viewers, 10) + ` AS viewers, ` +
strconv.FormatInt(userStatsCache.active.Admins, 10) + ` AS active_admins, ` + strconv.FormatInt(userStatsCache.active.Admins, 10) + ` AS active_admins, ` +
strconv.FormatInt(userStatsCache.active.Editors, 10) + ` AS active_editors, ` + strconv.FormatInt(userStatsCache.active.Editors, 10) + ` AS active_editors, ` +
strconv.FormatInt(userStatsCache.active.Viewers, 10) + ` AS active_viewers` strconv.FormatInt(userStatsCache.active.Viewers, 10) + ` AS active_viewers, ` +
strconv.FormatInt(userStatsCache.dailyActive.Admins, 10) + ` AS daily_active_admins, ` +
strconv.FormatInt(userStatsCache.dailyActive.Editors, 10) + ` AS daily_active_editors, ` +
strconv.FormatInt(userStatsCache.dailyActive.Viewers, 10) + ` AS daily_active_viewers`
return sqlQuery return sqlQuery
} }
@ -131,6 +138,7 @@ func viewersPermissionsCounterSQL(statName string, isFolder bool, permission mod
func GetAdminStats(query *models.GetAdminStatsQuery) error { func GetAdminStats(query *models.GetAdminStatsQuery) error {
activeEndDate := time.Now().Add(-activeUserTimeLimit) activeEndDate := time.Now().Add(-activeUserTimeLimit)
dailyActiveEndDate := time.Now().Add(-dailyActiveUserTimeLimit)
var rawSQL = `SELECT var rawSQL = `SELECT
( (
@ -173,14 +181,22 @@ func GetAdminStats(query *models.GetAdminStatsQuery) error {
SELECT COUNT(*) SELECT COUNT(*)
FROM ` + dialect.Quote("user") + ` WHERE last_seen_at > ? FROM ` + dialect.Quote("user") + ` WHERE last_seen_at > ?
) AS active_users, ) AS active_users,
(
SELECT COUNT(*)
FROM ` + dialect.Quote("user") + ` WHERE last_seen_at > ?
) AS daily_active_users,
` + roleCounterSQL() + `, ` + roleCounterSQL() + `,
( (
SELECT COUNT(*) SELECT COUNT(*)
FROM ` + dialect.Quote("user_auth_token") + ` WHERE rotated_at > ? FROM ` + dialect.Quote("user_auth_token") + ` WHERE rotated_at > ?
) AS active_sessions` ) AS active_sessions,
(
SELECT COUNT(*)
FROM ` + dialect.Quote("user_auth_token") + ` WHERE rotated_at > ?
) AS daily_active_sessions`
var stats models.AdminStats var stats models.AdminStats
_, err := x.SQL(rawSQL, activeEndDate, activeEndDate.Unix()).Get(&stats) _, err := x.SQL(rawSQL, activeEndDate, dailyActiveEndDate, activeEndDate.Unix(), dailyActiveEndDate.Unix()).Get(&stats)
if err != nil { if err != nil {
return err return err
} }
@ -217,6 +233,7 @@ func updateUserRoleCountsIfNecessary(ctx context.Context, forced bool) error {
type memoUserStats struct { type memoUserStats struct {
active models.UserStats active models.UserStats
dailyActive models.UserStats
total models.UserStats total models.UserStats
memoized time.Time memoized time.Time
@ -230,7 +247,7 @@ var (
func updateUserRoleCounts(ctx context.Context) error { func updateUserRoleCounts(ctx context.Context) error {
query := ` query := `
SELECT role AS bitrole, active, COUNT(role) AS count FROM SELECT role AS bitrole, active, COUNT(role) AS count FROM
(SELECT last_seen_at>? AS active, SUM(role) AS role (SELECT last_seen_at>? AS active, last_seen_at>? AS daily_active, SUM(role) AS role
FROM (SELECT FROM (SELECT
u.id, u.id,
CASE org_user.role CASE org_user.role
@ -242,18 +259,20 @@ SELECT role AS bitrole, active, COUNT(role) AS count FROM
FROM ` + dialect.Quote("user") + ` AS u INNER JOIN org_user ON org_user.user_id = u.id FROM ` + dialect.Quote("user") + ` AS u INNER JOIN org_user ON org_user.user_id = u.id
GROUP BY u.id, u.last_seen_at, org_user.role) AS t2 GROUP BY u.id, u.last_seen_at, org_user.role) AS t2
GROUP BY id, last_seen_at) AS t1 GROUP BY id, last_seen_at) AS t1
GROUP BY active, role;` GROUP BY active, daily_active, role;`
activeUserDeadline := time.Now().Add(-activeUserTimeLimit) activeUserDeadline := time.Now().Add(-activeUserTimeLimit)
dailyActiveUserDeadline := time.Now().Add(-dailyActiveUserTimeLimit)
type rolebitmap struct { type rolebitmap struct {
Active bool Active bool
DailyActive bool
Bitrole int64 Bitrole int64
Count int64 Count int64
} }
bitmap := []rolebitmap{} bitmap := []rolebitmap{}
err := x.Context(ctx).SQL(query, activeUserDeadline).Find(&bitmap) err := x.Context(ctx).SQL(query, activeUserDeadline, dailyActiveUserDeadline).Find(&bitmap)
if err != nil { if err != nil {
return err return err
} }
@ -271,6 +290,9 @@ GROUP BY active, role;`
if role.Active { if role.Active {
memo.active = addToStats(memo.active, roletype, role.Count) memo.active = addToStats(memo.active, roletype, role.Count)
} }
if role.DailyActive {
memo.dailyActive = addToStats(memo.dailyActive, roletype, role.Count)
}
} }
userStatsCache = memo userStatsCache = memo