metrics_receiver_non_numerical_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package prometheusreceiver
  4. import (
  5. "fmt"
  6. "math"
  7. "testing"
  8. "github.com/prometheus/prometheus/model/value"
  9. "github.com/stretchr/testify/assert"
  10. "go.opentelemetry.io/collector/pdata/pcommon"
  11. "go.opentelemetry.io/collector/pdata/pmetric"
  12. )
  13. var staleNaNsPage1 = `
  14. # HELP go_threads Number of OS threads created
  15. # TYPE go_threads gauge
  16. go_threads 19
  17. # HELP http_requests_total The total number of HTTP requests.
  18. # TYPE http_requests_total counter
  19. http_requests_total{method="post",code="200"} 100
  20. http_requests_total{method="post",code="400"} 5
  21. # HELP http_request_duration_seconds A histogram of the request duration.
  22. # TYPE http_request_duration_seconds histogram
  23. http_request_duration_seconds_bucket{le="0.05"} 1000
  24. http_request_duration_seconds_bucket{le="0.5"} 1500
  25. http_request_duration_seconds_bucket{le="1"} 2000
  26. http_request_duration_seconds_bucket{le="+Inf"} 2500
  27. http_request_duration_seconds_sum 5000
  28. http_request_duration_seconds_count 2500
  29. # HELP rpc_duration_seconds A summary of the RPC duration in seconds.
  30. # TYPE rpc_duration_seconds summary
  31. rpc_duration_seconds{quantile="0.01"} 1
  32. rpc_duration_seconds{quantile="0.9"} 5
  33. rpc_duration_seconds{quantile="0.99"} 8
  34. rpc_duration_seconds_sum 5000
  35. rpc_duration_seconds_count 1000
  36. `
  37. var (
  38. totalScrapes = 10
  39. )
  40. // TestStaleNaNs validates that staleness marker gets generated when the timeseries is no longer present
  41. func TestStaleNaNs(t *testing.T) {
  42. var mockResponses []mockPrometheusResponse
  43. for i := 0; i < totalScrapes; i++ {
  44. if i%2 == 0 {
  45. mockResponses = append(mockResponses, mockPrometheusResponse{
  46. code: 200,
  47. data: staleNaNsPage1,
  48. })
  49. } else {
  50. mockResponses = append(mockResponses, mockPrometheusResponse{
  51. code: 500,
  52. data: "",
  53. })
  54. }
  55. }
  56. targets := []*testData{
  57. {
  58. name: "target1",
  59. pages: mockResponses,
  60. validateFunc: verifyStaleNaNs,
  61. validateScrapes: true,
  62. },
  63. }
  64. testComponent(t, targets, nil)
  65. }
  66. func verifyStaleNaNs(t *testing.T, td *testData, resourceMetrics []pmetric.ResourceMetrics) {
  67. verifyNumTotalScrapeResults(t, td, resourceMetrics)
  68. metrics1 := resourceMetrics[0].ScopeMetrics().At(0).Metrics()
  69. ts := getTS(metrics1)
  70. for i := 0; i < totalScrapes; i++ {
  71. if i%2 == 0 {
  72. verifyStaleNaNsSuccessfulScrape(t, td, resourceMetrics[i], ts, i+1)
  73. } else {
  74. verifyStaleNaNsFailedScrape(t, td, resourceMetrics[i], ts, i+1)
  75. }
  76. }
  77. }
  78. func verifyStaleNaNsSuccessfulScrape(t *testing.T, td *testData, resourceMetric pmetric.ResourceMetrics, startTimestamp pcommon.Timestamp, iteration int) {
  79. // m1 has 4 metrics + 5 internal scraper metrics
  80. assert.Equal(t, 9, metricsCount(resourceMetric))
  81. wantAttributes := td.attributes // should want attribute be part of complete target or each scrape?
  82. metrics1 := resourceMetric.ScopeMetrics().At(0).Metrics()
  83. ts1 := getTS(metrics1)
  84. e1 := []testExpectation{
  85. assertMetricPresent("go_threads",
  86. compareMetricType(pmetric.MetricTypeGauge),
  87. compareMetricUnit(""),
  88. []dataPointExpectation{
  89. {
  90. numberPointComparator: []numberPointComparator{
  91. compareTimestamp(ts1),
  92. compareDoubleValue(19),
  93. },
  94. },
  95. }),
  96. assertMetricPresent("http_requests_total",
  97. compareMetricType(pmetric.MetricTypeSum),
  98. compareMetricUnit(""),
  99. []dataPointExpectation{
  100. {
  101. numberPointComparator: []numberPointComparator{
  102. compareStartTimestamp(startTimestamp),
  103. compareTimestamp(ts1),
  104. compareDoubleValue(100),
  105. compareAttributes(map[string]string{"method": "post", "code": "200"}),
  106. },
  107. },
  108. {
  109. numberPointComparator: []numberPointComparator{
  110. compareStartTimestamp(startTimestamp),
  111. compareTimestamp(ts1),
  112. compareDoubleValue(5),
  113. compareAttributes(map[string]string{"method": "post", "code": "400"}),
  114. },
  115. },
  116. }),
  117. assertMetricPresent("http_request_duration_seconds",
  118. compareMetricType(pmetric.MetricTypeHistogram),
  119. compareMetricUnit(""),
  120. []dataPointExpectation{
  121. {
  122. histogramPointComparator: []histogramPointComparator{
  123. compareHistogramStartTimestamp(startTimestamp),
  124. compareHistogramTimestamp(ts1),
  125. compareHistogram(2500, 5000, []float64{0.05, 0.5, 1}, []uint64{1000, 500, 500, 500}),
  126. },
  127. },
  128. }),
  129. assertMetricPresent("rpc_duration_seconds",
  130. compareMetricType(pmetric.MetricTypeSummary),
  131. compareMetricUnit(""),
  132. []dataPointExpectation{
  133. {
  134. summaryPointComparator: []summaryPointComparator{
  135. compareSummaryStartTimestamp(startTimestamp),
  136. compareSummaryTimestamp(ts1),
  137. compareSummary(1000, 5000, [][]float64{{0.01, 1}, {0.9, 5}, {0.99, 8}}),
  138. },
  139. },
  140. }),
  141. }
  142. doCompare(t, fmt.Sprintf("validScrape-scrape-%d", iteration), wantAttributes, resourceMetric, e1)
  143. }
  144. func verifyStaleNaNsFailedScrape(t *testing.T, td *testData, resourceMetric pmetric.ResourceMetrics, startTimestamp pcommon.Timestamp, iteration int) {
  145. // m1 has 4 metrics + 5 internal scraper metrics
  146. assert.Equal(t, 9, metricsCount(resourceMetric))
  147. wantAttributes := td.attributes
  148. allMetrics := getMetrics(resourceMetric)
  149. assertUp(t, 0, allMetrics)
  150. metrics1 := resourceMetric.ScopeMetrics().At(0).Metrics()
  151. ts1 := getTS(metrics1)
  152. e1 := []testExpectation{
  153. assertMetricPresent("go_threads",
  154. compareMetricType(pmetric.MetricTypeGauge),
  155. compareMetricUnit(""),
  156. []dataPointExpectation{
  157. {
  158. numberPointComparator: []numberPointComparator{
  159. compareTimestamp(ts1),
  160. assertNumberPointFlagNoRecordedValue(),
  161. },
  162. },
  163. }),
  164. assertMetricPresent("http_requests_total",
  165. compareMetricType(pmetric.MetricTypeSum),
  166. compareMetricUnit(""),
  167. []dataPointExpectation{
  168. {
  169. numberPointComparator: []numberPointComparator{
  170. compareStartTimestamp(startTimestamp),
  171. compareTimestamp(ts1),
  172. assertNumberPointFlagNoRecordedValue(),
  173. },
  174. },
  175. {
  176. numberPointComparator: []numberPointComparator{
  177. compareStartTimestamp(startTimestamp),
  178. compareTimestamp(ts1),
  179. assertNumberPointFlagNoRecordedValue(),
  180. },
  181. },
  182. }),
  183. assertMetricPresent("http_request_duration_seconds",
  184. compareMetricType(pmetric.MetricTypeHistogram),
  185. compareMetricUnit(""),
  186. []dataPointExpectation{
  187. {
  188. histogramPointComparator: []histogramPointComparator{
  189. compareHistogramStartTimestamp(startTimestamp),
  190. compareHistogramTimestamp(ts1),
  191. assertHistogramPointFlagNoRecordedValue(),
  192. },
  193. },
  194. }),
  195. assertMetricPresent("rpc_duration_seconds",
  196. compareMetricType(pmetric.MetricTypeSummary),
  197. compareMetricUnit(""),
  198. []dataPointExpectation{
  199. {
  200. summaryPointComparator: []summaryPointComparator{
  201. compareSummaryStartTimestamp(startTimestamp),
  202. compareSummaryTimestamp(ts1),
  203. assertSummaryPointFlagNoRecordedValue(),
  204. },
  205. },
  206. }),
  207. }
  208. doCompare(t, fmt.Sprintf("failedScrape-scrape-%d", iteration), wantAttributes, resourceMetric, e1)
  209. }
  210. // Prometheus gauge metric can be set to NaN, a use case could be when value 0 is not representable
  211. // Prometheus summary metric quantiles can have NaN after getting expired
  212. var normalNaNsPage1 = `
  213. # HELP go_threads Number of OS threads created
  214. # TYPE go_threads gauge
  215. go_threads NaN
  216. # HELP redis_connected_clients Redis connected clients
  217. redis_connected_clients{name="rough-snowflake-web",port="6380"} NaN
  218. # HELP rpc_duration_seconds A summary of the RPC duration in seconds.
  219. # TYPE rpc_duration_seconds summary
  220. rpc_duration_seconds{quantile="0.01"} NaN
  221. rpc_duration_seconds{quantile="0.9"} NaN
  222. rpc_duration_seconds{quantile="0.99"} NaN
  223. rpc_duration_seconds_sum 5000
  224. rpc_duration_seconds_count 1000
  225. `
  226. // TestNormalNaNs validates the output of receiver when testdata contains NaN values
  227. func TestNormalNaNs(t *testing.T) {
  228. // 1. setup input data
  229. targets := []*testData{
  230. {
  231. name: "target1",
  232. pages: []mockPrometheusResponse{
  233. {code: 200, data: normalNaNsPage1},
  234. },
  235. validateFunc: verifyNormalNaNs,
  236. },
  237. }
  238. testComponent(t, targets, nil)
  239. }
  240. func verifyNormalNaNs(t *testing.T, td *testData, resourceMetrics []pmetric.ResourceMetrics) {
  241. verifyNumValidScrapeResults(t, td, resourceMetrics)
  242. m1 := resourceMetrics[0]
  243. // m1 has 3 metrics + 5 internal scraper metrics
  244. assert.Equal(t, 8, metricsCount(m1))
  245. wantAttributes := td.attributes
  246. metrics1 := m1.ScopeMetrics().At(0).Metrics()
  247. ts1 := getTS(metrics1)
  248. e1 := []testExpectation{
  249. assertMetricPresent("go_threads",
  250. compareMetricType(pmetric.MetricTypeGauge),
  251. compareMetricUnit(""),
  252. []dataPointExpectation{
  253. {
  254. numberPointComparator: []numberPointComparator{
  255. compareTimestamp(ts1),
  256. assertNormalNan(),
  257. },
  258. },
  259. }),
  260. assertMetricPresent("redis_connected_clients",
  261. compareMetricType(pmetric.MetricTypeGauge),
  262. compareMetricUnit(""),
  263. []dataPointExpectation{
  264. {
  265. numberPointComparator: []numberPointComparator{
  266. compareTimestamp(ts1),
  267. compareAttributes(map[string]string{"name": "rough-snowflake-web", "port": "6380"}),
  268. assertNormalNan(),
  269. },
  270. },
  271. }),
  272. assertMetricPresent("rpc_duration_seconds",
  273. compareMetricType(pmetric.MetricTypeSummary),
  274. compareMetricUnit(""),
  275. []dataPointExpectation{
  276. {
  277. summaryPointComparator: []summaryPointComparator{
  278. compareSummaryStartTimestamp(ts1),
  279. compareSummaryTimestamp(ts1),
  280. compareSummary(1000, 5000, [][]float64{{0.01, math.Float64frombits(value.NormalNaN)},
  281. {0.9, math.Float64frombits(value.NormalNaN)}, {0.99, math.Float64frombits(value.NormalNaN)}}),
  282. },
  283. },
  284. }),
  285. }
  286. doCompare(t, "scrape-NormalNaN-1", wantAttributes, m1, e1)
  287. }
  288. var infPage1 = `
  289. # HELP go_threads Number of OS threads created
  290. # TYPE go_threads gauge
  291. go_threads +Inf
  292. # HELP redis_connected_clients Redis connected clients
  293. redis_connected_clients{name="rough-snowflake-web",port="6380"} -Inf
  294. # HELP http_requests_total The total number of HTTP requests.
  295. # TYPE http_requests_total counter
  296. http_requests_total{method="post",code="200"} +Inf
  297. # HELP rpc_duration_seconds A summary of the RPC duration in seconds.
  298. # TYPE rpc_duration_seconds summary
  299. rpc_duration_seconds{quantile="0.01"} +Inf
  300. rpc_duration_seconds{quantile="0.9"} +Inf
  301. rpc_duration_seconds{quantile="0.99"} +Inf
  302. rpc_duration_seconds_sum 5000
  303. rpc_duration_seconds_count 1000
  304. `
  305. func TestInfValues(t *testing.T) {
  306. // 1. setup input data
  307. targets := []*testData{
  308. {
  309. name: "target1",
  310. pages: []mockPrometheusResponse{
  311. {code: 200, data: infPage1},
  312. },
  313. validateFunc: verifyInfValues,
  314. },
  315. }
  316. testComponent(t, targets, nil)
  317. }
  318. func verifyInfValues(t *testing.T, td *testData, resourceMetrics []pmetric.ResourceMetrics) {
  319. verifyNumValidScrapeResults(t, td, resourceMetrics)
  320. m1 := resourceMetrics[0]
  321. // m1 has 4 metrics + 5 internal scraper metrics
  322. assert.Equal(t, 9, metricsCount(m1))
  323. wantAttributes := td.attributes
  324. metrics1 := m1.ScopeMetrics().At(0).Metrics()
  325. ts1 := getTS(metrics1)
  326. e1 := []testExpectation{
  327. assertMetricPresent("go_threads",
  328. compareMetricType(pmetric.MetricTypeGauge),
  329. compareMetricUnit(""),
  330. []dataPointExpectation{
  331. {
  332. numberPointComparator: []numberPointComparator{
  333. compareTimestamp(ts1),
  334. compareDoubleValue(math.Inf(1)),
  335. },
  336. },
  337. }),
  338. assertMetricPresent("redis_connected_clients",
  339. compareMetricType(pmetric.MetricTypeGauge),
  340. compareMetricUnit(""),
  341. []dataPointExpectation{
  342. {
  343. numberPointComparator: []numberPointComparator{
  344. compareTimestamp(ts1),
  345. compareAttributes(map[string]string{"name": "rough-snowflake-web", "port": "6380"}),
  346. compareDoubleValue(math.Inf(-1)),
  347. },
  348. },
  349. }),
  350. assertMetricPresent("http_requests_total",
  351. compareMetricType(pmetric.MetricTypeSum),
  352. compareMetricUnit(""),
  353. []dataPointExpectation{
  354. {
  355. numberPointComparator: []numberPointComparator{
  356. compareStartTimestamp(ts1),
  357. compareTimestamp(ts1),
  358. compareDoubleValue(math.Inf(1)),
  359. compareAttributes(map[string]string{"method": "post", "code": "200"}),
  360. },
  361. },
  362. }),
  363. assertMetricPresent("rpc_duration_seconds",
  364. compareMetricType(pmetric.MetricTypeSummary),
  365. compareMetricUnit(""),
  366. []dataPointExpectation{
  367. {
  368. summaryPointComparator: []summaryPointComparator{
  369. compareSummaryStartTimestamp(ts1),
  370. compareSummaryTimestamp(ts1),
  371. compareSummary(1000, 5000, [][]float64{{0.01, math.Inf(1)}, {0.9, math.Inf(1)}, {0.99, math.Inf(1)}}),
  372. },
  373. },
  374. }),
  375. }
  376. doCompare(t, "scrape-InfValues-1", wantAttributes, m1, e1)
  377. }