mirror of https://github.com/grafana/grafana
parent
487e7b5ea6
commit
6e7941d396
@ -0,0 +1,54 @@ |
|||||||
|
package usagestats |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/bus" |
||||||
|
"github.com/grafana/grafana/pkg/services/auth" |
||||||
|
"github.com/grafana/grafana/pkg/social" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/log" |
||||||
|
"github.com/grafana/grafana/pkg/registry" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
) |
||||||
|
|
||||||
|
var metricsLogger log.Logger = log.New("metrics") |
||||||
|
|
||||||
|
func init() { |
||||||
|
registry.RegisterService(&UsageStatsService{}) |
||||||
|
} |
||||||
|
|
||||||
|
type UsageStatsService struct { |
||||||
|
Cfg *setting.Cfg `inject:""` |
||||||
|
TokenService *auth.UserAuthTokenService `inject:""` |
||||||
|
Bus bus.Bus `inject:""` |
||||||
|
|
||||||
|
oauthProviders map[string]bool |
||||||
|
} |
||||||
|
|
||||||
|
func (uss *UsageStatsService) Init() error { |
||||||
|
|
||||||
|
uss.oauthProviders = social.GetOAuthProviders(uss.Cfg) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (uss *UsageStatsService) Run(ctx context.Context) error { |
||||||
|
uss.updateTotalStats() |
||||||
|
|
||||||
|
onceEveryDayTick := time.NewTicker(time.Hour * 24) |
||||||
|
everyMinuteTicker := time.NewTicker(time.Minute) |
||||||
|
defer onceEveryDayTick.Stop() |
||||||
|
defer everyMinuteTicker.Stop() |
||||||
|
|
||||||
|
for { |
||||||
|
select { |
||||||
|
case <-onceEveryDayTick.C: |
||||||
|
uss.sendUsageStats(uss.oauthProviders) |
||||||
|
case <-everyMinuteTicker.C: |
||||||
|
uss.updateTotalStats() |
||||||
|
case <-ctx.Done(): |
||||||
|
return ctx.Err() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,168 @@ |
|||||||
|
package usagestats |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"net/http" |
||||||
|
"runtime" |
||||||
|
"strings" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/metrics" |
||||||
|
"github.com/grafana/grafana/pkg/models" |
||||||
|
"github.com/grafana/grafana/pkg/plugins" |
||||||
|
"github.com/grafana/grafana/pkg/setting" |
||||||
|
) |
||||||
|
|
||||||
|
var usageStatsURL = "https://stats.grafana.org/grafana-usage-report" |
||||||
|
|
||||||
|
func (uss *UsageStatsService) sendUsageStats(oauthProviders map[string]bool) { |
||||||
|
if !setting.ReportingEnabled { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
metricsLogger.Debug(fmt.Sprintf("Sending anonymous usage stats to %s", usageStatsURL)) |
||||||
|
|
||||||
|
version := strings.Replace(setting.BuildVersion, ".", "_", -1) |
||||||
|
|
||||||
|
metrics := map[string]interface{}{} |
||||||
|
report := map[string]interface{}{ |
||||||
|
"version": version, |
||||||
|
"metrics": metrics, |
||||||
|
"os": runtime.GOOS, |
||||||
|
"arch": runtime.GOARCH, |
||||||
|
"edition": getEdition(), |
||||||
|
"packaging": setting.Packaging, |
||||||
|
} |
||||||
|
|
||||||
|
statsQuery := models.GetSystemStatsQuery{} |
||||||
|
if err := uss.Bus.Dispatch(&statsQuery); err != nil { |
||||||
|
metricsLogger.Error("Failed to get system stats", "error", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards |
||||||
|
metrics["stats.users.count"] = statsQuery.Result.Users |
||||||
|
metrics["stats.orgs.count"] = statsQuery.Result.Orgs |
||||||
|
metrics["stats.playlist.count"] = statsQuery.Result.Playlists |
||||||
|
metrics["stats.plugins.apps.count"] = len(plugins.Apps) |
||||||
|
metrics["stats.plugins.panels.count"] = len(plugins.Panels) |
||||||
|
metrics["stats.plugins.datasources.count"] = len(plugins.DataSources) |
||||||
|
metrics["stats.alerts.count"] = statsQuery.Result.Alerts |
||||||
|
metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers |
||||||
|
metrics["stats.datasources.count"] = statsQuery.Result.Datasources |
||||||
|
metrics["stats.stars.count"] = statsQuery.Result.Stars |
||||||
|
metrics["stats.folders.count"] = statsQuery.Result.Folders |
||||||
|
metrics["stats.dashboard_permissions.count"] = statsQuery.Result.DashboardPermissions |
||||||
|
metrics["stats.folder_permissions.count"] = statsQuery.Result.FolderPermissions |
||||||
|
metrics["stats.provisioned_dashboards.count"] = statsQuery.Result.ProvisionedDashboards |
||||||
|
metrics["stats.snapshots.count"] = statsQuery.Result.Snapshots |
||||||
|
metrics["stats.teams.count"] = statsQuery.Result.Teams |
||||||
|
|
||||||
|
dsStats := models.GetDataSourceStatsQuery{} |
||||||
|
if err := uss.Bus.Dispatch(&dsStats); err != nil { |
||||||
|
metricsLogger.Error("Failed to get datasource stats", "error", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// send counters for each data source
|
||||||
|
// but ignore any custom data sources
|
||||||
|
// as sending that name could be sensitive information
|
||||||
|
dsOtherCount := 0 |
||||||
|
for _, dsStat := range dsStats.Result { |
||||||
|
if models.IsKnownDataSourcePlugin(dsStat.Type) { |
||||||
|
metrics["stats.ds."+dsStat.Type+".count"] = dsStat.Count |
||||||
|
} else { |
||||||
|
dsOtherCount += dsStat.Count |
||||||
|
} |
||||||
|
} |
||||||
|
metrics["stats.ds.other.count"] = dsOtherCount |
||||||
|
|
||||||
|
metrics["stats.packaging."+setting.Packaging+".count"] = 1 |
||||||
|
|
||||||
|
dsAccessStats := models.GetDataSourceAccessStatsQuery{} |
||||||
|
if err := uss.Bus.Dispatch(&dsAccessStats); err != nil { |
||||||
|
metricsLogger.Error("Failed to get datasource access stats", "error", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
// send access counters for each data source
|
||||||
|
// but ignore any custom data sources
|
||||||
|
// as sending that name could be sensitive information
|
||||||
|
dsAccessOtherCount := make(map[string]int64) |
||||||
|
for _, dsAccessStat := range dsAccessStats.Result { |
||||||
|
if dsAccessStat.Access == "" { |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
access := strings.ToLower(dsAccessStat.Access) |
||||||
|
|
||||||
|
if models.IsKnownDataSourcePlugin(dsAccessStat.Type) { |
||||||
|
metrics["stats.ds_access."+dsAccessStat.Type+"."+access+".count"] = dsAccessStat.Count |
||||||
|
} else { |
||||||
|
old := dsAccessOtherCount[access] |
||||||
|
dsAccessOtherCount[access] = old + dsAccessStat.Count |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for access, count := range dsAccessOtherCount { |
||||||
|
metrics["stats.ds_access.other."+access+".count"] = count |
||||||
|
} |
||||||
|
|
||||||
|
anStats := models.GetAlertNotifierUsageStatsQuery{} |
||||||
|
if err := uss.Bus.Dispatch(&anStats); err != nil { |
||||||
|
metricsLogger.Error("Failed to get alert notification stats", "error", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
for _, stats := range anStats.Result { |
||||||
|
metrics["stats.alert_notifiers."+stats.Type+".count"] = stats.Count |
||||||
|
} |
||||||
|
|
||||||
|
authTypes := map[string]bool{} |
||||||
|
authTypes["anonymous"] = setting.AnonymousEnabled |
||||||
|
authTypes["basic_auth"] = setting.BasicAuthEnabled |
||||||
|
authTypes["ldap"] = setting.LdapEnabled |
||||||
|
authTypes["auth_proxy"] = setting.AuthProxyEnabled |
||||||
|
|
||||||
|
for provider, enabled := range oauthProviders { |
||||||
|
authTypes["oauth_"+provider] = enabled |
||||||
|
} |
||||||
|
|
||||||
|
for authType, enabled := range authTypes { |
||||||
|
enabledValue := 0 |
||||||
|
if enabled { |
||||||
|
enabledValue = 1 |
||||||
|
} |
||||||
|
metrics["stats.auth_enabled."+authType+".count"] = enabledValue |
||||||
|
} |
||||||
|
|
||||||
|
out, _ := json.MarshalIndent(report, "", " ") |
||||||
|
data := bytes.NewBuffer(out) |
||||||
|
|
||||||
|
client := http.Client{Timeout: 5 * time.Second} |
||||||
|
go client.Post(usageStatsURL, "application/json", data) |
||||||
|
} |
||||||
|
|
||||||
|
func (uss *UsageStatsService) updateTotalStats() { |
||||||
|
statsQuery := models.GetSystemStatsQuery{} |
||||||
|
if err := uss.Bus.Dispatch(&statsQuery); err != nil { |
||||||
|
metricsLogger.Error("Failed to get system stats", "error", err) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
metrics.M_StatTotal_Dashboards.Set(float64(statsQuery.Result.Dashboards)) |
||||||
|
metrics.M_StatTotal_Users.Set(float64(statsQuery.Result.Users)) |
||||||
|
metrics.M_StatActive_Users.Set(float64(statsQuery.Result.ActiveUsers)) |
||||||
|
metrics.M_StatTotal_Playlists.Set(float64(statsQuery.Result.Playlists)) |
||||||
|
metrics.M_StatTotal_Orgs.Set(float64(statsQuery.Result.Orgs)) |
||||||
|
} |
||||||
|
|
||||||
|
func getEdition() string { |
||||||
|
if setting.IsEnterprise { |
||||||
|
return "enterprise" |
||||||
|
} else { |
||||||
|
return "oss" |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue