Skip to content

Commit c402d4c

Browse files
Added disk merged metric for Linux & updated tests
1 parent add8f61 commit c402d4c

File tree

6 files changed

+142
-34
lines changed

6 files changed

+142
-34
lines changed

receiver/hostmetricsreceiver/hostmetrics_receiver_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ var resourceMetrics = []string{
6666
}
6767

6868
var systemSpecificMetrics = map[string][]string{
69-
"linux": {"system.filesystem.inodes.usage", "system.swap.page_faults"},
69+
"linux": {"system.disk.merged", "system.filesystem.inodes.usage", "system.swap.page_faults"},
7070
"darwin": {"system.filesystem.inodes.usage", "system.swap.page_faults"},
7171
"freebsd": {"system.filesystem.inodes.usage", "system.swap.page_faults"},
7272
"openbsd": {"system.filesystem.inodes.usage", "system.swap.page_faults"},

receiver/hostmetricsreceiver/internal/scraper/diskscraper/disk_metadata.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,13 @@ var diskTimeDescriptor = func() pdata.MetricDescriptor {
6363
descriptor.SetType(pdata.MetricTypeMonotonicInt64)
6464
return descriptor
6565
}()
66+
67+
var diskMergedDescriptor = func() pdata.MetricDescriptor {
68+
descriptor := pdata.NewMetricDescriptor()
69+
descriptor.InitEmpty()
70+
descriptor.SetName("system.disk.merged")
71+
descriptor.SetDescription("The number of disk reads merged into single physical disk access operations.")
72+
descriptor.SetUnit("1")
73+
descriptor.SetType(pdata.MetricTypeMonotonicInt64)
74+
return descriptor
75+
}()

receiver/hostmetricsreceiver/internal/scraper/diskscraper/disk_scraper.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@ import (
2929
type scraper struct {
3030
config *Config
3131
startTime pdata.TimestampUnixNano
32+
33+
// for mocking gopsutil disk.IOCounters
34+
ioCounters func(names ...string) (map[string]disk.IOCountersStat, error)
3235
}
3336

3437
// newDiskScraper creates a Disk Scraper
3538
func newDiskScraper(_ context.Context, cfg *Config) *scraper {
36-
return &scraper{config: cfg}
39+
return &scraper{config: cfg, ioCounters: disk.IOCounters}
3740
}
3841

3942
// Initialize
@@ -59,19 +62,20 @@ func (s *scraper) ScrapeMetrics(ctx context.Context) (pdata.MetricSlice, error)
5962

6063
metrics := pdata.NewMetricSlice()
6164

62-
ioCounters, err := disk.IOCounters()
65+
ioCounters, err := s.ioCounters()
6366
if err != nil {
6467
return metrics, err
6568
}
6669

67-
metrics.Resize(3)
68-
initializeDiskIOMetric(metrics.At(0), ioCounters, s.startTime)
69-
initializeDiskOpsMetric(metrics.At(1), ioCounters, s.startTime)
70-
initializeDiskTimeMetric(metrics.At(2), ioCounters, s.startTime)
70+
metrics.Resize(3 + systemSpecificMetricsLen)
71+
initializeDiskIOMetric(metrics.At(0), s.startTime, ioCounters)
72+
initializeDiskOpsMetric(metrics.At(1), s.startTime, ioCounters)
73+
initializeDiskTimeMetric(metrics.At(2), s.startTime, ioCounters)
74+
appendSystemSpecificMetrics(metrics, 3, s.startTime, ioCounters)
7175
return metrics, nil
7276
}
7377

74-
func initializeDiskIOMetric(metric pdata.Metric, ioCounters map[string]disk.IOCountersStat, startTime pdata.TimestampUnixNano) {
78+
func initializeDiskIOMetric(metric pdata.Metric, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
7579
diskIODescriptor.CopyTo(metric.MetricDescriptor())
7680

7781
idps := metric.Int64DataPoints()
@@ -85,7 +89,7 @@ func initializeDiskIOMetric(metric pdata.Metric, ioCounters map[string]disk.IOCo
8589
}
8690
}
8791

88-
func initializeDiskOpsMetric(metric pdata.Metric, ioCounters map[string]disk.IOCountersStat, startTime pdata.TimestampUnixNano) {
92+
func initializeDiskOpsMetric(metric pdata.Metric, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
8993
diskOpsDescriptor.CopyTo(metric.MetricDescriptor())
9094

9195
idps := metric.Int64DataPoints()
@@ -99,7 +103,7 @@ func initializeDiskOpsMetric(metric pdata.Metric, ioCounters map[string]disk.IOC
99103
}
100104
}
101105

102-
func initializeDiskTimeMetric(metric pdata.Metric, ioCounters map[string]disk.IOCountersStat, startTime pdata.TimestampUnixNano) {
106+
func initializeDiskTimeMetric(metric pdata.Metric, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
103107
diskTimeDescriptor.CopyTo(metric.MetricDescriptor())
104108

105109
idps := metric.Int64DataPoints()
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// +build linux
16+
17+
package diskscraper
18+
19+
import (
20+
"github.com/shirou/gopsutil/disk"
21+
22+
"go.opentelemetry.io/collector/consumer/pdata"
23+
)
24+
25+
const systemSpecificMetricsLen = 1
26+
27+
func appendSystemSpecificMetrics(metrics pdata.MetricSlice, startIdx int, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
28+
metric := metrics.At(startIdx)
29+
diskMergedDescriptor.CopyTo(metric.MetricDescriptor())
30+
31+
idps := metric.Int64DataPoints()
32+
idps.Resize(2 * len(ioCounters))
33+
34+
idx := 0
35+
for device, ioCounter := range ioCounters {
36+
initializeDataPoint(idps.At(idx+0), startTime, device, readDirectionLabelValue, int64(ioCounter.MergedReadCount))
37+
initializeDataPoint(idps.At(idx+1), startTime, device, writeDirectionLabelValue, int64(ioCounter.MergedWriteCount))
38+
idx += 2
39+
}
40+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// +build !linux
16+
17+
package diskscraper
18+
19+
import (
20+
"github.com/shirou/gopsutil/disk"
21+
22+
"go.opentelemetry.io/collector/consumer/pdata"
23+
)
24+
25+
const systemSpecificMetricsLen = 0
26+
27+
func appendSystemSpecificMetrics(metrics pdata.MetricSlice, startIdx int, startTime pdata.TimestampUnixNano, ioCounters map[string]disk.IOCountersStat) {
28+
}

receiver/hostmetricsreceiver/internal/scraper/diskscraper/disk_scraper_test.go

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,70 @@ package diskscraper
1616

1717
import (
1818
"context"
19+
"errors"
20+
"runtime"
1921
"testing"
2022

23+
"github.com/shirou/gopsutil/disk"
2124
"github.com/stretchr/testify/assert"
2225
"github.com/stretchr/testify/require"
2326

2427
"go.opentelemetry.io/collector/consumer/pdata"
2528
"go.opentelemetry.io/collector/receiver/hostmetricsreceiver/internal"
2629
)
2730

28-
type validationFn func(*testing.T, pdata.MetricSlice)
29-
3031
func TestScrapeMetrics(t *testing.T) {
31-
createScraperAndValidateScrapedMetrics(t, &Config{}, func(t *testing.T, metrics pdata.MetricSlice) {
32-
// expect 3 metrics
33-
assert.Equal(t, 3, metrics.Len())
34-
35-
// for each disk metric, expect a read & write datapoint for at least one drive
36-
assertDiskMetricMatchesDescriptorAndHasReadAndWriteDataPoints(t, metrics.At(0), diskIODescriptor)
37-
assertDiskMetricMatchesDescriptorAndHasReadAndWriteDataPoints(t, metrics.At(1), diskOpsDescriptor)
38-
assertDiskMetricMatchesDescriptorAndHasReadAndWriteDataPoints(t, metrics.At(2), diskTimeDescriptor)
39-
})
32+
type testCase struct {
33+
name string
34+
ioCountersFunc func(names ...string) (map[string]disk.IOCountersStat, error)
35+
expectedErr string
36+
}
37+
38+
testCases := []testCase{
39+
{
40+
name: "Standard",
41+
},
42+
{
43+
name: "Error",
44+
ioCountersFunc: func(names ...string) (map[string]disk.IOCountersStat, error) { return nil, errors.New("err1") },
45+
expectedErr: "err1",
46+
},
47+
}
48+
49+
for _, test := range testCases {
50+
t.Run(test.name, func(t *testing.T) {
51+
scraper := newDiskScraper(context.Background(), &Config{})
52+
if test.ioCountersFunc != nil {
53+
scraper.ioCounters = test.ioCountersFunc
54+
}
55+
56+
err := scraper.Initialize(context.Background())
57+
require.NoError(t, err, "Failed to initialize disk scraper: %v", err)
58+
defer func() { assert.NoError(t, scraper.Close(context.Background())) }()
59+
60+
metrics, err := scraper.ScrapeMetrics(context.Background())
61+
if test.expectedErr != "" {
62+
assert.EqualError(t, err, test.expectedErr)
63+
return
64+
}
65+
require.NoError(t, err, "Failed to scrape metrics: %v", err)
66+
67+
assert.GreaterOrEqual(t, metrics.Len(), 3)
68+
69+
assertDiskMetricValid(t, metrics.At(0), diskIODescriptor)
70+
assertDiskMetricValid(t, metrics.At(1), diskOpsDescriptor)
71+
assertDiskMetricValid(t, metrics.At(2), diskTimeDescriptor)
72+
73+
if runtime.GOOS == "Linux" {
74+
assertDiskMetricValid(t, metrics.At(3), diskMergedDescriptor)
75+
}
76+
})
77+
}
4078
}
4179

42-
func assertDiskMetricMatchesDescriptorAndHasReadAndWriteDataPoints(t *testing.T, metric pdata.Metric, expectedDescriptor pdata.MetricDescriptor) {
80+
func assertDiskMetricValid(t *testing.T, metric pdata.Metric, expectedDescriptor pdata.MetricDescriptor) {
4381
internal.AssertDescriptorEqual(t, expectedDescriptor, metric.MetricDescriptor())
4482
assert.GreaterOrEqual(t, metric.Int64DataPoints().Len(), 2)
4583
internal.AssertInt64MetricLabelHasValue(t, metric, 0, directionLabelName, readDirectionLabelValue)
4684
internal.AssertInt64MetricLabelHasValue(t, metric, 1, directionLabelName, writeDirectionLabelValue)
4785
}
48-
49-
func createScraperAndValidateScrapedMetrics(t *testing.T, config *Config, assertFn validationFn) {
50-
scraper := newDiskScraper(context.Background(), config)
51-
err := scraper.Initialize(context.Background())
52-
require.NoError(t, err, "Failed to initialize disk scraper: %v", err)
53-
defer func() { assert.NoError(t, scraper.Close(context.Background())) }()
54-
55-
metrics, err := scraper.ScrapeMetrics(context.Background())
56-
require.NoError(t, err, "Failed to scrape metrics: %v", err)
57-
58-
assertFn(t, metrics)
59-
}

0 commit comments

Comments
 (0)