metrics.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. package metrics
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "net/http"
  6. "runtime"
  7. "strings"
  8. "time"
  9. "github.com/grafana/grafana/pkg/bus"
  10. "github.com/grafana/grafana/pkg/models"
  11. "github.com/grafana/grafana/pkg/plugins"
  12. "github.com/grafana/grafana/pkg/setting"
  13. "github.com/prometheus/client_golang/prometheus"
  14. )
  15. const exporterName = "grafana"
  16. var (
  17. M_Instance_Start prometheus.Counter
  18. M_Page_Status *prometheus.CounterVec
  19. M_Api_Status *prometheus.CounterVec
  20. M_Proxy_Status *prometheus.CounterVec
  21. M_Http_Request_Total *prometheus.CounterVec
  22. M_Http_Request_Summary *prometheus.SummaryVec
  23. M_Api_User_SignUpStarted prometheus.Counter
  24. M_Api_User_SignUpCompleted prometheus.Counter
  25. M_Api_User_SignUpInvite prometheus.Counter
  26. M_Api_Dashboard_Save prometheus.Summary
  27. M_Api_Dashboard_Get prometheus.Summary
  28. M_Api_Dashboard_Search prometheus.Summary
  29. M_Api_Admin_User_Create prometheus.Counter
  30. M_Api_Login_Post prometheus.Counter
  31. M_Api_Login_OAuth prometheus.Counter
  32. M_Api_Org_Create prometheus.Counter
  33. M_Api_Dashboard_Snapshot_Create prometheus.Counter
  34. M_Api_Dashboard_Snapshot_External prometheus.Counter
  35. M_Api_Dashboard_Snapshot_Get prometheus.Counter
  36. M_Api_Dashboard_Insert prometheus.Counter
  37. M_Alerting_Result_State *prometheus.CounterVec
  38. M_Alerting_Notification_Sent *prometheus.CounterVec
  39. M_Aws_CloudWatch_GetMetricStatistics prometheus.Counter
  40. M_Aws_CloudWatch_ListMetrics prometheus.Counter
  41. M_Aws_CloudWatch_GetMetricData prometheus.Counter
  42. M_DB_DataSource_QueryById prometheus.Counter
  43. // Timers
  44. M_DataSource_ProxyReq_Timer prometheus.Summary
  45. M_Alerting_Execution_Time prometheus.Summary
  46. // StatTotals
  47. M_Alerting_Active_Alerts prometheus.Gauge
  48. M_StatTotal_Dashboards prometheus.Gauge
  49. M_StatTotal_Users prometheus.Gauge
  50. M_StatActive_Users prometheus.Gauge
  51. M_StatTotal_Orgs prometheus.Gauge
  52. M_StatTotal_Playlists prometheus.Gauge
  53. // M_Grafana_Version is a gauge that contains build info about this binary
  54. //
  55. // Deprecated: use M_Grafana_Build_Version instead.
  56. M_Grafana_Version *prometheus.GaugeVec
  57. // grafanaBuildVersion is a gauge that contains build info about this binary
  58. grafanaBuildVersion *prometheus.GaugeVec
  59. )
  60. func newCounterVecStartingAtZero(opts prometheus.CounterOpts, labels []string, labelValues ...string) *prometheus.CounterVec {
  61. counter := prometheus.NewCounterVec(opts, labels)
  62. for _, label := range labelValues {
  63. counter.WithLabelValues(label).Add(0)
  64. }
  65. return counter
  66. }
  67. func newCounterStartingAtZero(opts prometheus.CounterOpts, labelValues ...string) prometheus.Counter {
  68. counter := prometheus.NewCounter(opts)
  69. counter.Add(0)
  70. return counter
  71. }
  72. func init() {
  73. M_Instance_Start = prometheus.NewCounter(prometheus.CounterOpts{
  74. Name: "instance_start_total",
  75. Help: "counter for started instances",
  76. Namespace: exporterName,
  77. })
  78. httpStatusCodes := []string{"200", "404", "500", "unknown"}
  79. M_Page_Status = newCounterVecStartingAtZero(
  80. prometheus.CounterOpts{
  81. Name: "page_response_status_total",
  82. Help: "page http response status",
  83. Namespace: exporterName,
  84. }, []string{"code"}, httpStatusCodes...)
  85. M_Api_Status = newCounterVecStartingAtZero(
  86. prometheus.CounterOpts{
  87. Name: "api_response_status_total",
  88. Help: "api http response status",
  89. Namespace: exporterName,
  90. }, []string{"code"}, httpStatusCodes...)
  91. M_Proxy_Status = newCounterVecStartingAtZero(
  92. prometheus.CounterOpts{
  93. Name: "proxy_response_status_total",
  94. Help: "proxy http response status",
  95. Namespace: exporterName,
  96. }, []string{"code"}, httpStatusCodes...)
  97. M_Http_Request_Total = prometheus.NewCounterVec(
  98. prometheus.CounterOpts{
  99. Name: "http_request_total",
  100. Help: "http request counter",
  101. },
  102. []string{"handler", "statuscode", "method"},
  103. )
  104. M_Http_Request_Summary = prometheus.NewSummaryVec(
  105. prometheus.SummaryOpts{
  106. Name: "http_request_duration_milliseconds",
  107. Help: "http request summary",
  108. },
  109. []string{"handler", "statuscode", "method"},
  110. )
  111. M_Api_User_SignUpStarted = newCounterStartingAtZero(prometheus.CounterOpts{
  112. Name: "api_user_signup_started_total",
  113. Help: "amount of users who started the signup flow",
  114. Namespace: exporterName,
  115. })
  116. M_Api_User_SignUpCompleted = newCounterStartingAtZero(prometheus.CounterOpts{
  117. Name: "api_user_signup_completed_total",
  118. Help: "amount of users who completed the signup flow",
  119. Namespace: exporterName,
  120. })
  121. M_Api_User_SignUpInvite = newCounterStartingAtZero(prometheus.CounterOpts{
  122. Name: "api_user_signup_invite_total",
  123. Help: "amount of users who have been invited",
  124. Namespace: exporterName,
  125. })
  126. M_Api_Dashboard_Save = prometheus.NewSummary(prometheus.SummaryOpts{
  127. Name: "api_dashboard_save_milliseconds",
  128. Help: "summary for dashboard save duration",
  129. Namespace: exporterName,
  130. })
  131. M_Api_Dashboard_Get = prometheus.NewSummary(prometheus.SummaryOpts{
  132. Name: "api_dashboard_get_milliseconds",
  133. Help: "summary for dashboard get duration",
  134. Namespace: exporterName,
  135. })
  136. M_Api_Dashboard_Search = prometheus.NewSummary(prometheus.SummaryOpts{
  137. Name: "api_dashboard_search_milliseconds",
  138. Help: "summary for dashboard search duration",
  139. Namespace: exporterName,
  140. })
  141. M_Api_Admin_User_Create = newCounterStartingAtZero(prometheus.CounterOpts{
  142. Name: "api_admin_user_created_total",
  143. Help: "api admin user created counter",
  144. Namespace: exporterName,
  145. })
  146. M_Api_Login_Post = newCounterStartingAtZero(prometheus.CounterOpts{
  147. Name: "api_login_post_total",
  148. Help: "api login post counter",
  149. Namespace: exporterName,
  150. })
  151. M_Api_Login_OAuth = newCounterStartingAtZero(prometheus.CounterOpts{
  152. Name: "api_login_oauth_total",
  153. Help: "api login oauth counter",
  154. Namespace: exporterName,
  155. })
  156. M_Api_Org_Create = newCounterStartingAtZero(prometheus.CounterOpts{
  157. Name: "api_org_create_total",
  158. Help: "api org created counter",
  159. Namespace: exporterName,
  160. })
  161. M_Api_Dashboard_Snapshot_Create = newCounterStartingAtZero(prometheus.CounterOpts{
  162. Name: "api_dashboard_snapshot_create_total",
  163. Help: "dashboard snapshots created",
  164. Namespace: exporterName,
  165. })
  166. M_Api_Dashboard_Snapshot_External = newCounterStartingAtZero(prometheus.CounterOpts{
  167. Name: "api_dashboard_snapshot_external_total",
  168. Help: "external dashboard snapshots created",
  169. Namespace: exporterName,
  170. })
  171. M_Api_Dashboard_Snapshot_Get = newCounterStartingAtZero(prometheus.CounterOpts{
  172. Name: "api_dashboard_snapshot_get_total",
  173. Help: "loaded dashboards",
  174. Namespace: exporterName,
  175. })
  176. M_Api_Dashboard_Insert = newCounterStartingAtZero(prometheus.CounterOpts{
  177. Name: "api_models_dashboard_insert_total",
  178. Help: "dashboards inserted ",
  179. Namespace: exporterName,
  180. })
  181. M_Alerting_Result_State = prometheus.NewCounterVec(prometheus.CounterOpts{
  182. Name: "alerting_result_total",
  183. Help: "alert execution result counter",
  184. Namespace: exporterName,
  185. }, []string{"state"})
  186. M_Alerting_Notification_Sent = prometheus.NewCounterVec(prometheus.CounterOpts{
  187. Name: "alerting_notification_sent_total",
  188. Help: "counter for how many alert notifications been sent",
  189. Namespace: exporterName,
  190. }, []string{"type"})
  191. M_Aws_CloudWatch_GetMetricStatistics = newCounterStartingAtZero(prometheus.CounterOpts{
  192. Name: "aws_cloudwatch_get_metric_statistics_total",
  193. Help: "counter for getting metric statistics from aws",
  194. Namespace: exporterName,
  195. })
  196. M_Aws_CloudWatch_ListMetrics = newCounterStartingAtZero(prometheus.CounterOpts{
  197. Name: "aws_cloudwatch_list_metrics_total",
  198. Help: "counter for getting list of metrics from aws",
  199. Namespace: exporterName,
  200. })
  201. M_Aws_CloudWatch_GetMetricData = newCounterStartingAtZero(prometheus.CounterOpts{
  202. Name: "aws_cloudwatch_get_metric_data_total",
  203. Help: "counter for getting metric data time series from aws",
  204. Namespace: exporterName,
  205. })
  206. M_DB_DataSource_QueryById = newCounterStartingAtZero(prometheus.CounterOpts{
  207. Name: "db_datasource_query_by_id_total",
  208. Help: "counter for getting datasource by id",
  209. Namespace: exporterName,
  210. })
  211. M_DataSource_ProxyReq_Timer = prometheus.NewSummary(prometheus.SummaryOpts{
  212. Name: "api_dataproxy_request_all_milliseconds",
  213. Help: "summary for dataproxy request duration",
  214. Namespace: exporterName,
  215. })
  216. M_Alerting_Execution_Time = prometheus.NewSummary(prometheus.SummaryOpts{
  217. Name: "alerting_execution_time_milliseconds",
  218. Help: "summary of alert exeuction duration",
  219. Namespace: exporterName,
  220. })
  221. M_Alerting_Active_Alerts = prometheus.NewGauge(prometheus.GaugeOpts{
  222. Name: "alerting_active_alerts",
  223. Help: "amount of active alerts",
  224. Namespace: exporterName,
  225. })
  226. M_StatTotal_Dashboards = prometheus.NewGauge(prometheus.GaugeOpts{
  227. Name: "stat_totals_dashboard",
  228. Help: "total amount of dashboards",
  229. Namespace: exporterName,
  230. })
  231. M_StatTotal_Users = prometheus.NewGauge(prometheus.GaugeOpts{
  232. Name: "stat_total_users",
  233. Help: "total amount of users",
  234. Namespace: exporterName,
  235. })
  236. M_StatActive_Users = prometheus.NewGauge(prometheus.GaugeOpts{
  237. Name: "stat_active_users",
  238. Help: "number of active users",
  239. Namespace: exporterName,
  240. })
  241. M_StatTotal_Orgs = prometheus.NewGauge(prometheus.GaugeOpts{
  242. Name: "stat_total_orgs",
  243. Help: "total amount of orgs",
  244. Namespace: exporterName,
  245. })
  246. M_StatTotal_Playlists = prometheus.NewGauge(prometheus.GaugeOpts{
  247. Name: "stat_total_playlists",
  248. Help: "total amount of playlists",
  249. Namespace: exporterName,
  250. })
  251. M_Grafana_Version = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  252. Name: "info",
  253. Help: "Information about the Grafana. This metric is deprecated. please use `grafana_build_info`",
  254. Namespace: exporterName,
  255. }, []string{"version"})
  256. grafanaBuildVersion = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  257. Name: "build_info",
  258. Help: "A metric with a constant '1' value labeled by version, revision, branch, and goversion from which Grafana was built.",
  259. Namespace: exporterName,
  260. }, []string{"version", "revision", "branch", "goversion"})
  261. }
  262. // SetBuildInformation sets the build information for this binary
  263. func SetBuildInformation(version, revision, branch string) {
  264. // We export this info twice for backwards compatibility.
  265. // Once this have been released for some time we should be able to remote `M_Grafana_Version`
  266. // The reason we added a new one is that its common practice in the prometheus community
  267. // to name this metric `*_build_info` so its easy to do aggregation on all programs.
  268. M_Grafana_Version.WithLabelValues(version).Set(1)
  269. grafanaBuildVersion.WithLabelValues(version, revision, branch, runtime.Version()).Set(1)
  270. }
  271. func initMetricVars() {
  272. prometheus.MustRegister(
  273. M_Instance_Start,
  274. M_Page_Status,
  275. M_Api_Status,
  276. M_Proxy_Status,
  277. M_Http_Request_Total,
  278. M_Http_Request_Summary,
  279. M_Api_User_SignUpStarted,
  280. M_Api_User_SignUpCompleted,
  281. M_Api_User_SignUpInvite,
  282. M_Api_Dashboard_Save,
  283. M_Api_Dashboard_Get,
  284. M_Api_Dashboard_Search,
  285. M_DataSource_ProxyReq_Timer,
  286. M_Alerting_Execution_Time,
  287. M_Api_Admin_User_Create,
  288. M_Api_Login_Post,
  289. M_Api_Login_OAuth,
  290. M_Api_Org_Create,
  291. M_Api_Dashboard_Snapshot_Create,
  292. M_Api_Dashboard_Snapshot_External,
  293. M_Api_Dashboard_Snapshot_Get,
  294. M_Api_Dashboard_Insert,
  295. M_Alerting_Result_State,
  296. M_Alerting_Notification_Sent,
  297. M_Aws_CloudWatch_GetMetricStatistics,
  298. M_Aws_CloudWatch_ListMetrics,
  299. M_Aws_CloudWatch_GetMetricData,
  300. M_DB_DataSource_QueryById,
  301. M_Alerting_Active_Alerts,
  302. M_StatTotal_Dashboards,
  303. M_StatTotal_Users,
  304. M_StatActive_Users,
  305. M_StatTotal_Orgs,
  306. M_StatTotal_Playlists,
  307. M_Grafana_Version,
  308. grafanaBuildVersion)
  309. }
  310. func updateTotalStats() {
  311. statsQuery := models.GetSystemStatsQuery{}
  312. if err := bus.Dispatch(&statsQuery); err != nil {
  313. metricsLogger.Error("Failed to get system stats", "error", err)
  314. return
  315. }
  316. M_StatTotal_Dashboards.Set(float64(statsQuery.Result.Dashboards))
  317. M_StatTotal_Users.Set(float64(statsQuery.Result.Users))
  318. M_StatActive_Users.Set(float64(statsQuery.Result.ActiveUsers))
  319. M_StatTotal_Playlists.Set(float64(statsQuery.Result.Playlists))
  320. M_StatTotal_Orgs.Set(float64(statsQuery.Result.Orgs))
  321. }
  322. var usageStatsURL = "https://stats.grafana.org/grafana-usage-report"
  323. func getEdition() string {
  324. if setting.IsEnterprise {
  325. return "enterprise"
  326. } else {
  327. return "oss"
  328. }
  329. }
  330. func sendUsageStats(oauthProviders map[string]bool) {
  331. if !setting.ReportingEnabled {
  332. return
  333. }
  334. metricsLogger.Debug("Sending anonymous usage stats to stats.grafana.org")
  335. version := strings.Replace(setting.BuildVersion, ".", "_", -1)
  336. metrics := map[string]interface{}{}
  337. report := map[string]interface{}{
  338. "version": version,
  339. "metrics": metrics,
  340. "os": runtime.GOOS,
  341. "arch": runtime.GOARCH,
  342. "edition": getEdition(),
  343. "packaging": setting.Packaging,
  344. }
  345. statsQuery := models.GetSystemStatsQuery{}
  346. if err := bus.Dispatch(&statsQuery); err != nil {
  347. metricsLogger.Error("Failed to get system stats", "error", err)
  348. return
  349. }
  350. metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards
  351. metrics["stats.users.count"] = statsQuery.Result.Users
  352. metrics["stats.orgs.count"] = statsQuery.Result.Orgs
  353. metrics["stats.playlist.count"] = statsQuery.Result.Playlists
  354. metrics["stats.plugins.apps.count"] = len(plugins.Apps)
  355. metrics["stats.plugins.panels.count"] = len(plugins.Panels)
  356. metrics["stats.plugins.datasources.count"] = len(plugins.DataSources)
  357. metrics["stats.alerts.count"] = statsQuery.Result.Alerts
  358. metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers
  359. metrics["stats.datasources.count"] = statsQuery.Result.Datasources
  360. metrics["stats.stars.count"] = statsQuery.Result.Stars
  361. metrics["stats.folders.count"] = statsQuery.Result.Folders
  362. metrics["stats.dashboard_permissions.count"] = statsQuery.Result.DashboardPermissions
  363. metrics["stats.folder_permissions.count"] = statsQuery.Result.FolderPermissions
  364. metrics["stats.provisioned_dashboards.count"] = statsQuery.Result.ProvisionedDashboards
  365. metrics["stats.snapshots.count"] = statsQuery.Result.Snapshots
  366. metrics["stats.teams.count"] = statsQuery.Result.Teams
  367. dsStats := models.GetDataSourceStatsQuery{}
  368. if err := bus.Dispatch(&dsStats); err != nil {
  369. metricsLogger.Error("Failed to get datasource stats", "error", err)
  370. return
  371. }
  372. // send counters for each data source
  373. // but ignore any custom data sources
  374. // as sending that name could be sensitive information
  375. dsOtherCount := 0
  376. for _, dsStat := range dsStats.Result {
  377. if models.IsKnownDataSourcePlugin(dsStat.Type) {
  378. metrics["stats.ds."+dsStat.Type+".count"] = dsStat.Count
  379. } else {
  380. dsOtherCount += dsStat.Count
  381. }
  382. }
  383. metrics["stats.ds.other.count"] = dsOtherCount
  384. metrics["stats.packaging."+setting.Packaging+".count"] = 1
  385. dsAccessStats := models.GetDataSourceAccessStatsQuery{}
  386. if err := bus.Dispatch(&dsAccessStats); err != nil {
  387. metricsLogger.Error("Failed to get datasource access stats", "error", err)
  388. return
  389. }
  390. // send access counters for each data source
  391. // but ignore any custom data sources
  392. // as sending that name could be sensitive information
  393. dsAccessOtherCount := make(map[string]int64)
  394. for _, dsAccessStat := range dsAccessStats.Result {
  395. if dsAccessStat.Access == "" {
  396. continue
  397. }
  398. access := strings.ToLower(dsAccessStat.Access)
  399. if models.IsKnownDataSourcePlugin(dsAccessStat.Type) {
  400. metrics["stats.ds_access."+dsAccessStat.Type+"."+access+".count"] = dsAccessStat.Count
  401. } else {
  402. old := dsAccessOtherCount[access]
  403. dsAccessOtherCount[access] = old + dsAccessStat.Count
  404. }
  405. }
  406. for access, count := range dsAccessOtherCount {
  407. metrics["stats.ds_access.other."+access+".count"] = count
  408. }
  409. anStats := models.GetAlertNotifierUsageStatsQuery{}
  410. if err := bus.Dispatch(&anStats); err != nil {
  411. metricsLogger.Error("Failed to get alert notification stats", "error", err)
  412. return
  413. }
  414. for _, stats := range anStats.Result {
  415. metrics["stats.alert_notifiers."+stats.Type+".count"] = stats.Count
  416. }
  417. authTypes := map[string]bool{}
  418. authTypes["anonymous"] = setting.AnonymousEnabled
  419. authTypes["basic_auth"] = setting.BasicAuthEnabled
  420. authTypes["ldap"] = setting.LdapEnabled
  421. authTypes["auth_proxy"] = setting.AuthProxyEnabled
  422. for provider, enabled := range oauthProviders {
  423. authTypes["oauth_"+provider] = enabled
  424. }
  425. for authType, enabled := range authTypes {
  426. enabledValue := 0
  427. if enabled {
  428. enabledValue = 1
  429. }
  430. metrics["stats.auth_enabled."+authType+".count"] = enabledValue
  431. }
  432. out, _ := json.MarshalIndent(report, "", " ")
  433. data := bytes.NewBuffer(out)
  434. client := http.Client{Timeout: 5 * time.Second}
  435. go client.Post(usageStatsURL, "application/json", data)
  436. }