metrics.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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_DB_DataSource_QueryById prometheus.Counter
  42. // Timers
  43. M_DataSource_ProxyReq_Timer prometheus.Summary
  44. M_Alerting_Execution_Time prometheus.Summary
  45. // StatTotals
  46. M_Alerting_Active_Alerts prometheus.Gauge
  47. M_StatTotal_Dashboards prometheus.Gauge
  48. M_StatTotal_Users prometheus.Gauge
  49. M_StatTotal_Orgs prometheus.Gauge
  50. M_StatTotal_Playlists prometheus.Gauge
  51. M_Grafana_Version *prometheus.GaugeVec
  52. )
  53. func init() {
  54. M_Instance_Start = prometheus.NewCounter(prometheus.CounterOpts{
  55. Name: "instance_start_total",
  56. Help: "counter for started instances",
  57. Namespace: exporterName,
  58. })
  59. M_Page_Status = prometheus.NewCounterVec(
  60. prometheus.CounterOpts{
  61. Name: "page_response_status_total",
  62. Help: "page http response status",
  63. Namespace: exporterName,
  64. },
  65. []string{"code"},
  66. )
  67. M_Api_Status = prometheus.NewCounterVec(
  68. prometheus.CounterOpts{
  69. Name: "api_response_status_total",
  70. Help: "api http response status",
  71. Namespace: exporterName,
  72. },
  73. []string{"code"},
  74. )
  75. M_Proxy_Status = prometheus.NewCounterVec(
  76. prometheus.CounterOpts{
  77. Name: "proxy_response_status_total",
  78. Help: "proxy http response status",
  79. Namespace: exporterName,
  80. },
  81. []string{"code"},
  82. )
  83. M_Http_Request_Total = prometheus.NewCounterVec(
  84. prometheus.CounterOpts{
  85. Name: "http_request_total",
  86. Help: "http request counter",
  87. },
  88. []string{"handler", "statuscode", "method"},
  89. )
  90. M_Http_Request_Summary = prometheus.NewSummaryVec(
  91. prometheus.SummaryOpts{
  92. Name: "http_request_duration_milliseconds",
  93. Help: "http request summary",
  94. },
  95. []string{"handler", "statuscode", "method"},
  96. )
  97. M_Api_User_SignUpStarted = prometheus.NewCounter(prometheus.CounterOpts{
  98. Name: "api_user_signup_started_total",
  99. Help: "amount of users who started the signup flow",
  100. Namespace: exporterName,
  101. })
  102. M_Api_User_SignUpCompleted = prometheus.NewCounter(prometheus.CounterOpts{
  103. Name: "api_user_signup_completed_total",
  104. Help: "amount of users who completed the signup flow",
  105. Namespace: exporterName,
  106. })
  107. M_Api_User_SignUpInvite = prometheus.NewCounter(prometheus.CounterOpts{
  108. Name: "api_user_signup_invite_total",
  109. Help: "amount of users who have been invited",
  110. Namespace: exporterName,
  111. })
  112. M_Api_Dashboard_Save = prometheus.NewSummary(prometheus.SummaryOpts{
  113. Name: "api_dashboard_save_milliseconds",
  114. Help: "summary for dashboard save duration",
  115. Namespace: exporterName,
  116. })
  117. M_Api_Dashboard_Get = prometheus.NewSummary(prometheus.SummaryOpts{
  118. Name: "api_dashboard_get_milliseconds",
  119. Help: "summary for dashboard get duration",
  120. Namespace: exporterName,
  121. })
  122. M_Api_Dashboard_Search = prometheus.NewSummary(prometheus.SummaryOpts{
  123. Name: "api_dashboard_search_milliseconds",
  124. Help: "summary for dashboard search duration",
  125. Namespace: exporterName,
  126. })
  127. M_Api_Admin_User_Create = prometheus.NewCounter(prometheus.CounterOpts{
  128. Name: "api_admin_user_created_total",
  129. Help: "api admin user created counter",
  130. Namespace: exporterName,
  131. })
  132. M_Api_Login_Post = prometheus.NewCounter(prometheus.CounterOpts{
  133. Name: "api_login_post_total",
  134. Help: "api login post counter",
  135. Namespace: exporterName,
  136. })
  137. M_Api_Login_OAuth = prometheus.NewCounter(prometheus.CounterOpts{
  138. Name: "api_login_oauth_total",
  139. Help: "api login oauth counter",
  140. Namespace: exporterName,
  141. })
  142. M_Api_Org_Create = prometheus.NewCounter(prometheus.CounterOpts{
  143. Name: "api_org_create_total",
  144. Help: "api org created counter",
  145. Namespace: exporterName,
  146. })
  147. M_Api_Dashboard_Snapshot_Create = prometheus.NewCounter(prometheus.CounterOpts{
  148. Name: "api_dashboard_snapshot_create_total",
  149. Help: "dashboard snapshots created",
  150. Namespace: exporterName,
  151. })
  152. M_Api_Dashboard_Snapshot_External = prometheus.NewCounter(prometheus.CounterOpts{
  153. Name: "api_dashboard_snapshot_external_total",
  154. Help: "external dashboard snapshots created",
  155. Namespace: exporterName,
  156. })
  157. M_Api_Dashboard_Snapshot_Get = prometheus.NewCounter(prometheus.CounterOpts{
  158. Name: "api_dashboard_snapshot_get_total",
  159. Help: "loaded dashboards",
  160. Namespace: exporterName,
  161. })
  162. M_Api_Dashboard_Insert = prometheus.NewCounter(prometheus.CounterOpts{
  163. Name: "api_models_dashboard_insert_total",
  164. Help: "dashboards inserted ",
  165. Namespace: exporterName,
  166. })
  167. M_Alerting_Result_State = prometheus.NewCounterVec(prometheus.CounterOpts{
  168. Name: "alerting_result_total",
  169. Help: "alert execution result counter",
  170. Namespace: exporterName,
  171. }, []string{"state"})
  172. M_Alerting_Notification_Sent = prometheus.NewCounterVec(prometheus.CounterOpts{
  173. Name: "alerting_notification_sent_total",
  174. Help: "counter for how many alert notifications been sent",
  175. Namespace: exporterName,
  176. }, []string{"type"})
  177. M_Aws_CloudWatch_GetMetricStatistics = prometheus.NewCounter(prometheus.CounterOpts{
  178. Name: "aws_cloudwatch_get_metric_statistics_total",
  179. Help: "counter for getting metric statistics from aws",
  180. Namespace: exporterName,
  181. })
  182. M_Aws_CloudWatch_ListMetrics = prometheus.NewCounter(prometheus.CounterOpts{
  183. Name: "aws_cloudwatch_list_metrics_total",
  184. Help: "counter for getting list of metrics from aws",
  185. Namespace: exporterName,
  186. })
  187. M_DB_DataSource_QueryById = prometheus.NewCounter(prometheus.CounterOpts{
  188. Name: "db_datasource_query_by_id_total",
  189. Help: "counter for getting datasource by id",
  190. Namespace: exporterName,
  191. })
  192. M_DataSource_ProxyReq_Timer = prometheus.NewSummary(prometheus.SummaryOpts{
  193. Name: "api_dataproxy_request_all_milliseconds",
  194. Help: "summary for dataproxy request duration",
  195. Namespace: exporterName,
  196. })
  197. M_Alerting_Execution_Time = prometheus.NewSummary(prometheus.SummaryOpts{
  198. Name: "alerting_execution_time_milliseconds",
  199. Help: "summary of alert exeuction duration",
  200. Namespace: exporterName,
  201. })
  202. M_Alerting_Active_Alerts = prometheus.NewGauge(prometheus.GaugeOpts{
  203. Name: "alerting_active_alerts",
  204. Help: "amount of active alerts",
  205. Namespace: exporterName,
  206. })
  207. M_StatTotal_Dashboards = prometheus.NewGauge(prometheus.GaugeOpts{
  208. Name: "stat_totals_dashboard",
  209. Help: "total amount of dashboards",
  210. Namespace: exporterName,
  211. })
  212. M_StatTotal_Users = prometheus.NewGauge(prometheus.GaugeOpts{
  213. Name: "stat_total_users",
  214. Help: "total amount of users",
  215. Namespace: exporterName,
  216. })
  217. M_StatTotal_Orgs = prometheus.NewGauge(prometheus.GaugeOpts{
  218. Name: "stat_total_orgs",
  219. Help: "total amount of orgs",
  220. Namespace: exporterName,
  221. })
  222. M_StatTotal_Playlists = prometheus.NewGauge(prometheus.GaugeOpts{
  223. Name: "stat_total_playlists",
  224. Help: "total amount of playlists",
  225. Namespace: exporterName,
  226. })
  227. M_Grafana_Version = prometheus.NewGaugeVec(prometheus.GaugeOpts{
  228. Name: "info",
  229. Help: "Information about the Grafana",
  230. Namespace: exporterName,
  231. }, []string{"version"})
  232. }
  233. func initMetricVars(settings *MetricSettings) {
  234. prometheus.MustRegister(
  235. M_Instance_Start,
  236. M_Page_Status,
  237. M_Api_Status,
  238. M_Proxy_Status,
  239. M_Http_Request_Total,
  240. M_Http_Request_Summary,
  241. M_Api_User_SignUpStarted,
  242. M_Api_User_SignUpCompleted,
  243. M_Api_User_SignUpInvite,
  244. M_Api_Dashboard_Save,
  245. M_Api_Dashboard_Get,
  246. M_Api_Dashboard_Search,
  247. M_DataSource_ProxyReq_Timer,
  248. M_Alerting_Execution_Time,
  249. M_Api_Admin_User_Create,
  250. M_Api_Login_Post,
  251. M_Api_Login_OAuth,
  252. M_Api_Org_Create,
  253. M_Api_Dashboard_Snapshot_Create,
  254. M_Api_Dashboard_Snapshot_External,
  255. M_Api_Dashboard_Snapshot_Get,
  256. M_Api_Dashboard_Insert,
  257. M_Alerting_Result_State,
  258. M_Alerting_Notification_Sent,
  259. M_Aws_CloudWatch_GetMetricStatistics,
  260. M_Aws_CloudWatch_ListMetrics,
  261. M_DB_DataSource_QueryById,
  262. M_Alerting_Active_Alerts,
  263. M_StatTotal_Dashboards,
  264. M_StatTotal_Users,
  265. M_StatTotal_Orgs,
  266. M_StatTotal_Playlists,
  267. M_Grafana_Version)
  268. go instrumentationLoop(settings)
  269. }
  270. func instrumentationLoop(settings *MetricSettings) chan struct{} {
  271. M_Instance_Start.Inc()
  272. onceEveryDayTick := time.NewTicker(time.Hour * 24)
  273. secondTicker := time.NewTicker(time.Second * time.Duration(settings.IntervalSeconds))
  274. for {
  275. select {
  276. case <-onceEveryDayTick.C:
  277. sendUsageStats()
  278. case <-secondTicker.C:
  279. updateTotalStats()
  280. }
  281. }
  282. }
  283. var metricPublishCounter int64 = 0
  284. func updateTotalStats() {
  285. metricPublishCounter++
  286. if metricPublishCounter == 1 || metricPublishCounter%10 == 0 {
  287. statsQuery := models.GetSystemStatsQuery{}
  288. if err := bus.Dispatch(&statsQuery); err != nil {
  289. metricsLogger.Error("Failed to get system stats", "error", err)
  290. return
  291. }
  292. M_StatTotal_Dashboards.Set(float64(statsQuery.Result.Dashboards))
  293. M_StatTotal_Users.Set(float64(statsQuery.Result.Users))
  294. M_StatTotal_Playlists.Set(float64(statsQuery.Result.Playlists))
  295. M_StatTotal_Orgs.Set(float64(statsQuery.Result.Orgs))
  296. }
  297. }
  298. func sendUsageStats() {
  299. if !setting.ReportingEnabled {
  300. return
  301. }
  302. metricsLogger.Debug("Sending anonymous usage stats to stats.grafana.org")
  303. version := strings.Replace(setting.BuildVersion, ".", "_", -1)
  304. metrics := map[string]interface{}{}
  305. report := map[string]interface{}{
  306. "version": version,
  307. "metrics": metrics,
  308. "os": runtime.GOOS,
  309. "arch": runtime.GOARCH,
  310. }
  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. metrics["stats.dashboards.count"] = statsQuery.Result.Dashboards
  317. metrics["stats.users.count"] = statsQuery.Result.Users
  318. metrics["stats.orgs.count"] = statsQuery.Result.Orgs
  319. metrics["stats.playlist.count"] = statsQuery.Result.Playlists
  320. metrics["stats.plugins.apps.count"] = len(plugins.Apps)
  321. metrics["stats.plugins.panels.count"] = len(plugins.Panels)
  322. metrics["stats.plugins.datasources.count"] = len(plugins.DataSources)
  323. metrics["stats.alerts.count"] = statsQuery.Result.Alerts
  324. metrics["stats.active_users.count"] = statsQuery.Result.ActiveUsers
  325. metrics["stats.datasources.count"] = statsQuery.Result.Datasources
  326. metrics["stats.stars.count"] = statsQuery.Result.Stars
  327. dsStats := models.GetDataSourceStatsQuery{}
  328. if err := bus.Dispatch(&dsStats); err != nil {
  329. metricsLogger.Error("Failed to get datasource stats", "error", err)
  330. return
  331. }
  332. // send counters for each data source
  333. // but ignore any custom data sources
  334. // as sending that name could be sensitive information
  335. dsOtherCount := 0
  336. for _, dsStat := range dsStats.Result {
  337. if models.IsKnownDataSourcePlugin(dsStat.Type) {
  338. metrics["stats.ds."+dsStat.Type+".count"] = dsStat.Count
  339. } else {
  340. dsOtherCount += dsStat.Count
  341. }
  342. }
  343. metrics["stats.ds.other.count"] = dsOtherCount
  344. out, _ := json.MarshalIndent(report, "", " ")
  345. data := bytes.NewBuffer(out)
  346. client := http.Client{Timeout: time.Duration(5 * time.Second)}
  347. go client.Post("https://stats.grafana.org/grafana-usage-report", "application/json", data)
  348. }