scenarios.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. package testdata
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math"
  6. "math/rand"
  7. "strconv"
  8. "strings"
  9. "time"
  10. "github.com/grafana/grafana/pkg/components/null"
  11. "github.com/grafana/grafana/pkg/infra/log"
  12. "github.com/grafana/grafana/pkg/tsdb"
  13. )
  14. type ScenarioHandler func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult
  15. type Scenario struct {
  16. Id string `json:"id"`
  17. Name string `json:"name"`
  18. StringInput string `json:"stringOption"`
  19. Description string `json:"description"`
  20. Handler ScenarioHandler `json:"-"`
  21. }
  22. var ScenarioRegistry map[string]*Scenario
  23. func init() {
  24. ScenarioRegistry = make(map[string]*Scenario)
  25. logger := log.New("tsdb.testdata")
  26. logger.Debug("Initializing TestData Scenario")
  27. registerScenario(&Scenario{
  28. Id: "exponential_heatmap_bucket_data",
  29. Name: "Exponential heatmap bucket data",
  30. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  31. to := context.TimeRange.GetToAsMsEpoch()
  32. var series []*tsdb.TimeSeries
  33. start := 1
  34. factor := 2
  35. for i := 0; i < 10; i++ {
  36. timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
  37. serie := &tsdb.TimeSeries{Name: strconv.Itoa(start)}
  38. start *= factor
  39. points := make(tsdb.TimeSeriesPoints, 0)
  40. for j := int64(0); j < 100 && timeWalkerMs < to; j++ {
  41. v := float64(rand.Int63n(100))
  42. points = append(points, tsdb.NewTimePoint(null.FloatFrom(v), float64(timeWalkerMs)))
  43. timeWalkerMs += query.IntervalMs * 50
  44. }
  45. serie.Points = points
  46. series = append(series, serie)
  47. }
  48. queryRes := tsdb.NewQueryResult()
  49. queryRes.Series = append(queryRes.Series, series...)
  50. return queryRes
  51. },
  52. })
  53. registerScenario(&Scenario{
  54. Id: "linear_heatmap_bucket_data",
  55. Name: "Linear heatmap bucket data",
  56. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  57. to := context.TimeRange.GetToAsMsEpoch()
  58. var series []*tsdb.TimeSeries
  59. for i := 0; i < 10; i++ {
  60. timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
  61. serie := &tsdb.TimeSeries{Name: strconv.Itoa(i * 10)}
  62. points := make(tsdb.TimeSeriesPoints, 0)
  63. for j := int64(0); j < 100 && timeWalkerMs < to; j++ {
  64. v := float64(rand.Int63n(100))
  65. points = append(points, tsdb.NewTimePoint(null.FloatFrom(v), float64(timeWalkerMs)))
  66. timeWalkerMs += query.IntervalMs * 50
  67. }
  68. serie.Points = points
  69. series = append(series, serie)
  70. }
  71. queryRes := tsdb.NewQueryResult()
  72. queryRes.Series = append(queryRes.Series, series...)
  73. return queryRes
  74. },
  75. })
  76. registerScenario(&Scenario{
  77. Id: "random_walk",
  78. Name: "Random Walk",
  79. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  80. return getRandomWalk(query, context)
  81. },
  82. })
  83. registerScenario(&Scenario{
  84. Id: "random_walk_table",
  85. Name: "Random Walk Table",
  86. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  87. return getRandomWalkTable(query, context)
  88. },
  89. })
  90. registerScenario(&Scenario{
  91. Id: "slow_query",
  92. Name: "Slow Query",
  93. StringInput: "5s",
  94. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  95. stringInput := query.Model.Get("stringInput").MustString()
  96. parsedInterval, _ := time.ParseDuration(stringInput)
  97. time.Sleep(parsedInterval)
  98. return getRandomWalk(query, context)
  99. },
  100. })
  101. registerScenario(&Scenario{
  102. Id: "no_data_points",
  103. Name: "No Data Points",
  104. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  105. return tsdb.NewQueryResult()
  106. },
  107. })
  108. registerScenario(&Scenario{
  109. Id: "datapoints_outside_range",
  110. Name: "Datapoints Outside Range",
  111. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  112. queryRes := tsdb.NewQueryResult()
  113. series := newSeriesForQuery(query)
  114. outsideTime := context.TimeRange.MustGetFrom().Add(-1*time.Hour).Unix() * 1000
  115. series.Points = append(series.Points, tsdb.NewTimePoint(null.FloatFrom(10), float64(outsideTime)))
  116. queryRes.Series = append(queryRes.Series, series)
  117. return queryRes
  118. },
  119. })
  120. registerScenario(&Scenario{
  121. Id: "manual_entry",
  122. Name: "Manual Entry",
  123. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  124. queryRes := tsdb.NewQueryResult()
  125. points := query.Model.Get("points").MustArray()
  126. series := newSeriesForQuery(query)
  127. startTime := context.TimeRange.GetFromAsMsEpoch()
  128. endTime := context.TimeRange.GetToAsMsEpoch()
  129. for _, val := range points {
  130. pointValues := val.([]interface{})
  131. var value null.Float
  132. var time int64
  133. if valueFloat, err := strconv.ParseFloat(string(pointValues[0].(json.Number)), 64); err == nil {
  134. value = null.FloatFrom(valueFloat)
  135. }
  136. if timeInt, err := strconv.ParseInt(string(pointValues[1].(json.Number)), 10, 64); err != nil {
  137. continue
  138. } else {
  139. time = timeInt
  140. }
  141. if time >= startTime && time <= endTime {
  142. series.Points = append(series.Points, tsdb.NewTimePoint(value, float64(time)))
  143. }
  144. }
  145. queryRes.Series = append(queryRes.Series, series)
  146. return queryRes
  147. },
  148. })
  149. registerScenario(&Scenario{
  150. Id: "csv_metric_values",
  151. Name: "CSV Metric Values",
  152. StringInput: "1,20,90,30,5,0",
  153. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  154. queryRes := tsdb.NewQueryResult()
  155. stringInput := query.Model.Get("stringInput").MustString()
  156. stringInput = strings.Replace(stringInput, " ", "", -1)
  157. values := []null.Float{}
  158. for _, strVal := range strings.Split(stringInput, ",") {
  159. if strVal == "null" {
  160. values = append(values, null.FloatFromPtr(nil))
  161. }
  162. if val, err := strconv.ParseFloat(strVal, 64); err == nil {
  163. values = append(values, null.FloatFrom(val))
  164. }
  165. }
  166. if len(values) == 0 {
  167. return queryRes
  168. }
  169. series := newSeriesForQuery(query)
  170. startTime := context.TimeRange.GetFromAsMsEpoch()
  171. endTime := context.TimeRange.GetToAsMsEpoch()
  172. step := (endTime - startTime) / int64(len(values)-1)
  173. for _, val := range values {
  174. series.Points = append(series.Points, tsdb.TimePoint{val, null.FloatFrom(float64(startTime))})
  175. startTime += step
  176. }
  177. queryRes.Series = append(queryRes.Series, series)
  178. return queryRes
  179. },
  180. })
  181. registerScenario(&Scenario{
  182. Id: "streaming_client",
  183. Name: "Streaming Client",
  184. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  185. // Real work is in javascript client
  186. return tsdb.NewQueryResult()
  187. },
  188. })
  189. registerScenario(&Scenario{
  190. Id: "table_static",
  191. Name: "Table Static",
  192. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  193. timeWalkerMs := context.TimeRange.GetFromAsMsEpoch()
  194. to := context.TimeRange.GetToAsMsEpoch()
  195. table := tsdb.Table{
  196. Columns: []tsdb.TableColumn{
  197. {Text: "Time"},
  198. {Text: "Message"},
  199. {Text: "Description"},
  200. {Text: "Value"},
  201. },
  202. Rows: []tsdb.RowValues{},
  203. }
  204. for i := int64(0); i < 10 && timeWalkerMs < to; i++ {
  205. table.Rows = append(table.Rows, tsdb.RowValues{float64(timeWalkerMs), "This is a message", "Description", 23.1})
  206. timeWalkerMs += query.IntervalMs
  207. }
  208. queryRes := tsdb.NewQueryResult()
  209. queryRes.Tables = append(queryRes.Tables, &table)
  210. return queryRes
  211. },
  212. })
  213. registerScenario(&Scenario{
  214. Id: "logs",
  215. Name: "Logs",
  216. Handler: func(query *tsdb.Query, context *tsdb.TsdbQuery) *tsdb.QueryResult {
  217. from := context.TimeRange.GetFromAsMsEpoch()
  218. to := context.TimeRange.GetToAsMsEpoch()
  219. lines := query.Model.Get("lines").MustInt64(10)
  220. includeLevelColumn := query.Model.Get("levelColumn").MustBool(false)
  221. logLevelGenerator := newRandomStringProvider([]string{
  222. "emerg",
  223. "alert",
  224. "crit",
  225. "critical",
  226. "warn",
  227. "warning",
  228. "err",
  229. "eror",
  230. "error",
  231. "info",
  232. "notice",
  233. "dbug",
  234. "debug",
  235. "trace",
  236. "",
  237. })
  238. containerIDGenerator := newRandomStringProvider([]string{
  239. "f36a9eaa6d34310686f2b851655212023a216de955cbcc764210cefa71179b1a",
  240. "5a354a630364f3742c602f315132e16def594fe68b1e4a195b2fce628e24c97a",
  241. })
  242. hostnameGenerator := newRandomStringProvider([]string{
  243. "srv-001",
  244. "srv-002",
  245. })
  246. table := tsdb.Table{
  247. Columns: []tsdb.TableColumn{
  248. {Text: "time"},
  249. {Text: "message"},
  250. {Text: "container_id"},
  251. {Text: "hostname"},
  252. },
  253. Rows: []tsdb.RowValues{},
  254. }
  255. if includeLevelColumn {
  256. table.Columns = append(table.Columns, tsdb.TableColumn{Text: "level"})
  257. }
  258. for i := int64(0); i < lines && to > from; i++ {
  259. row := tsdb.RowValues{float64(to)}
  260. logLevel := logLevelGenerator.Next()
  261. timeFormatted := time.Unix(to/1000, 0).Format(time.RFC3339)
  262. lvlString := ""
  263. if !includeLevelColumn {
  264. lvlString = fmt.Sprintf("lvl=%s ", logLevel)
  265. }
  266. row = append(row, fmt.Sprintf("t=%s %smsg=\"Request Completed\" logger=context userId=1 orgId=1 uname=admin method=GET path=/api/datasources/proxy/152/api/prom/label status=502 remote_addr=[::1] time_ms=1 size=0 referer=\"http://localhost:3000/explore?left=%%5B%%22now-6h%%22,%%22now%%22,%%22Prometheus%%202.x%%22,%%7B%%7D,%%7B%%22ui%%22:%%5Btrue,true,true,%%22none%%22%%5D%%7D%%5D\"", timeFormatted, lvlString))
  267. row = append(row, containerIDGenerator.Next())
  268. row = append(row, hostnameGenerator.Next())
  269. if includeLevelColumn {
  270. row = append(row, logLevel)
  271. }
  272. table.Rows = append(table.Rows, row)
  273. to -= query.IntervalMs
  274. }
  275. queryRes := tsdb.NewQueryResult()
  276. queryRes.Tables = append(queryRes.Tables, &table)
  277. return queryRes
  278. },
  279. })
  280. }
  281. func getRandomWalk(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResult {
  282. timeWalkerMs := tsdbQuery.TimeRange.GetFromAsMsEpoch()
  283. to := tsdbQuery.TimeRange.GetToAsMsEpoch()
  284. series := newSeriesForQuery(query)
  285. points := make(tsdb.TimeSeriesPoints, 0)
  286. walker := query.Model.Get("startValue").MustFloat64(rand.Float64() * 100)
  287. for i := int64(0); i < 10000 && timeWalkerMs < to; i++ {
  288. points = append(points, tsdb.NewTimePoint(null.FloatFrom(walker), float64(timeWalkerMs)))
  289. walker += rand.Float64() - 0.5
  290. timeWalkerMs += query.IntervalMs
  291. }
  292. series.Points = points
  293. queryRes := tsdb.NewQueryResult()
  294. queryRes.Series = append(queryRes.Series, series)
  295. return queryRes
  296. }
  297. func getRandomWalkTable(query *tsdb.Query, tsdbQuery *tsdb.TsdbQuery) *tsdb.QueryResult {
  298. timeWalkerMs := tsdbQuery.TimeRange.GetFromAsMsEpoch()
  299. to := tsdbQuery.TimeRange.GetToAsMsEpoch()
  300. table := tsdb.Table{
  301. Columns: []tsdb.TableColumn{
  302. {Text: "Time"},
  303. {Text: "Value"},
  304. {Text: "Min"},
  305. {Text: "Max"},
  306. {Text: "Info"},
  307. },
  308. Rows: []tsdb.RowValues{},
  309. }
  310. withNil := query.Model.Get("withNil").MustBool(false)
  311. walker := query.Model.Get("startValue").MustFloat64(rand.Float64() * 100)
  312. spread := 2.5
  313. var info strings.Builder
  314. for i := int64(0); i < query.MaxDataPoints && timeWalkerMs < to; i++ {
  315. delta := rand.Float64() - 0.5
  316. walker += delta
  317. info.Reset()
  318. if delta > 0 {
  319. info.WriteString("up")
  320. } else {
  321. info.WriteString("down")
  322. }
  323. if math.Abs(delta) > .4 {
  324. info.WriteString(" fast")
  325. }
  326. row := tsdb.RowValues{
  327. float64(timeWalkerMs),
  328. walker,
  329. walker - ((rand.Float64() * spread) + 0.01), // Min
  330. walker + ((rand.Float64() * spread) + 0.01), // Max
  331. info.String(),
  332. }
  333. // Add some random null values
  334. if withNil && rand.Float64() > 0.8 {
  335. for i := 1; i < 4; i++ {
  336. if rand.Float64() > .2 {
  337. row[i] = nil
  338. }
  339. }
  340. }
  341. table.Rows = append(table.Rows, row)
  342. timeWalkerMs += query.IntervalMs
  343. }
  344. queryRes := tsdb.NewQueryResult()
  345. queryRes.Tables = append(queryRes.Tables, &table)
  346. return queryRes
  347. }
  348. func registerScenario(scenario *Scenario) {
  349. ScenarioRegistry[scenario.Id] = scenario
  350. }
  351. func newSeriesForQuery(query *tsdb.Query) *tsdb.TimeSeries {
  352. alias := query.Model.Get("alias").MustString("")
  353. if alias == "" {
  354. alias = query.RefId + "-series"
  355. }
  356. return &tsdb.TimeSeries{Name: alias}
  357. }