Skip to content

Commit

Permalink
Update hostmetrics swap scraper to use new perfcounters package (#1869)
Browse files Browse the repository at this point in the history
  • Loading branch information
james-bebbington authored Sep 28, 2020
1 parent 8f265cc commit 5d723ab
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 126 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,17 @@ import (
"github.com/shirou/gopsutil/mem"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/collector/consumer/pdata"
)

func TestScrapeMetrics_Errors(t *testing.T) {
type testCase struct {
name string
bootTimeFunc func() (uint64, error)
virtualMemoryFunc func() (*mem.VirtualMemoryStat, error)
swapMemoryFunc func() (*mem.SwapMemoryStat, error)
expectedStartTime pdata.TimestampUnixNano
initializationErr string
expectedError string
}

testCases := []testCase{
{
name: "bootTimeError",
bootTimeFunc: func() (uint64, error) { return 0, errors.New("err1") },
initializationErr: "err1",
},
{
name: "virtualMemoryError",
virtualMemoryFunc: func() (*mem.VirtualMemoryStat, error) { return nil, errors.New("err1") },
Expand All @@ -66,9 +56,6 @@ func TestScrapeMetrics_Errors(t *testing.T) {
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
scraper := newSwapScraper(context.Background(), &Config{})
if test.bootTimeFunc != nil {
scraper.bootTime = test.bootTimeFunc
}
if test.virtualMemoryFunc != nil {
scraper.virtualMemory = test.virtualMemoryFunc
}
Expand All @@ -77,23 +64,11 @@ func TestScrapeMetrics_Errors(t *testing.T) {
}

err := scraper.Initialize(context.Background())
if test.initializationErr != "" {
assert.EqualError(t, err, test.initializationErr)
return
}
require.NoError(t, err, "Failed to initialize swap scraper: %v", err)
defer func() { assert.NoError(t, scraper.Close(context.Background())) }()

metrics, err := scraper.ScrapeMetrics(context.Background())
if test.expectedError != "" {
assert.EqualError(t, err, test.expectedError)
return
}

assert.Equal(t, 3, metrics.Len())
assertSwapUsageMetricValid(t, metrics.At(0))
assertPagingMetricValid(t, metrics.At(1), test.expectedStartTime)
assertPageFaultsMetricValid(t, metrics.At(2), test.expectedStartTime)
_, err = scraper.ScrapeMetrics(context.Background())
assert.EqualError(t, err, test.expectedError)
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package swapscraper

import (
"context"
"errors"
"runtime"
"testing"

Expand All @@ -27,29 +28,64 @@ import (
)

func TestScrapeMetrics(t *testing.T) {
scraper := newSwapScraper(context.Background(), &Config{})
err := scraper.Initialize(context.Background())
require.NoError(t, err, "Failed to initialize swap scraper: %v", err)
defer func() { assert.NoError(t, scraper.Close(context.Background())) }()

metrics, err := scraper.ScrapeMetrics(context.Background())
assert.NoError(t, err)

// expect 3 metrics (windows does not currently support page_faults metric)
expectedMetrics := 3
if runtime.GOOS == "windows" {
expectedMetrics = 2
type testCase struct {
name string
bootTimeFunc func() (uint64, error)
expectedStartTime pdata.TimestampUnixNano
initializationErr string
}
assert.Equal(t, expectedMetrics, metrics.Len())

assertSwapUsageMetricValid(t, metrics.At(0))
internal.AssertSameTimeStampForMetrics(t, metrics, 0, 1)
testCases := []testCase{
{
name: "Standard",
},
{
name: "Validate Start Time",
bootTimeFunc: func() (uint64, error) { return 100, nil },
expectedStartTime: 100 * 1e9,
},
{
name: "Boot Time Error",
bootTimeFunc: func() (uint64, error) { return 0, errors.New("err1") },
initializationErr: "err1",
},
}

assertPagingMetricValid(t, metrics.At(1), 0)
if runtime.GOOS != "windows" {
assertPageFaultsMetricValid(t, metrics.At(2), 0)
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
scraper := newSwapScraper(context.Background(), &Config{})
if test.bootTimeFunc != nil {
scraper.bootTime = test.bootTimeFunc
}

err := scraper.Initialize(context.Background())
if test.initializationErr != "" {
assert.EqualError(t, err, test.initializationErr)
return
}
require.NoError(t, err, "Failed to initialize swap scraper: %v", err)
defer func() { assert.NoError(t, scraper.Close(context.Background())) }()

metrics, err := scraper.ScrapeMetrics(context.Background())
require.NoError(t, err)

// expect 3 metrics (windows does not currently support page_faults metric)
expectedMetrics := 3
if runtime.GOOS == "windows" {
expectedMetrics = 2
}
assert.Equal(t, expectedMetrics, metrics.Len())

assertSwapUsageMetricValid(t, metrics.At(0))
internal.AssertSameTimeStampForMetrics(t, metrics, 0, 1)

assertPagingMetricValid(t, metrics.At(1), test.expectedStartTime)
if runtime.GOOS != "windows" {
assertPageFaultsMetricValid(t, metrics.At(2), test.expectedStartTime)
}
internal.AssertSameTimeStampForMetrics(t, metrics, 1, metrics.Len())
})
}
internal.AssertSameTimeStampForMetrics(t, metrics, 1, metrics.Len())
}

func assertSwapUsageMetricValid(t *testing.T, hostSwapUsageMetric pdata.Metric) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,35 @@ package swapscraper

import (
"context"
"math"
"sync"
"time"

"github.com/shirou/gopsutil/host"

"go.opentelemetry.io/collector/component/componenterror"
"go.opentelemetry.io/collector/consumer/pdata"
"go.opentelemetry.io/collector/receiver/hostmetricsreceiver/internal"
"go.opentelemetry.io/collector/receiver/hostmetricsreceiver/internal/windows/pdh"
"go.opentelemetry.io/collector/receiver/hostmetricsreceiver/internal/perfcounters"
)

const (
pageReadsPerSecPath = `\Memory\Page Reads/sec`
pageWritesperSecPath = `\Memory\Page Writes/sec`
memory = "Memory"

pageReadsPerSec = "Page Reads/sec"
pageWritesPerSec = "Page Writes/sec"
)

// scraper for Swap Metrics
type scraper struct {
config *Config

pageReadsPerSecCounter pdh.PerfCounterScraper
pageWritesPerSecCounter pdh.PerfCounterScraper
config *Config
startTime pdata.TimestampUnixNano

pageSize uint64

startTime pdata.TimestampUnixNano
prevPagingScrapeTime time.Time
cumulativePageReads float64
cumulativePageWrites float64
perfCounterScraper perfcounters.PerfCounterScraper

// for mocking getPageFileStats
// for mocking
bootTime func() (uint64, error)
pageFileStats func() ([]*pageFileData, error)
}

Expand All @@ -60,44 +59,24 @@ var (
func newSwapScraper(_ context.Context, cfg *Config) *scraper {
once.Do(func() { pageSize = getPageSize() })

return &scraper{config: cfg, pageSize: pageSize, pageFileStats: getPageFileStats}
return &scraper{config: cfg, pageSize: pageSize, perfCounterScraper: &perfcounters.PerfLibScraper{}, bootTime: host.BootTime, pageFileStats: getPageFileStats}
}

// Initialize
func (s *scraper) Initialize(_ context.Context) error {
s.startTime = internal.TimeToUnixNano(time.Now())
s.prevPagingScrapeTime = time.Now()

var err error

s.pageReadsPerSecCounter, err = pdh.NewPerfCounter(pageReadsPerSecPath, true)
bootTime, err := s.bootTime()
if err != nil {
return err
}

s.pageWritesPerSecCounter, err = pdh.NewPerfCounter(pageWritesperSecPath, true)
if err != nil {
return err
}
s.startTime = pdata.TimestampUnixNano(bootTime * 1e9)

return nil
return s.perfCounterScraper.Initialize(memory)
}

// Close
func (s *scraper) Close(context.Context) error {
var errors []error

err := s.pageReadsPerSecCounter.Close()
if err != nil {
errors = append(errors, err)
}

err = s.pageWritesPerSecCounter.Close()
if err != nil {
errors = append(errors, err)
}

return componenterror.CombineErrors(errors)
return nil
}

// ScrapeMetrics
Expand Down Expand Up @@ -155,44 +134,46 @@ func initializeSwapUsageDataPoint(dataPoint pdata.IntDataPoint, now pdata.Timest
}

func (s *scraper) scrapeAndAppendPagingMetric(metrics pdata.MetricSlice) error {
now := time.Now()
durationSinceLastScraped := now.Sub(s.prevPagingScrapeTime).Seconds()
s.prevPagingScrapeTime = now
nowUnixTime := pdata.TimestampUnixNano(uint64(now.UnixNano()))
now := internal.TimeToUnixNano(time.Now())

counters, err := s.perfCounterScraper.Scrape()
if err != nil {
return err
}

pageReadsPerSecValues, err := s.pageReadsPerSecCounter.ScrapeData()
memoryObject, err := counters.GetObject(memory)
if err != nil {
return err
}

pageWritesPerSecValues, err := s.pageWritesPerSecCounter.ScrapeData()
memoryCounterValues, err := memoryObject.GetValues(pageReadsPerSec, pageWritesPerSec)
if err != nil {
return err
}

s.cumulativePageReads += (pageReadsPerSecValues[0].Value * durationSinceLastScraped)
s.cumulativePageWrites += (pageWritesPerSecValues[0].Value * durationSinceLastScraped)
if len(memoryCounterValues) > 0 {
idx := metrics.Len()
metrics.Resize(idx + 1)
initializePagingMetric(metrics.At(idx), s.startTime, now, memoryCounterValues[0])
}

idx := metrics.Len()
metrics.Resize(idx + 1)
initializePagingMetric(metrics.At(idx), s.startTime, nowUnixTime, s.cumulativePageReads, s.cumulativePageWrites)
return nil
}

func initializePagingMetric(metric pdata.Metric, startTime, now pdata.TimestampUnixNano, reads float64, writes float64) {
func initializePagingMetric(metric pdata.Metric, startTime, now pdata.TimestampUnixNano, memoryCounterValues *perfcounters.CounterValues) {
swapPagingDescriptor.CopyTo(metric)

idps := metric.IntSum().DataPoints()
idps.Resize(2)
initializePagingDataPoint(idps.At(0), startTime, now, inDirectionLabelValue, reads)
initializePagingDataPoint(idps.At(1), startTime, now, outDirectionLabelValue, writes)
initializePagingDataPoint(idps.At(0), startTime, now, inDirectionLabelValue, memoryCounterValues.Values[pageReadsPerSec])
initializePagingDataPoint(idps.At(1), startTime, now, outDirectionLabelValue, memoryCounterValues.Values[pageWritesPerSec])
}

func initializePagingDataPoint(dataPoint pdata.IntDataPoint, startTime, now pdata.TimestampUnixNano, directionLabel string, value float64) {
func initializePagingDataPoint(dataPoint pdata.IntDataPoint, startTime, now pdata.TimestampUnixNano, directionLabel string, value int64) {
labelsMap := dataPoint.LabelsMap()
labelsMap.Insert(typeLabelName, majorTypeLabelValue)
labelsMap.Insert(directionLabelName, directionLabel)
dataPoint.SetStartTime(startTime)
dataPoint.SetTimestamp(now)
dataPoint.SetValue(int64(math.Round(value)))
dataPoint.SetValue(value)
}
Loading

0 comments on commit 5d723ab

Please sign in to comment.