prometheusbackend_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. /*
  2. Copyright 2019 The Vitess Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package prometheusbackend
  14. import (
  15. "fmt"
  16. "net/http"
  17. "net/http/httptest"
  18. "os"
  19. "strings"
  20. "testing"
  21. "time"
  22. "git.nspix.com/golang/micro/stats"
  23. "github.com/prometheus/client_golang/prometheus/promhttp"
  24. )
  25. const namespace = "namespace"
  26. func TestPrometheusCounter(t *testing.T) {
  27. name := "blah"
  28. c := stats.NewCounter(name, "blah")
  29. c.Add(1)
  30. checkHandlerForMetrics(t, name, 1)
  31. //TODO: ban this? And for other counter types too?
  32. // c.Add(-1)
  33. c.Reset()
  34. checkHandlerForMetrics(t, name, 0)
  35. }
  36. func TestPrometheusGauge(t *testing.T) {
  37. name := "blah_gauge"
  38. c := stats.NewGauge(name, "help")
  39. c.Add(1)
  40. checkHandlerForMetrics(t, name, 1)
  41. c.Add(-1)
  42. checkHandlerForMetrics(t, name, 0)
  43. c.Set(-5)
  44. checkHandlerForMetrics(t, name, -5)
  45. c.Reset()
  46. checkHandlerForMetrics(t, name, 0)
  47. }
  48. func TestPrometheusCounterFunc(t *testing.T) {
  49. name := "blah_counterfunc"
  50. stats.NewCounterFunc(name, "help", func() int64 {
  51. return 2
  52. })
  53. checkHandlerForMetrics(t, name, 2)
  54. }
  55. func TestPrometheusGaugeFunc(t *testing.T) {
  56. name := "blah_gaugefunc"
  57. stats.NewGaugeFunc(name, "help", func() int64 {
  58. return -3
  59. })
  60. checkHandlerForMetrics(t, name, -3)
  61. }
  62. func TestPrometheusFloatFunc(t *testing.T) {
  63. name := "blah_floatfunc"
  64. stats.Publish(name, stats.FloatFunc(func() float64 { return -4 }))
  65. checkHandlerForMetrics(t, name, -4)
  66. }
  67. func TestPrometheusCounterDuration(t *testing.T) {
  68. name := "blah_counterduration"
  69. d := stats.NewCounterDuration(name, "help")
  70. d.Add(1 * time.Second)
  71. checkHandlerForMetrics(t, name, 1)
  72. }
  73. func TestPrometheusCounterDurationFunc(t *testing.T) {
  74. name := "blah_counterdurationfunc"
  75. stats.NewCounterDurationFunc(name, "help", func() time.Duration { return 1 * time.Second })
  76. checkHandlerForMetrics(t, name, 1)
  77. }
  78. func TestPrometheusGaugeDuration(t *testing.T) {
  79. name := "blah_gaugeduration"
  80. d := stats.NewGaugeDuration(name, "help")
  81. d.Set(1 * time.Second)
  82. checkHandlerForMetrics(t, name, 1)
  83. }
  84. func TestPrometheusGaugeDurationFunc(t *testing.T) {
  85. name := "blah_gaugedurationfunc"
  86. stats.NewGaugeDurationFunc(name, "help", func() time.Duration { return 1 * time.Second })
  87. checkHandlerForMetrics(t, name, 1)
  88. }
  89. func checkHandlerForMetrics(t *testing.T, metric string, value int) {
  90. response := testMetricsHandler(t)
  91. expected := fmt.Sprintf("%s_%s %d", namespace, metric, value)
  92. if !strings.Contains(response.Body.String(), expected) {
  93. t.Fatalf("Expected %s got %s", expected, response.Body.String())
  94. }
  95. }
  96. func TestPrometheusCountersWithSingleLabel(t *testing.T) {
  97. name := "blah_counterswithsinglelabel"
  98. c := stats.NewCountersWithSingleLabel(name, "help", "label", "tag1", "tag2")
  99. c.Add("tag1", 1)
  100. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
  101. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
  102. c.Add("tag2", 41)
  103. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
  104. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 41)
  105. c.Reset("tag2")
  106. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
  107. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
  108. }
  109. func TestPrometheusGaugesWithSingleLabel(t *testing.T) {
  110. name := "blah_gaugeswithsinglelabel"
  111. c := stats.NewGaugesWithSingleLabel(name, "help", "label", "tag1", "tag2")
  112. c.Add("tag1", 1)
  113. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", 1)
  114. c.Add("tag2", 1)
  115. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 1)
  116. c.Set("tag1", -1)
  117. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", -1)
  118. c.Reset("tag2")
  119. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag1", -1)
  120. checkHandlerForMetricWithSingleLabel(t, name, "label", "tag2", 0)
  121. }
  122. func checkHandlerForMetricWithSingleLabel(t *testing.T, metric, label, tag string, value int) {
  123. response := testMetricsHandler(t)
  124. expected := fmt.Sprintf("%s_%s{%s=\"%s\"} %d", namespace, metric, label, tag, value)
  125. if !strings.Contains(response.Body.String(), expected) {
  126. t.Fatalf("Expected %s got %s", expected, response.Body.String())
  127. }
  128. }
  129. func TestPrometheusCountersWithMultiLabels(t *testing.T) {
  130. name := "blah_counterswithmultilabels"
  131. labels := []string{"label1", "label2"}
  132. labelValues := []string{"foo", "bar"}
  133. c := stats.NewCountersWithMultiLabels(name, "help", labels)
  134. c.Add(labelValues, 1)
  135. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
  136. labelValues2 := []string{"baz", "bazbar"}
  137. c.Add(labelValues2, 1)
  138. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
  139. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
  140. c.Reset(labelValues)
  141. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 0)
  142. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
  143. }
  144. func TestPrometheusGaugesWithMultiLabels(t *testing.T) {
  145. name := "blah_gaugeswithmultilabels"
  146. labels := []string{"label1", "label2"}
  147. labelValues := []string{"foo", "bar"}
  148. c := stats.NewGaugesWithMultiLabels(name, "help", labels)
  149. c.Add(labelValues, 1)
  150. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 1)
  151. c.Set(labelValues, -1)
  152. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, -1)
  153. labelValues2 := []string{"baz", "bazbar"}
  154. c.Add(labelValues2, 1)
  155. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, -1)
  156. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
  157. c.Reset(labelValues)
  158. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues, 0)
  159. checkHandlerForMetricWithMultiLabels(t, name, labels, labelValues2, 1)
  160. }
  161. func TestPrometheusCountersWithMultiLabels_AddPanic(t *testing.T) {
  162. defer func() {
  163. if r := recover(); r == nil {
  164. t.Errorf("The code did not panic when adding to inequal label lengths")
  165. }
  166. }()
  167. name := "blah_counterswithmultilabels_inequallength"
  168. c := stats.NewCountersWithMultiLabels(name, "help", []string{"label1", "label2"})
  169. c.Add([]string{"label1"}, 1)
  170. }
  171. func TestPrometheusCountersFuncWithMultiLabels(t *testing.T) {
  172. name := "blah_countersfuncwithmultilabels"
  173. labels := []string{"label1", "label2"}
  174. stats.NewCountersFuncWithMultiLabels(name, "help", labels, func() map[string]int64 {
  175. m := make(map[string]int64)
  176. m["foo.bar"] = 1
  177. m["bar.baz"] = 1
  178. return m
  179. })
  180. checkHandlerForMetricWithMultiLabels(t, name, labels, []string{"foo", "bar"}, 1)
  181. checkHandlerForMetricWithMultiLabels(t, name, labels, []string{"bar", "baz"}, 1)
  182. }
  183. func checkHandlerForMetricWithMultiLabels(t *testing.T, metric string, labels []string, labelValues []string, value int64) {
  184. response := testMetricsHandler(t)
  185. expected := fmt.Sprintf("%s_%s{%s=\"%s\",%s=\"%s\"} %d", namespace, metric, labels[0], labelValues[0], labels[1], labelValues[1], value)
  186. if !strings.Contains(response.Body.String(), expected) {
  187. t.Fatalf("Expected %s got %s", expected, response.Body.String())
  188. }
  189. }
  190. func TestPrometheusTimings(t *testing.T) {
  191. name := "blah_timings"
  192. cats := []string{"cat1", "cat2"}
  193. timing := stats.NewTimings(name, "help", "category", cats...)
  194. timing.Add("cat1", time.Duration(30*time.Millisecond))
  195. timing.Add("cat1", time.Duration(200*time.Millisecond))
  196. timing.Add("cat1", time.Duration(1*time.Second))
  197. response := testMetricsHandler(t)
  198. var s []string
  199. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.0005\"} %d", namespace, name, cats[0], 0))
  200. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.001\"} %d", namespace, name, cats[0], 0))
  201. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.005\"} %d", namespace, name, cats[0], 0))
  202. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.01\"} %d", namespace, name, cats[0], 0))
  203. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.05\"} %d", namespace, name, cats[0], 1))
  204. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.1\"} %d", namespace, name, cats[0], 1))
  205. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"0.5\"} %d", namespace, name, cats[0], 2))
  206. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"1\"} %d", namespace, name, cats[0], 3))
  207. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"5\"} %d", namespace, name, cats[0], 3))
  208. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"10\"} %d", namespace, name, cats[0], 3))
  209. s = append(s, fmt.Sprintf("%s_%s_bucket{category=\"%s\",le=\"+Inf\"} %d", namespace, name, cats[0], 3))
  210. s = append(s, fmt.Sprintf("%s_%s_sum{category=\"%s\"} %s", namespace, name, cats[0], "1.23"))
  211. s = append(s, fmt.Sprintf("%s_%s_count{category=\"%s\"} %d", namespace, name, cats[0], 3))
  212. for _, line := range s {
  213. if !strings.Contains(response.Body.String(), line) {
  214. t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
  215. }
  216. }
  217. }
  218. func TestPrometheusMultiTimings(t *testing.T) {
  219. name := "blah_multitimings"
  220. cats := []string{"cat1", "cat2"}
  221. catLabels := []string{"foo", "bar"}
  222. timing := stats.NewMultiTimings(name, "help", cats)
  223. timing.Add(catLabels, time.Duration(30*time.Millisecond))
  224. timing.Add(catLabels, time.Duration(200*time.Millisecond))
  225. timing.Add(catLabels, time.Duration(1*time.Second))
  226. response := testMetricsHandler(t)
  227. var s []string
  228. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.0005\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
  229. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.001\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
  230. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.005\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
  231. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.01\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 0))
  232. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.05\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 1))
  233. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.1\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 1))
  234. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"0.5\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 2))
  235. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"1\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
  236. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"5\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
  237. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"10\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
  238. s = append(s, fmt.Sprintf("%s_%s_bucket{%s=\"%s\",%s=\"%s\",le=\"+Inf\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
  239. s = append(s, fmt.Sprintf("%s_%s_sum{%s=\"%s\",%s=\"%s\"} %s", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], "1.23"))
  240. s = append(s, fmt.Sprintf("%s_%s_count{%s=\"%s\",%s=\"%s\"} %d", namespace, name, cats[0], catLabels[0], cats[1], catLabels[1], 3))
  241. for _, line := range s {
  242. if !strings.Contains(response.Body.String(), line) {
  243. t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
  244. }
  245. }
  246. }
  247. func TestPrometheusMultiTimings_PanicWrongLength(t *testing.T) {
  248. defer func() {
  249. if r := recover(); r == nil {
  250. t.Errorf("The code did not panic when adding to inequal label lengths")
  251. }
  252. }()
  253. c := stats.NewMultiTimings("name", "help", []string{"label1", "label2"})
  254. c.Add([]string{"label1"}, time.Duration(100000000))
  255. }
  256. func TestPrometheusHistogram(t *testing.T) {
  257. name := "blah_hist"
  258. hist := stats.NewHistogram(name, "help", []int64{1, 5, 10})
  259. hist.Add(2)
  260. hist.Add(3)
  261. hist.Add(6)
  262. response := testMetricsHandler(t)
  263. var s []string
  264. s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"1\"} %d", namespace, name, 0))
  265. s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"5\"} %d", namespace, name, 2))
  266. s = append(s, fmt.Sprintf("%s_%s_bucket{le=\"10\"} %d", namespace, name, 3))
  267. s = append(s, fmt.Sprintf("%s_%s_sum %d", namespace, name, 1))
  268. s = append(s, fmt.Sprintf("%s_%s_count %d", namespace, name, 3))
  269. for _, line := range s {
  270. if !strings.Contains(response.Body.String(), line) {
  271. t.Fatalf("Expected result to contain %s, got %s", line, response.Body.String())
  272. }
  273. }
  274. }
  275. func testMetricsHandler(t *testing.T) *httptest.ResponseRecorder {
  276. req, _ := http.NewRequest("GET", "/metrics", nil)
  277. response := httptest.NewRecorder()
  278. promhttp.Handler().ServeHTTP(response, req)
  279. return response
  280. }
  281. func TestMain(m *testing.M) {
  282. Init(namespace)
  283. os.Exit(m.Run())
  284. }