memory_scraper_test.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package memoryscraper
  4. import (
  5. "context"
  6. "errors"
  7. "runtime"
  8. "testing"
  9. "github.com/shirou/gopsutil/v3/mem"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/stretchr/testify/require"
  12. "go.opentelemetry.io/collector/component/componenttest"
  13. "go.opentelemetry.io/collector/pdata/pcommon"
  14. "go.opentelemetry.io/collector/pdata/pmetric"
  15. "go.opentelemetry.io/collector/receiver/receivertest"
  16. "go.opentelemetry.io/collector/receiver/scrapererror"
  17. "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal"
  18. "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/scraper/memoryscraper/internal/metadata"
  19. )
  20. func TestScrape(t *testing.T) {
  21. type testCase struct {
  22. name string
  23. virtualMemoryFunc func(context.Context) (*mem.VirtualMemoryStat, error)
  24. expectedErr string
  25. initializationErr string
  26. config *Config
  27. expectedMetricCount int
  28. bootTimeFunc func(context.Context) (uint64, error)
  29. }
  30. testCases := []testCase{
  31. {
  32. name: "Standard",
  33. config: &Config{
  34. MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),
  35. },
  36. expectedMetricCount: 1,
  37. },
  38. {
  39. name: "All metrics enabled",
  40. config: &Config{
  41. MetricsBuilderConfig: metadata.MetricsBuilderConfig{
  42. Metrics: metadata.MetricsConfig{
  43. SystemMemoryUtilization: metadata.MetricConfig{
  44. Enabled: true,
  45. },
  46. SystemMemoryUsage: metadata.MetricConfig{
  47. Enabled: true,
  48. },
  49. SystemLinuxMemoryAvailable: metadata.MetricConfig{
  50. Enabled: true,
  51. },
  52. },
  53. },
  54. },
  55. expectedMetricCount: func() int {
  56. if runtime.GOOS == "linux" {
  57. return 3
  58. }
  59. return 2
  60. }(),
  61. },
  62. {
  63. name: "Error",
  64. virtualMemoryFunc: func(context.Context) (*mem.VirtualMemoryStat, error) { return nil, errors.New("err1") },
  65. expectedErr: "err1",
  66. config: &Config{
  67. MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),
  68. },
  69. expectedMetricCount: 1,
  70. },
  71. {
  72. name: "Error",
  73. bootTimeFunc: func(context.Context) (uint64, error) { return 100, errors.New("err1") },
  74. initializationErr: "err1",
  75. config: &Config{
  76. MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(),
  77. },
  78. expectedMetricCount: 1,
  79. },
  80. }
  81. for _, test := range testCases {
  82. t.Run(test.name, func(t *testing.T) {
  83. scraper := newMemoryScraper(context.Background(), receivertest.NewNopCreateSettings(), test.config)
  84. if test.virtualMemoryFunc != nil {
  85. scraper.virtualMemory = test.virtualMemoryFunc
  86. }
  87. if test.bootTimeFunc != nil {
  88. scraper.bootTime = test.bootTimeFunc
  89. }
  90. err := scraper.start(context.Background(), componenttest.NewNopHost())
  91. if test.initializationErr != "" {
  92. assert.EqualError(t, err, test.initializationErr)
  93. return
  94. }
  95. require.NoError(t, err, "Failed to initialize memory scraper: %v", err)
  96. md, err := scraper.scrape(context.Background())
  97. if test.expectedErr != "" {
  98. assert.EqualError(t, err, test.expectedErr)
  99. isPartial := scrapererror.IsPartialScrapeError(err)
  100. assert.True(t, isPartial)
  101. if isPartial {
  102. var scraperErr scrapererror.PartialScrapeError
  103. require.ErrorAs(t, err, &scraperErr)
  104. assert.Equal(t, metricsLen, scraperErr.Failed)
  105. }
  106. return
  107. }
  108. require.NoError(t, err, "Failed to scrape metrics: %v", err)
  109. assert.Equal(t, test.expectedMetricCount, md.MetricCount())
  110. metrics := md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics()
  111. memUsageIdx := -1
  112. for i := 0; i < md.MetricCount(); i++ {
  113. if metrics.At(i).Name() == "system.memory.usage" {
  114. memUsageIdx = i
  115. }
  116. }
  117. assert.NotEqual(t, memUsageIdx, -1)
  118. assertMemoryUsageMetricValid(t, metrics.At(memUsageIdx), "system.memory.usage")
  119. if runtime.GOOS == "linux" {
  120. assertMemoryUsageMetricHasLinuxSpecificStateLabels(t, metrics.At(memUsageIdx))
  121. } else if runtime.GOOS != "windows" {
  122. internal.AssertSumMetricHasAttributeValue(t, metrics.At(memUsageIdx), 2, "state",
  123. pcommon.NewValueStr(metadata.AttributeStateInactive.String()))
  124. }
  125. internal.AssertSameTimeStampForAllMetrics(t, metrics)
  126. })
  127. }
  128. }
  129. func TestScrape_MemoryUtilization(t *testing.T) {
  130. type testCase struct {
  131. name string
  132. virtualMemoryFunc func(context.Context) (*mem.VirtualMemoryStat, error)
  133. expectedErr error
  134. }
  135. testCases := []testCase{
  136. {
  137. name: "Standard",
  138. },
  139. {
  140. name: "Invalid total memory",
  141. virtualMemoryFunc: func(context.Context) (*mem.VirtualMemoryStat, error) { return &mem.VirtualMemoryStat{Total: 0}, nil },
  142. expectedErr: ErrInvalidTotalMem,
  143. },
  144. }
  145. for _, test := range testCases {
  146. t.Run(test.name, func(t *testing.T) {
  147. mbc := metadata.DefaultMetricsBuilderConfig()
  148. mbc.Metrics.SystemMemoryUtilization.Enabled = true
  149. mbc.Metrics.SystemMemoryUsage.Enabled = false
  150. scraperConfig := Config{
  151. MetricsBuilderConfig: mbc,
  152. }
  153. scraper := newMemoryScraper(context.Background(), receivertest.NewNopCreateSettings(), &scraperConfig)
  154. if test.virtualMemoryFunc != nil {
  155. scraper.virtualMemory = test.virtualMemoryFunc
  156. }
  157. err := scraper.start(context.Background(), componenttest.NewNopHost())
  158. require.NoError(t, err, "Failed to initialize memory scraper: %v", err)
  159. md, err := scraper.scrape(context.Background())
  160. if test.expectedErr != nil {
  161. var partialScrapeErr scrapererror.PartialScrapeError
  162. assert.ErrorAs(t, err, &partialScrapeErr)
  163. return
  164. }
  165. require.NoError(t, err, "Failed to scrape metrics: %v", err)
  166. metrics := md.ResourceMetrics().At(0).ScopeMetrics().At(0).Metrics()
  167. assertMemoryUtilizationMetricValid(t, metrics.At(0), "system.memory.utilization")
  168. if runtime.GOOS == "linux" {
  169. assertMemoryUtilizationMetricHasLinuxSpecificStateLabels(t, metrics.At(0))
  170. } else if runtime.GOOS != "windows" {
  171. internal.AssertGaugeMetricHasAttributeValue(t, metrics.At(0), 2, "state",
  172. pcommon.NewValueStr(metadata.AttributeStateInactive.String()))
  173. }
  174. internal.AssertSameTimeStampForAllMetrics(t, metrics)
  175. })
  176. }
  177. }
  178. func assertMemoryUsageMetricValid(t *testing.T, metric pmetric.Metric, expectedName string) {
  179. assert.Equal(t, expectedName, metric.Name())
  180. assert.GreaterOrEqual(t, metric.Sum().DataPoints().Len(), 2)
  181. internal.AssertSumMetricHasAttributeValue(t, metric, 0, "state",
  182. pcommon.NewValueStr(metadata.AttributeStateUsed.String()))
  183. internal.AssertSumMetricHasAttributeValue(t, metric, 1, "state",
  184. pcommon.NewValueStr(metadata.AttributeStateFree.String()))
  185. }
  186. func assertMemoryUtilizationMetricValid(t *testing.T, metric pmetric.Metric, expectedName string) {
  187. assert.Equal(t, expectedName, metric.Name())
  188. assert.GreaterOrEqual(t, metric.Gauge().DataPoints().Len(), 2)
  189. internal.AssertGaugeMetricHasAttributeValue(t, metric, 0, "state",
  190. pcommon.NewValueStr(metadata.AttributeStateUsed.String()))
  191. internal.AssertGaugeMetricHasAttributeValue(t, metric, 1, "state",
  192. pcommon.NewValueStr(metadata.AttributeStateFree.String()))
  193. }
  194. func assertMemoryUsageMetricHasLinuxSpecificStateLabels(t *testing.T, metric pmetric.Metric) {
  195. internal.AssertSumMetricHasAttributeValue(t, metric, 2, "state",
  196. pcommon.NewValueStr(metadata.AttributeStateBuffered.String()))
  197. internal.AssertSumMetricHasAttributeValue(t, metric, 3, "state",
  198. pcommon.NewValueStr(metadata.AttributeStateCached.String()))
  199. internal.AssertSumMetricHasAttributeValue(t, metric, 4, "state",
  200. pcommon.NewValueStr(metadata.AttributeStateSlabReclaimable.String()))
  201. internal.AssertSumMetricHasAttributeValue(t, metric, 5, "state",
  202. pcommon.NewValueStr(metadata.AttributeStateSlabUnreclaimable.String()))
  203. }
  204. func assertMemoryUtilizationMetricHasLinuxSpecificStateLabels(t *testing.T, metric pmetric.Metric) {
  205. internal.AssertGaugeMetricHasAttributeValue(t, metric, 2, "state",
  206. pcommon.NewValueStr(metadata.AttributeStateBuffered.String()))
  207. internal.AssertGaugeMetricHasAttributeValue(t, metric, 3, "state",
  208. pcommon.NewValueStr(metadata.AttributeStateCached.String()))
  209. internal.AssertGaugeMetricHasAttributeValue(t, metric, 4, "state",
  210. pcommon.NewValueStr(metadata.AttributeStateSlabReclaimable.String()))
  211. internal.AssertGaugeMetricHasAttributeValue(t, metric, 5, "state",
  212. pcommon.NewValueStr(metadata.AttributeStateSlabUnreclaimable.String()))
  213. }