prometheus_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package prometheusexporter
  4. import (
  5. "context"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "strings"
  10. "testing"
  11. "time"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/require"
  14. "go.opentelemetry.io/collector/component/componenttest"
  15. "go.opentelemetry.io/collector/config/confighttp"
  16. "go.opentelemetry.io/collector/config/configtls"
  17. "go.opentelemetry.io/collector/exporter/exportertest"
  18. "go.opentelemetry.io/collector/pdata/pcommon"
  19. "go.opentelemetry.io/collector/pdata/pmetric"
  20. conventions "go.opentelemetry.io/collector/semconv/v1.6.1"
  21. "github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/testdata"
  22. "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/resourcetotelemetry"
  23. )
  24. func TestPrometheusExporter(t *testing.T) {
  25. tests := []struct {
  26. config *Config
  27. wantErr string
  28. wantStartErr string
  29. }{
  30. {
  31. config: &Config{
  32. Namespace: "test",
  33. ConstLabels: map[string]string{
  34. "foo0": "bar0",
  35. "code0": "one0",
  36. },
  37. HTTPServerSettings: confighttp.HTTPServerSettings{
  38. Endpoint: "localhost:8999",
  39. },
  40. SendTimestamps: false,
  41. MetricExpiration: 60 * time.Second,
  42. },
  43. },
  44. {
  45. config: &Config{
  46. HTTPServerSettings: confighttp.HTTPServerSettings{
  47. Endpoint: "localhost:88999",
  48. },
  49. },
  50. wantStartErr: "listen tcp: address 88999: invalid port",
  51. },
  52. {
  53. config: &Config{},
  54. wantErr: "expecting a non-blank address to run the Prometheus metrics handler",
  55. },
  56. }
  57. factory := NewFactory()
  58. set := exportertest.NewNopCreateSettings()
  59. for _, tt := range tests {
  60. // Run it a few times to ensure that shutdowns exit cleanly.
  61. for j := 0; j < 3; j++ {
  62. exp, err := factory.CreateMetricsExporter(context.Background(), set, tt.config)
  63. if tt.wantErr != "" {
  64. require.Error(t, err)
  65. assert.Equal(t, tt.wantErr, err.Error())
  66. continue
  67. }
  68. require.NoError(t, err)
  69. assert.NotNil(t, exp)
  70. err = exp.Start(context.Background(), componenttest.NewNopHost())
  71. if tt.wantStartErr != "" {
  72. require.Error(t, err)
  73. assert.Equal(t, tt.wantStartErr, err.Error())
  74. } else {
  75. require.NoError(t, err)
  76. }
  77. require.NoError(t, exp.Shutdown(context.Background()))
  78. }
  79. }
  80. }
  81. func TestPrometheusExporter_WithTLS(t *testing.T) {
  82. cfg := &Config{
  83. Namespace: "test",
  84. ConstLabels: map[string]string{
  85. "foo2": "bar2",
  86. "code2": "one2",
  87. },
  88. HTTPServerSettings: confighttp.HTTPServerSettings{
  89. Endpoint: "localhost:7777",
  90. TLSSetting: &configtls.TLSServerSetting{
  91. TLSSetting: configtls.TLSSetting{
  92. CertFile: "./testdata/certs/server.crt",
  93. KeyFile: "./testdata/certs/server.key",
  94. CAFile: "./testdata/certs/ca.crt",
  95. },
  96. },
  97. },
  98. SendTimestamps: true,
  99. MetricExpiration: 120 * time.Minute,
  100. ResourceToTelemetrySettings: resourcetotelemetry.Settings{
  101. Enabled: true,
  102. },
  103. }
  104. factory := NewFactory()
  105. set := exportertest.NewNopCreateSettings()
  106. exp, err := factory.CreateMetricsExporter(context.Background(), set, cfg)
  107. require.NoError(t, err)
  108. tlscs := configtls.TLSClientSetting{
  109. TLSSetting: configtls.TLSSetting{
  110. CAFile: "./testdata/certs/ca.crt",
  111. CertFile: "./testdata/certs/client.crt",
  112. KeyFile: "./testdata/certs/client.key",
  113. },
  114. ServerName: "localhost",
  115. }
  116. tls, err := tlscs.LoadTLSConfig()
  117. assert.NoError(t, err)
  118. httpClient := &http.Client{
  119. Transport: &http.Transport{
  120. TLSClientConfig: tls,
  121. },
  122. }
  123. t.Cleanup(func() {
  124. require.NoError(t, exp.Shutdown(context.Background()))
  125. // trigger a get so that the server cleans up our keepalive socket
  126. _, err = httpClient.Get("https://localhost:7777/metrics")
  127. require.NoError(t, err)
  128. })
  129. assert.NotNil(t, exp)
  130. require.NoError(t, exp.Start(context.Background(), componenttest.NewNopHost()))
  131. md := testdata.GenerateMetricsOneMetric()
  132. assert.NotNil(t, md)
  133. assert.NoError(t, exp.ConsumeMetrics(context.Background(), md))
  134. rsp, err := httpClient.Get("https://localhost:7777/metrics")
  135. require.NoError(t, err, "Failed to perform a scrape")
  136. if g, w := rsp.StatusCode, 200; g != w {
  137. t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
  138. }
  139. blob, _ := io.ReadAll(rsp.Body)
  140. _ = rsp.Body.Close()
  141. want := []string{
  142. `# HELP test_counter_int`,
  143. `# TYPE test_counter_int counter`,
  144. `test_counter_int{code2="one2",foo2="bar2",label_1="label-value-1",resource_attr="resource-attr-val-1"} 123 1581452773000`,
  145. `test_counter_int{code2="one2",foo2="bar2",label_2="label-value-2",resource_attr="resource-attr-val-1"} 456 1581452773000`,
  146. }
  147. for _, w := range want {
  148. if !strings.Contains(string(blob), w) {
  149. t.Errorf("Missing %v from response:\n%v", w, string(blob))
  150. }
  151. }
  152. }
  153. // See: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/4986
  154. func TestPrometheusExporter_endToEndMultipleTargets(t *testing.T) {
  155. cfg := &Config{
  156. Namespace: "test",
  157. ConstLabels: map[string]string{
  158. "foo1": "bar1",
  159. "code1": "one1",
  160. },
  161. HTTPServerSettings: confighttp.HTTPServerSettings{
  162. Endpoint: "localhost:7777",
  163. },
  164. MetricExpiration: 120 * time.Minute,
  165. }
  166. factory := NewFactory()
  167. set := exportertest.NewNopCreateSettings()
  168. exp, err := factory.CreateMetricsExporter(context.Background(), set, cfg)
  169. assert.NoError(t, err)
  170. t.Cleanup(func() {
  171. require.NoError(t, exp.Shutdown(context.Background()))
  172. // trigger a get so that the server cleans up our keepalive socket
  173. _, err = http.Get("http://localhost:7777/metrics")
  174. require.NoError(t, err)
  175. })
  176. assert.NotNil(t, exp)
  177. require.NoError(t, exp.Start(context.Background(), componenttest.NewNopHost()))
  178. // Should accumulate multiple metrics from different targets
  179. assert.NoError(t, exp.ConsumeMetrics(context.Background(), metricBuilder(128, "metric_1_", "cpu-exporter", "localhost:8080")))
  180. assert.NoError(t, exp.ConsumeMetrics(context.Background(), metricBuilder(128, "metric_1_", "cpu-exporter", "localhost:8081")))
  181. for delta := 0; delta <= 20; delta += 10 {
  182. assert.NoError(t, exp.ConsumeMetrics(context.Background(), metricBuilder(int64(delta), "metric_2_", "cpu-exporter", "localhost:8080")))
  183. assert.NoError(t, exp.ConsumeMetrics(context.Background(), metricBuilder(int64(delta), "metric_2_", "cpu-exporter", "localhost:8081")))
  184. res, err1 := http.Get("http://localhost:7777/metrics")
  185. require.NoError(t, err1, "Failed to perform a scrape")
  186. if g, w := res.StatusCode, 200; g != w {
  187. t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
  188. }
  189. blob, _ := io.ReadAll(res.Body)
  190. _ = res.Body.Close()
  191. want := []string{
  192. `# HELP test_metric_1_this_one_there_where Extra ones`,
  193. `# TYPE test_metric_1_this_one_there_where counter`,
  194. fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8080",job="cpu-exporter",os="windows"} %v`, 99+128),
  195. fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8080",job="cpu-exporter",os="linux"} %v`, 100+128),
  196. fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8081",job="cpu-exporter",os="windows"} %v`, 99+128),
  197. fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8081",job="cpu-exporter",os="linux"} %v`, 100+128),
  198. `# HELP test_metric_2_this_one_there_where Extra ones`,
  199. `# TYPE test_metric_2_this_one_there_where counter`,
  200. fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8080",job="cpu-exporter",os="windows"} %v`, 99+delta),
  201. fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8080",job="cpu-exporter",os="linux"} %v`, 100+delta),
  202. fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8081",job="cpu-exporter",os="windows"} %v`, 99+delta),
  203. fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8081",job="cpu-exporter",os="linux"} %v`, 100+delta),
  204. }
  205. for _, w := range want {
  206. if !strings.Contains(string(blob), w) {
  207. t.Errorf("Missing %v from response:\n%v", w, string(blob))
  208. }
  209. }
  210. }
  211. // Expired metrics should be removed during first scrape
  212. exp.(*wrapMetricsExporter).exporter.collector.accumulator.(*lastValueAccumulator).metricExpiration = 1 * time.Millisecond
  213. time.Sleep(10 * time.Millisecond)
  214. res, err := http.Get("http://localhost:7777/metrics")
  215. require.NoError(t, err, "Failed to perform a scrape")
  216. if g, w := res.StatusCode, 200; g != w {
  217. t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
  218. }
  219. blob, _ := io.ReadAll(res.Body)
  220. _ = res.Body.Close()
  221. require.Emptyf(t, string(blob), "Metrics did not expire")
  222. }
  223. func TestPrometheusExporter_endToEnd(t *testing.T) {
  224. cfg := &Config{
  225. Namespace: "test",
  226. ConstLabels: map[string]string{
  227. "foo1": "bar1",
  228. "code1": "one1",
  229. },
  230. HTTPServerSettings: confighttp.HTTPServerSettings{
  231. Endpoint: "localhost:7777",
  232. },
  233. MetricExpiration: 120 * time.Minute,
  234. }
  235. factory := NewFactory()
  236. set := exportertest.NewNopCreateSettings()
  237. exp, err := factory.CreateMetricsExporter(context.Background(), set, cfg)
  238. assert.NoError(t, err)
  239. t.Cleanup(func() {
  240. require.NoError(t, exp.Shutdown(context.Background()))
  241. // trigger a get so that the server cleans up our keepalive socket
  242. _, err = http.Get("http://localhost:7777/metrics")
  243. require.NoError(t, err)
  244. })
  245. assert.NotNil(t, exp)
  246. require.NoError(t, exp.Start(context.Background(), componenttest.NewNopHost()))
  247. // Should accumulate multiple metrics
  248. assert.NoError(t, exp.ConsumeMetrics(context.Background(), metricBuilder(128, "metric_1_", "cpu-exporter", "localhost:8080")))
  249. for delta := 0; delta <= 20; delta += 10 {
  250. assert.NoError(t, exp.ConsumeMetrics(context.Background(), metricBuilder(int64(delta), "metric_2_", "cpu-exporter", "localhost:8080")))
  251. res, err1 := http.Get("http://localhost:7777/metrics")
  252. require.NoError(t, err1, "Failed to perform a scrape")
  253. if g, w := res.StatusCode, 200; g != w {
  254. t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
  255. }
  256. blob, _ := io.ReadAll(res.Body)
  257. _ = res.Body.Close()
  258. want := []string{
  259. `# HELP test_metric_1_this_one_there_where Extra ones`,
  260. `# TYPE test_metric_1_this_one_there_where counter`,
  261. fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8080",job="cpu-exporter",os="windows"} %v`, 99+128),
  262. fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8080",job="cpu-exporter",os="linux"} %v`, 100+128),
  263. `# HELP test_metric_2_this_one_there_where Extra ones`,
  264. `# TYPE test_metric_2_this_one_there_where counter`,
  265. fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8080",job="cpu-exporter",os="windows"} %v`, 99+delta),
  266. fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code1="one1",foo1="bar1",instance="localhost:8080",job="cpu-exporter",os="linux"} %v`, 100+delta),
  267. }
  268. for _, w := range want {
  269. if !strings.Contains(string(blob), w) {
  270. t.Errorf("Missing %v from response:\n%v", w, string(blob))
  271. }
  272. }
  273. }
  274. // Expired metrics should be removed during first scrape
  275. exp.(*wrapMetricsExporter).exporter.collector.accumulator.(*lastValueAccumulator).metricExpiration = 1 * time.Millisecond
  276. time.Sleep(10 * time.Millisecond)
  277. res, err := http.Get("http://localhost:7777/metrics")
  278. require.NoError(t, err, "Failed to perform a scrape")
  279. if g, w := res.StatusCode, 200; g != w {
  280. t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
  281. }
  282. blob, _ := io.ReadAll(res.Body)
  283. _ = res.Body.Close()
  284. require.Emptyf(t, string(blob), "Metrics did not expire")
  285. }
  286. func TestPrometheusExporter_endToEndWithTimestamps(t *testing.T) {
  287. cfg := &Config{
  288. Namespace: "test",
  289. ConstLabels: map[string]string{
  290. "foo2": "bar2",
  291. "code2": "one2",
  292. },
  293. HTTPServerSettings: confighttp.HTTPServerSettings{
  294. Endpoint: "localhost:7777",
  295. },
  296. SendTimestamps: true,
  297. MetricExpiration: 120 * time.Minute,
  298. }
  299. factory := NewFactory()
  300. set := exportertest.NewNopCreateSettings()
  301. exp, err := factory.CreateMetricsExporter(context.Background(), set, cfg)
  302. assert.NoError(t, err)
  303. t.Cleanup(func() {
  304. require.NoError(t, exp.Shutdown(context.Background()))
  305. // trigger a get so that the server cleans up our keepalive socket
  306. _, err = http.Get("http://localhost:7777/metrics")
  307. require.NoError(t, err)
  308. })
  309. assert.NotNil(t, exp)
  310. require.NoError(t, exp.Start(context.Background(), componenttest.NewNopHost()))
  311. // Should accumulate multiple metrics
  312. assert.NoError(t, exp.ConsumeMetrics(context.Background(), metricBuilder(128, "metric_1_", "node-exporter", "localhost:8080")))
  313. for delta := 0; delta <= 20; delta += 10 {
  314. assert.NoError(t, exp.ConsumeMetrics(context.Background(), metricBuilder(int64(delta), "metric_2_", "node-exporter", "localhost:8080")))
  315. res, err1 := http.Get("http://localhost:7777/metrics")
  316. require.NoError(t, err1, "Failed to perform a scrape")
  317. if g, w := res.StatusCode, 200; g != w {
  318. t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
  319. }
  320. blob, _ := io.ReadAll(res.Body)
  321. _ = res.Body.Close()
  322. want := []string{
  323. `# HELP test_metric_1_this_one_there_where Extra ones`,
  324. `# TYPE test_metric_1_this_one_there_where counter`,
  325. fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code2="one2",foo2="bar2",instance="localhost:8080",job="node-exporter",os="windows"} %v %v`, 99+128, 1543160298100+128000),
  326. fmt.Sprintf(`test_metric_1_this_one_there_where{arch="x86",code2="one2",foo2="bar2",instance="localhost:8080",job="node-exporter",os="linux"} %v %v`, 100+128, 1543160298100),
  327. `# HELP test_metric_2_this_one_there_where Extra ones`,
  328. `# TYPE test_metric_2_this_one_there_where counter`,
  329. fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code2="one2",foo2="bar2",instance="localhost:8080",job="node-exporter",os="windows"} %v %v`, 99+delta, 1543160298100+delta*1000),
  330. fmt.Sprintf(`test_metric_2_this_one_there_where{arch="x86",code2="one2",foo2="bar2",instance="localhost:8080",job="node-exporter",os="linux"} %v %v`, 100+delta, 1543160298100),
  331. }
  332. for _, w := range want {
  333. if !strings.Contains(string(blob), w) {
  334. t.Errorf("Missing %v from response:\n%v", w, string(blob))
  335. }
  336. }
  337. }
  338. // Expired metrics should be removed during first scrape
  339. exp.(*wrapMetricsExporter).exporter.collector.accumulator.(*lastValueAccumulator).metricExpiration = 1 * time.Millisecond
  340. time.Sleep(10 * time.Millisecond)
  341. res, err := http.Get("http://localhost:7777/metrics")
  342. require.NoError(t, err, "Failed to perform a scrape")
  343. if g, w := res.StatusCode, 200; g != w {
  344. t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
  345. }
  346. blob, _ := io.ReadAll(res.Body)
  347. _ = res.Body.Close()
  348. require.Emptyf(t, string(blob), "Metrics did not expire")
  349. }
  350. func TestPrometheusExporter_endToEndWithResource(t *testing.T) {
  351. cfg := &Config{
  352. Namespace: "test",
  353. ConstLabels: map[string]string{
  354. "foo2": "bar2",
  355. "code2": "one2",
  356. },
  357. HTTPServerSettings: confighttp.HTTPServerSettings{
  358. Endpoint: "localhost:7777",
  359. },
  360. SendTimestamps: true,
  361. MetricExpiration: 120 * time.Minute,
  362. ResourceToTelemetrySettings: resourcetotelemetry.Settings{
  363. Enabled: true,
  364. },
  365. }
  366. factory := NewFactory()
  367. set := exportertest.NewNopCreateSettings()
  368. exp, err := factory.CreateMetricsExporter(context.Background(), set, cfg)
  369. assert.NoError(t, err)
  370. t.Cleanup(func() {
  371. require.NoError(t, exp.Shutdown(context.Background()))
  372. // trigger a get so that the server cleans up our keepalive socket
  373. _, err = http.Get("http://localhost:7777/metrics")
  374. require.NoError(t, err, "Failed to perform a scrape")
  375. })
  376. assert.NotNil(t, exp)
  377. require.NoError(t, exp.Start(context.Background(), componenttest.NewNopHost()))
  378. md := testdata.GenerateMetricsOneMetric()
  379. assert.NotNil(t, md)
  380. assert.NoError(t, exp.ConsumeMetrics(context.Background(), md))
  381. rsp, err := http.Get("http://localhost:7777/metrics")
  382. require.NoError(t, err, "Failed to perform a scrape")
  383. if g, w := rsp.StatusCode, 200; g != w {
  384. t.Errorf("Mismatched HTTP response status code: Got: %d Want: %d", g, w)
  385. }
  386. blob, _ := io.ReadAll(rsp.Body)
  387. _ = rsp.Body.Close()
  388. want := []string{
  389. `# HELP test_counter_int`,
  390. `# TYPE test_counter_int counter`,
  391. `test_counter_int{code2="one2",foo2="bar2",label_1="label-value-1",resource_attr="resource-attr-val-1"} 123 1581452773000`,
  392. `test_counter_int{code2="one2",foo2="bar2",label_2="label-value-2",resource_attr="resource-attr-val-1"} 456 1581452773000`,
  393. }
  394. for _, w := range want {
  395. if !strings.Contains(string(blob), w) {
  396. t.Errorf("Missing %v from response:\n%v", w, string(blob))
  397. }
  398. }
  399. }
  400. func metricBuilder(delta int64, prefix, job, instance string) pmetric.Metrics {
  401. md := pmetric.NewMetrics()
  402. rms := md.ResourceMetrics().AppendEmpty()
  403. rms0 := md.ResourceMetrics().At(0)
  404. rms0.Resource().Attributes().PutStr(conventions.AttributeServiceName, job)
  405. rms0.Resource().Attributes().PutStr(conventions.AttributeServiceInstanceID, instance)
  406. ms := rms.ScopeMetrics().AppendEmpty().Metrics()
  407. m1 := ms.AppendEmpty()
  408. m1.SetName(prefix + "this/one/there(where)")
  409. m1.SetDescription("Extra ones")
  410. m1.SetUnit("1")
  411. d1 := m1.SetEmptySum()
  412. d1.SetIsMonotonic(true)
  413. d1.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
  414. dp1 := d1.DataPoints().AppendEmpty()
  415. dp1.SetStartTimestamp(pcommon.NewTimestampFromTime(time.Unix(1543160298+delta, 100000090)))
  416. dp1.SetTimestamp(pcommon.NewTimestampFromTime(time.Unix(1543160298+delta, 100000997)))
  417. dp1.Attributes().PutStr("os", "windows")
  418. dp1.Attributes().PutStr("arch", "x86")
  419. dp1.SetIntValue(99 + delta)
  420. m2 := ms.AppendEmpty()
  421. m2.SetName(prefix + "this/one/there(where)")
  422. m2.SetDescription("Extra ones")
  423. m2.SetUnit("1")
  424. d2 := m2.SetEmptySum()
  425. d2.SetIsMonotonic(true)
  426. d2.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
  427. dp2 := d2.DataPoints().AppendEmpty()
  428. dp2.SetStartTimestamp(pcommon.NewTimestampFromTime(time.Unix(1543160298, 100000090)))
  429. dp2.SetTimestamp(pcommon.NewTimestampFromTime(time.Unix(1543160298, 100000997)))
  430. dp2.Attributes().PutStr("os", "linux")
  431. dp2.Attributes().PutStr("arch", "x86")
  432. dp2.SetIntValue(100 + delta)
  433. return md
  434. }