Skip to content

Commit

Permalink
Switch to using perf counters to get disk io metrics on Windows (#1340)
Browse files Browse the repository at this point in the history
  • Loading branch information
james-bebbington authored Jul 15, 2020
1 parent 11e8cb0 commit 7eee24f
Show file tree
Hide file tree
Showing 9 changed files with 572 additions and 194 deletions.
11 changes: 5 additions & 6 deletions receiver/hostmetricsreceiver/hostmetrics_receiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ var standardMetrics = []string{
"system.memory.usage",
"system.disk.io",
"system.disk.ops",
"system.disk.time",
"system.filesystem.usage",
"system.cpu.load_average.1m",
"system.cpu.load_average.5m",
Expand All @@ -68,11 +67,11 @@ var resourceMetrics = []string{
}

var systemSpecificMetrics = map[string][]string{
"linux": {"system.disk.merged", "system.filesystem.inodes.usage", "system.processes.running", "system.processes.blocked", "system.swap.page_faults"},
"darwin": {"system.filesystem.inodes.usage", "system.processes.running", "system.processes.blocked", "system.swap.page_faults"},
"freebsd": {"system.filesystem.inodes.usage", "system.processes.running", "system.processes.blocked", "system.swap.page_faults"},
"openbsd": {"system.filesystem.inodes.usage", "system.processes.running", "system.processes.blocked", "system.swap.page_faults"},
"solaris": {"system.filesystem.inodes.usage", "system.swap.page_faults"},
"linux": {"system.disk.merged", "system.disk.time", "system.filesystem.inodes.usage", "system.processes.running", "system.processes.blocked", "system.swap.page_faults"},
"darwin": {"system.disk.time", "system.filesystem.inodes.usage", "system.processes.running", "system.processes.blocked", "system.swap.page_faults"},
"freebsd": {"system.disk.time", "system.filesystem.inodes.usage", "system.processes.running", "system.processes.blocked", "system.swap.page_faults"},
"openbsd": {"system.disk.time", "system.filesystem.inodes.usage", "system.processes.running", "system.processes.blocked", "system.swap.page_faults"},
"solaris": {"system.disk.time", "system.filesystem.inodes.usage", "system.swap.page_faults"},
}

var factories = map[string]internal.ScraperFactory{
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,115 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !linux
// +build !windows

package diskscraper

import (
"context"
"time"

"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/host"

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

const systemSpecificMetricsLen = 0
// scraper for Disk Metrics
type scraper struct {
config *Config
startTime pdata.TimestampUnixNano

// for mocking
bootTime func() (uint64, error)
ioCounters func(names ...string) (map[string]disk.IOCountersStat, error)
}

// newDiskScraper creates a Disk Scraper
func newDiskScraper(_ context.Context, cfg *Config) *scraper {
return &scraper{config: cfg, bootTime: host.BootTime, ioCounters: disk.IOCounters}
}

// Initialize
func (s *scraper) Initialize(_ context.Context) error {
bootTime, err := s.bootTime()
if err != nil {
return err
}

s.startTime = pdata.TimestampUnixNano(bootTime * 1e9)
return nil
}

// Close
func (s *scraper) Close(_ context.Context) error {
return nil
}

// ScrapeMetrics
func (s *scraper) ScrapeMetrics(_ context.Context) (pdata.MetricSlice, error) {
metrics := pdata.NewMetricSlice()

ioCounters, err := s.ioCounters()
if err != nil {
return metrics, err
}

metrics.Resize(3 + systemSpecificMetricsLen)
initializeDiskIOMetric(metrics.At(0), s.startTime, ioCounters)
initializeDiskOpsMetric(metrics.At(1), s.startTime, ioCounters)
initializeDiskTimeMetric(metrics.At(2), s.startTime, ioCounters)
appendSystemSpecificMetrics(metrics, 3, s.startTime, ioCounters)
return metrics, nil
}

func initializeDiskIOMetric(metric pdata.Metric, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
diskIODescriptor.CopyTo(metric.MetricDescriptor())

idps := metric.Int64DataPoints()
idps.Resize(2 * len(ioCounters))

idx := 0
for device, ioCounter := range ioCounters {
initializeDataPoint(idps.At(idx+0), startTime, device, readDirectionLabelValue, int64(ioCounter.ReadBytes))
initializeDataPoint(idps.At(idx+1), startTime, device, writeDirectionLabelValue, int64(ioCounter.WriteBytes))
idx += 2
}
}

func initializeDiskOpsMetric(metric pdata.Metric, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
diskOpsDescriptor.CopyTo(metric.MetricDescriptor())

idps := metric.Int64DataPoints()
idps.Resize(2 * len(ioCounters))

idx := 0
for device, ioCounter := range ioCounters {
initializeDataPoint(idps.At(idx+0), startTime, device, readDirectionLabelValue, int64(ioCounter.ReadCount))
initializeDataPoint(idps.At(idx+1), startTime, device, writeDirectionLabelValue, int64(ioCounter.WriteCount))
idx += 2
}
}

func initializeDiskTimeMetric(metric pdata.Metric, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
diskTimeDescriptor.CopyTo(metric.MetricDescriptor())

idps := metric.Int64DataPoints()
idps.Resize(2 * len(ioCounters))

idx := 0
for device, ioCounter := range ioCounters {
initializeDataPoint(idps.At(idx+0), startTime, device, readDirectionLabelValue, int64(ioCounter.ReadTime))
initializeDataPoint(idps.At(idx+1), startTime, device, writeDirectionLabelValue, int64(ioCounter.WriteTime))
idx += 2
}
}

func appendSystemSpecificMetrics(metrics pdata.MetricSlice, startIdx int, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
func initializeDataPoint(dataPoint pdata.Int64DataPoint, startTime pdata.TimestampUnixNano, deviceLabel string, directionLabel string, value int64) {
labelsMap := dataPoint.LabelsMap()
labelsMap.Insert(deviceLabelName, deviceLabel)
labelsMap.Insert(directionLabelName, directionLabel)
dataPoint.SetStartTime(startTime)
dataPoint.SetTimestamp(pdata.TimestampUnixNano(uint64(time.Now().UnixNano())))
dataPoint.SetValue(value)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !linux,!windows

package diskscraper

import (
"github.com/shirou/gopsutil/disk"

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

const systemSpecificMetricsLen = 0

func appendSystemSpecificMetrics(metrics pdata.MetricSlice, startIdx int, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !windows

package diskscraper

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

"github.com/shirou/gopsutil/disk"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

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

func TestScrapeMetrics_Others(t *testing.T) {
type testCase struct {
name string
bootTimeFunc func() (uint64, error)
ioCountersFunc func(names ...string) (map[string]disk.IOCountersStat, error)
expectedStartTime pdata.TimestampUnixNano
initializationErr string
expectedErr string
}

testCases := []testCase{
{
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",
},
{
name: "Error",
ioCountersFunc: func(names ...string) (map[string]disk.IOCountersStat, error) { return nil, errors.New("err2") },
expectedErr: "err2",
},
}

for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
scraper := newDiskScraper(context.Background(), &Config{})
if test.bootTimeFunc != nil {
scraper.bootTime = test.bootTimeFunc
}
if test.ioCountersFunc != nil {
scraper.ioCounters = test.ioCountersFunc
}

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

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

require.NoError(t, err, "Failed to scrape metrics: %v", err)

assertDiskMetricValid(t, metrics.At(0), diskIODescriptor, test.expectedStartTime)
assertDiskMetricValid(t, metrics.At(1), diskOpsDescriptor, test.expectedStartTime)
assertDiskMetricValid(t, metrics.At(2), diskTimeDescriptor, test.expectedStartTime)

if runtime.GOOS == "linux" {
assertDiskMetricValid(t, metrics.At(3), diskMergedDescriptor, test.expectedStartTime)
}
})
}
}
Loading

0 comments on commit 7eee24f

Please sign in to comment.