No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.
 
 
 

594 líneas
15 KiB

  1. // Copyright 2017, OpenCensus Authors
  2. //
  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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package prometheus
  15. import (
  16. "context"
  17. "fmt"
  18. "io/ioutil"
  19. "net/http"
  20. "net/http/httptest"
  21. "strings"
  22. "sync"
  23. "testing"
  24. "time"
  25. "go.opencensus.io/stats"
  26. "go.opencensus.io/stats/view"
  27. "go.opencensus.io/tag"
  28. "github.com/prometheus/client_golang/prometheus"
  29. )
  30. func newView(measureName string, agg *view.Aggregation) *view.View {
  31. m := stats.Int64(measureName, "bytes", stats.UnitBytes)
  32. return &view.View{
  33. Name: "foo",
  34. Description: "bar",
  35. Measure: m,
  36. Aggregation: agg,
  37. }
  38. }
  39. func TestOnlyCumulativeWindowSupported(t *testing.T) {
  40. // See Issue https://github.com/census-instrumentation/opencensus-go/issues/214.
  41. count1 := &view.CountData{Value: 1}
  42. lastValue1 := &view.LastValueData{Value: 56.7}
  43. tests := []struct {
  44. vds *view.Data
  45. want int
  46. }{
  47. 0: {
  48. vds: &view.Data{
  49. View: newView("TestOnlyCumulativeWindowSupported/m1", view.Count()),
  50. },
  51. want: 0, // no rows present
  52. },
  53. 1: {
  54. vds: &view.Data{
  55. View: newView("TestOnlyCumulativeWindowSupported/m2", view.Count()),
  56. Rows: []*view.Row{
  57. {Data: count1},
  58. },
  59. },
  60. want: 1,
  61. },
  62. 2: {
  63. vds: &view.Data{
  64. View: newView("TestOnlyCumulativeWindowSupported/m3", view.LastValue()),
  65. Rows: []*view.Row{
  66. {Data: lastValue1},
  67. },
  68. },
  69. want: 1,
  70. },
  71. }
  72. for i, tt := range tests {
  73. reg := prometheus.NewRegistry()
  74. collector := newCollector(Options{}, reg)
  75. collector.addViewData(tt.vds)
  76. mm, err := reg.Gather()
  77. if err != nil {
  78. t.Errorf("#%d: Gather err: %v", i, err)
  79. }
  80. reg.Unregister(collector)
  81. if got, want := len(mm), tt.want; got != want {
  82. t.Errorf("#%d: got nil %v want nil %v", i, got, want)
  83. }
  84. }
  85. }
  86. func TestCollectNonRacy(t *testing.T) {
  87. // Despite enforcing the singleton, for this case we
  88. // need an exporter hence won't be using NewExporter.
  89. exp, err := NewExporter(Options{})
  90. if err != nil {
  91. t.Fatalf("NewExporter: %v", err)
  92. }
  93. collector := exp.c
  94. // Synchronize and make sure every goroutine has terminated before we exit
  95. var waiter sync.WaitGroup
  96. waiter.Add(3)
  97. defer waiter.Wait()
  98. doneCh := make(chan bool)
  99. // 1. Viewdata write routine at 700ns
  100. go func() {
  101. defer waiter.Done()
  102. tick := time.NewTicker(700 * time.Nanosecond)
  103. defer tick.Stop()
  104. defer func() {
  105. close(doneCh)
  106. }()
  107. for i := 0; i < 1e3; i++ {
  108. count1 := &view.CountData{Value: 1}
  109. vds := []*view.Data{
  110. {View: newView(fmt.Sprintf("TestCollectNonRacy/m2-%d", i), view.Count()), Rows: []*view.Row{{Data: count1}}},
  111. }
  112. for _, v := range vds {
  113. exp.ExportView(v)
  114. }
  115. <-tick.C
  116. }
  117. }()
  118. inMetricsChan := make(chan prometheus.Metric, 1000)
  119. // 2. Simulating the Prometheus metrics consumption routine running at 900ns
  120. go func() {
  121. defer waiter.Done()
  122. tick := time.NewTicker(900 * time.Nanosecond)
  123. defer tick.Stop()
  124. for {
  125. select {
  126. case <-doneCh:
  127. return
  128. case <-inMetricsChan:
  129. }
  130. }
  131. }()
  132. // 3. Collect/Read routine at 800ns
  133. go func() {
  134. defer waiter.Done()
  135. tick := time.NewTicker(800 * time.Nanosecond)
  136. defer tick.Stop()
  137. for {
  138. select {
  139. case <-doneCh:
  140. return
  141. case <-tick.C:
  142. // Perform some collection here
  143. collector.Collect(inMetricsChan)
  144. }
  145. }
  146. }()
  147. }
  148. type mSlice []*stats.Int64Measure
  149. func (measures *mSlice) createAndAppend(name, desc, unit string) {
  150. m := stats.Int64(name, desc, unit)
  151. *measures = append(*measures, m)
  152. }
  153. type vCreator []*view.View
  154. func (vc *vCreator) createAndAppend(name, description string, keys []tag.Key, measure stats.Measure, agg *view.Aggregation) {
  155. v := &view.View{
  156. Name: name,
  157. Description: description,
  158. TagKeys: keys,
  159. Measure: measure,
  160. Aggregation: agg,
  161. }
  162. *vc = append(*vc, v)
  163. }
  164. func TestMetricsEndpointOutput(t *testing.T) {
  165. exporter, err := NewExporter(Options{})
  166. if err != nil {
  167. t.Fatalf("failed to create prometheus exporter: %v", err)
  168. }
  169. view.RegisterExporter(exporter)
  170. names := []string{"foo", "bar", "baz"}
  171. var measures mSlice
  172. for _, name := range names {
  173. measures.createAndAppend("tests/"+name, name, "")
  174. }
  175. var vc vCreator
  176. for _, m := range measures {
  177. vc.createAndAppend(m.Name(), m.Description(), nil, m, view.Count())
  178. }
  179. if err := view.Register(vc...); err != nil {
  180. t.Fatalf("failed to create views: %v", err)
  181. }
  182. defer view.Unregister(vc...)
  183. view.SetReportingPeriod(time.Millisecond)
  184. for _, m := range measures {
  185. stats.Record(context.Background(), m.M(1))
  186. }
  187. srv := httptest.NewServer(exporter)
  188. defer srv.Close()
  189. var i int
  190. var output string
  191. for {
  192. time.Sleep(10 * time.Millisecond)
  193. if i == 1000 {
  194. t.Fatal("no output at /metrics (10s wait)")
  195. }
  196. i++
  197. resp, err := http.Get(srv.URL)
  198. if err != nil {
  199. t.Fatalf("failed to get /metrics: %v", err)
  200. }
  201. body, err := ioutil.ReadAll(resp.Body)
  202. if err != nil {
  203. t.Fatalf("failed to read body: %v", err)
  204. }
  205. resp.Body.Close()
  206. output = string(body)
  207. if output != "" {
  208. break
  209. }
  210. }
  211. if strings.Contains(output, "collected before with the same name and label values") {
  212. t.Fatal("metric name and labels being duplicated but must be unique")
  213. }
  214. if strings.Contains(output, "error(s) occurred") {
  215. t.Fatal("error reported by prometheus registry")
  216. }
  217. for _, name := range names {
  218. if !strings.Contains(output, "tests_"+name+" 1") {
  219. t.Fatalf("measurement missing in output: %v", name)
  220. }
  221. }
  222. }
  223. func TestCumulativenessFromHistograms(t *testing.T) {
  224. exporter, err := NewExporter(Options{})
  225. if err != nil {
  226. t.Fatalf("failed to create prometheus exporter: %v", err)
  227. }
  228. view.RegisterExporter(exporter)
  229. reportPeriod := time.Millisecond
  230. view.SetReportingPeriod(reportPeriod)
  231. m := stats.Float64("tests/bills", "payments by denomination", stats.UnitDimensionless)
  232. v := &view.View{
  233. Name: "cash/register",
  234. Description: "this is a test",
  235. Measure: m,
  236. // Intentionally used repeated elements in the ascending distribution.
  237. // to ensure duplicate distribution items are handles.
  238. Aggregation: view.Distribution(1, 5, 5, 5, 5, 10, 20, 50, 100, 250),
  239. }
  240. if err := view.Register(v); err != nil {
  241. t.Fatalf("Register error: %v", err)
  242. }
  243. defer view.Unregister(v)
  244. // Give the reporter ample time to process registration
  245. <-time.After(10 * reportPeriod)
  246. values := []float64{0.25, 245.67, 12, 1.45, 199.9, 7.69, 187.12}
  247. // We want the results that look like this:
  248. // 1: [0.25] | 1 + prev(i) = 1 + 0 = 1
  249. // 5: [1.45] | 1 + prev(i) = 1 + 1 = 2
  250. // 10: [7.69] | 1 + prev(i) = 1 + 2 = 3
  251. // 20: [12] | 1 + prev(i) = 1 + 3 = 4
  252. // 50: [] | 0 + prev(i) = 0 + 4 = 4
  253. // 100: [] | 0 + prev(i) = 0 + 4 = 4
  254. // 250: [187.12, 199.9, 245.67] | 3 + prev(i) = 3 + 4 = 7
  255. wantLines := []string{
  256. `cash_register_bucket{le="1"} 1`,
  257. `cash_register_bucket{le="5"} 2`,
  258. `cash_register_bucket{le="10"} 3`,
  259. `cash_register_bucket{le="20"} 4`,
  260. `cash_register_bucket{le="50"} 4`,
  261. `cash_register_bucket{le="100"} 4`,
  262. `cash_register_bucket{le="250"} 7`,
  263. `cash_register_bucket{le="+Inf"} 7`,
  264. `cash_register_sum 654.0799999999999`, // Summation of the input values
  265. `cash_register_count 7`,
  266. }
  267. ctx := context.Background()
  268. ms := make([]stats.Measurement, 0, len(values))
  269. for _, value := range values {
  270. mx := m.M(value)
  271. ms = append(ms, mx)
  272. }
  273. stats.Record(ctx, ms...)
  274. // Give the recorder ample time to process recording
  275. <-time.After(10 * reportPeriod)
  276. cst := httptest.NewServer(exporter)
  277. defer cst.Close()
  278. res, err := http.Get(cst.URL)
  279. if err != nil {
  280. t.Fatalf("http.Get error: %v", err)
  281. }
  282. blob, err := ioutil.ReadAll(res.Body)
  283. if err != nil {
  284. t.Fatalf("Read body error: %v", err)
  285. }
  286. str := strings.Trim(string(blob), "\n")
  287. lines := strings.Split(str, "\n")
  288. nonComments := make([]string, 0, len(lines))
  289. for _, line := range lines {
  290. if !strings.Contains(line, "#") {
  291. nonComments = append(nonComments, line)
  292. }
  293. }
  294. got := strings.Join(nonComments, "\n")
  295. want := strings.Join(wantLines, "\n")
  296. if got != want {
  297. t.Fatalf("\ngot:\n%s\n\nwant:\n%s\n", got, want)
  298. }
  299. }
  300. func TestHistogramUnorderedBucketBounds(t *testing.T) {
  301. exporter, err := NewExporter(Options{})
  302. if err != nil {
  303. t.Fatalf("failed to create prometheus exporter: %v", err)
  304. }
  305. view.RegisterExporter(exporter)
  306. reportPeriod := time.Millisecond
  307. view.SetReportingPeriod(reportPeriod)
  308. m := stats.Float64("tests/bills", "payments by denomination", stats.UnitDimensionless)
  309. v := &view.View{
  310. Name: "cash/register",
  311. Description: "this is a test",
  312. Measure: m,
  313. // Intentionally used unordered and duplicated elements in the distribution
  314. // to ensure unordered bucket bounds are handled.
  315. Aggregation: view.Distribution(10, 5, 1, 1, 50, 5, 20, 100, 250),
  316. }
  317. if err := view.Register(v); err != nil {
  318. t.Fatalf("Register error: %v", err)
  319. }
  320. defer view.Unregister(v)
  321. // Give the reporter ample time to process registration
  322. <-time.After(10 * reportPeriod)
  323. values := []float64{0.25, 245.67, 12, 1.45, 199.9, 7.69, 187.12}
  324. // We want the results that look like this:
  325. // 1: [0.25] | 1 + prev(i) = 1 + 0 = 1
  326. // 5: [1.45] | 1 + prev(i) = 1 + 1 = 2
  327. // 10: [7.69] | 1 + prev(i) = 1 + 2 = 3
  328. // 20: [12] | 1 + prev(i) = 1 + 3 = 4
  329. // 50: [] | 0 + prev(i) = 0 + 4 = 4
  330. // 100: [] | 0 + prev(i) = 0 + 4 = 4
  331. // 250: [187.12, 199.9, 245.67] | 3 + prev(i) = 3 + 4 = 7
  332. wantLines := []string{
  333. `cash_register_bucket{le="1"} 1`,
  334. `cash_register_bucket{le="5"} 2`,
  335. `cash_register_bucket{le="10"} 3`,
  336. `cash_register_bucket{le="20"} 4`,
  337. `cash_register_bucket{le="50"} 4`,
  338. `cash_register_bucket{le="100"} 4`,
  339. `cash_register_bucket{le="250"} 7`,
  340. `cash_register_bucket{le="+Inf"} 7`,
  341. `cash_register_sum 654.0799999999999`, // Summation of the input values
  342. `cash_register_count 7`,
  343. }
  344. ctx := context.Background()
  345. ms := make([]stats.Measurement, 0, len(values))
  346. for _, value := range values {
  347. mx := m.M(value)
  348. ms = append(ms, mx)
  349. }
  350. stats.Record(ctx, ms...)
  351. // Give the recorder ample time to process recording
  352. <-time.After(10 * reportPeriod)
  353. cst := httptest.NewServer(exporter)
  354. defer cst.Close()
  355. res, err := http.Get(cst.URL)
  356. if err != nil {
  357. t.Fatalf("http.Get error: %v", err)
  358. }
  359. blob, err := ioutil.ReadAll(res.Body)
  360. if err != nil {
  361. t.Fatalf("Read body error: %v", err)
  362. }
  363. str := strings.Trim(string(blob), "\n")
  364. lines := strings.Split(str, "\n")
  365. nonComments := make([]string, 0, len(lines))
  366. for _, line := range lines {
  367. if !strings.Contains(line, "#") {
  368. nonComments = append(nonComments, line)
  369. }
  370. }
  371. got := strings.Join(nonComments, "\n")
  372. want := strings.Join(wantLines, "\n")
  373. if got != want {
  374. t.Fatalf("\ngot:\n%s\n\nwant:\n%s\n", got, want)
  375. }
  376. }
  377. func TestConstLabelsIncluded(t *testing.T) {
  378. constLabels := prometheus.Labels{
  379. "service": "spanner",
  380. }
  381. measureLabel, _ := tag.NewKey("method")
  382. exporter, err := NewExporter(Options{
  383. ConstLabels: constLabels,
  384. })
  385. if err != nil {
  386. t.Fatalf("failed to create prometheus exporter: %v", err)
  387. }
  388. view.RegisterExporter(exporter)
  389. defer view.UnregisterExporter(exporter)
  390. names := []string{"foo", "bar", "baz"}
  391. var measures mSlice
  392. for _, name := range names {
  393. measures.createAndAppend("tests/"+name, name, "")
  394. }
  395. var vc vCreator
  396. for _, m := range measures {
  397. vc.createAndAppend(m.Name(), m.Description(), []tag.Key{measureLabel}, m, view.Count())
  398. }
  399. if err := view.Register(vc...); err != nil {
  400. t.Fatalf("failed to create views: %v", err)
  401. }
  402. defer view.Unregister(vc...)
  403. view.SetReportingPeriod(time.Millisecond)
  404. ctx, _ := tag.New(context.Background(), tag.Upsert(measureLabel, "issue961"))
  405. for _, m := range measures {
  406. stats.Record(ctx, m.M(1))
  407. }
  408. srv := httptest.NewServer(exporter)
  409. defer srv.Close()
  410. var i int
  411. var output string
  412. for {
  413. time.Sleep(10 * time.Millisecond)
  414. if i == 1000 {
  415. t.Fatal("no output at /metrics (10s wait)")
  416. }
  417. i++
  418. resp, err := http.Get(srv.URL)
  419. if err != nil {
  420. t.Fatalf("failed to get /metrics: %v", err)
  421. }
  422. body, err := ioutil.ReadAll(resp.Body)
  423. if err != nil {
  424. t.Fatalf("failed to read body: %v", err)
  425. }
  426. resp.Body.Close()
  427. output = string(body)
  428. if output != "" {
  429. break
  430. }
  431. }
  432. if strings.Contains(output, "collected before with the same name and label values") {
  433. t.Fatal("metric name and labels being duplicated but must be unique")
  434. }
  435. if strings.Contains(output, "error(s) occurred") {
  436. t.Fatal("error reported by prometheus registry")
  437. }
  438. want := `# HELP tests_bar bar
  439. # TYPE tests_bar counter
  440. tests_bar{method="issue961",service="spanner"} 1
  441. # HELP tests_baz baz
  442. # TYPE tests_baz counter
  443. tests_baz{method="issue961",service="spanner"} 1
  444. # HELP tests_foo foo
  445. # TYPE tests_foo counter
  446. tests_foo{method="issue961",service="spanner"} 1
  447. `
  448. if output != want {
  449. t.Fatal("output differed from expected")
  450. }
  451. }
  452. func TestViewMeasureWithoutTag(t *testing.T) {
  453. exporter, err := NewExporter(Options{})
  454. if err != nil {
  455. t.Fatalf("failed to create prometheus exporter: %v", err)
  456. }
  457. view.RegisterExporter(exporter)
  458. defer view.UnregisterExporter(exporter)
  459. m := stats.Int64("tests/foo", "foo", stats.UnitDimensionless)
  460. k1, _ := tag.NewKey("key/1")
  461. k2, _ := tag.NewKey("key/2")
  462. k3, _ := tag.NewKey("key/3")
  463. k4, _ := tag.NewKey("key/4")
  464. k5, _ := tag.NewKey("key/5")
  465. randomKey, _ := tag.NewKey("issue659")
  466. v := &view.View{
  467. Name: m.Name(),
  468. Description: m.Description(),
  469. TagKeys: []tag.Key{k2, k5, k3, k1, k4}, // Ensure view has a tag
  470. Measure: m,
  471. Aggregation: view.Count(),
  472. }
  473. if err := view.Register(v); err != nil {
  474. t.Fatalf("failed to create views: %v", err)
  475. }
  476. defer view.Unregister(v)
  477. view.SetReportingPeriod(time.Millisecond)
  478. // Make a measure without some tags in the view.
  479. ctx1, _ := tag.New(context.Background(), tag.Upsert(k4, "issue659"), tag.Upsert(randomKey, "value"), tag.Upsert(k2, "issue659"))
  480. stats.Record(ctx1, m.M(1))
  481. ctx2, _ := tag.New(context.Background(), tag.Upsert(k5, "issue659"), tag.Upsert(k3, "issue659"), tag.Upsert(k1, "issue659"))
  482. stats.Record(ctx2, m.M(2))
  483. srv := httptest.NewServer(exporter)
  484. defer srv.Close()
  485. var i int
  486. var output string
  487. for {
  488. time.Sleep(10 * time.Millisecond)
  489. if i == 1000 {
  490. t.Fatal("no output at /metrics (10s wait)")
  491. }
  492. i++
  493. resp, err := http.Get(srv.URL)
  494. if err != nil {
  495. t.Fatalf("failed to get /metrics: %v", err)
  496. }
  497. body, err := ioutil.ReadAll(resp.Body)
  498. if err != nil {
  499. t.Fatalf("failed to read body: %v", err)
  500. }
  501. resp.Body.Close()
  502. output = string(body)
  503. if output != "" {
  504. break
  505. }
  506. }
  507. if strings.Contains(output, "collected before with the same name and label values") {
  508. t.Fatal("metric name and labels being duplicated but must be unique")
  509. }
  510. if strings.Contains(output, "error(s) occurred") {
  511. t.Fatal("error reported by prometheus registry")
  512. }
  513. want := `# HELP tests_foo foo
  514. # TYPE tests_foo counter
  515. tests_foo{key_1="",key_2="issue659",key_3="",key_4="issue659",key_5=""} 1
  516. tests_foo{key_1="issue659",key_2="",key_3="issue659",key_4="",key_5="issue659"} 1
  517. `
  518. if output != want {
  519. t.Fatalf("output differed from expected output: %s want: %s", output, want)
  520. }
  521. }