Support any OS in host metrics receiver & cpu scraper, and include all relevant cpu states for Linux (#902)

This commit is contained in:
James Bebbington 2020-05-03 11:39:28 +10:00 committed by GitHub
parent 24c1569e97
commit 3a51f6090d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 108 additions and 47 deletions

View File

@ -16,9 +16,7 @@ package hostmetricsreceiver
import (
"context"
"errors"
"fmt"
"runtime"
"time"
"github.com/spf13/viper"
@ -143,11 +141,6 @@ func (f *Factory) CreateMetricsReceiver(
cfg configmodels.Receiver,
consumer consumer.MetricsConsumer,
) (component.MetricsReceiver, error) {
if runtime.GOOS != "windows" {
return nil, errors.New("hostmetrics receiver is currently only supported on windows")
}
config := cfg.(*Config)
hmr, err := NewHostMetricsReceiver(ctx, params.Logger, config, f.scraperFactories, consumer)

View File

@ -16,7 +16,6 @@ package hostmetricsreceiver
import (
"context"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
@ -47,11 +46,6 @@ func TestCreateReceiver(t *testing.T) {
mReceiver, err := factory.CreateMetricsReceiver(context.Background(), creationParams, cfg, nil)
if runtime.GOOS != "windows" {
assert.NotNil(t, err)
assert.Nil(t, tReceiver)
} else {
assert.Nil(t, err)
assert.NotNil(t, mReceiver)
}
assert.Nil(t, err)
assert.NotNil(t, mReceiver)
}

View File

@ -49,11 +49,6 @@ func TestGatherMetrics_EndToEnd(t *testing.T) {
receiver, err := NewHostMetricsReceiver(context.Background(), zap.NewNop(), config, factories, sink)
if runtime.GOOS != "windows" {
require.Error(t, err, "Expected error when creating a host metrics receiver with cpuscraper collector on a non-windows environment")
return
}
require.NoError(t, err, "Failed to create metrics receiver: %v", err)
err = receiver.Start(context.Background(), componenttest.NewNopHost())
@ -77,10 +72,10 @@ func assertMetricData(t *testing.T, got []pdata.Metrics) {
// expect 1 metric
assert.Equal(t, 1, metrics.Len())
// for cpu seconds metric, expect 5 timeseries with appropriate labels
// for cpu seconds metric, expect a datapoint for each state label & core combination with at least 4 standard states
hostCPUTimeMetric := metrics.At(0)
internal.AssertDescriptorEqual(t, cpuscraper.MetricCPUSecondsDescriptor, hostCPUTimeMetric.MetricDescriptor())
assert.Equal(t, 4*runtime.NumCPU(), hostCPUTimeMetric.Int64DataPoints().Len())
assert.GreaterOrEqual(t, hostCPUTimeMetric.Int64DataPoints().Len(), runtime.NumCPU()*4)
internal.AssertInt64MetricLabelExists(t, hostCPUTimeMetric, 0, cpuscraper.CPULabel)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 0, cpuscraper.StateLabel, cpuscraper.UserStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 1, cpuscraper.StateLabel, cpuscraper.SystemStateLabelValue)

View File

@ -30,6 +30,10 @@ var (
SystemStateLabelValue = "system"
IdleStateLabelValue = "idle"
InterruptStateLabelValue = "interrupt"
NiceStateLabelValue = "nice"
SoftIRQStateLabelValue = "softirq"
StealStateLabelValue = "steal"
WaitStateLabelValue = "wait"
)
var MetricCPUSecondsDescriptor = metricCPUSecondsDescriptor()

View File

@ -114,12 +114,9 @@ func initializeCPUSecondsMetric(metric pdata.Metric, startTime pdata.TimestampUn
MetricCPUSecondsDescriptor.CopyTo(metric.MetricDescriptor())
idps := metric.Int64DataPoints()
idps.Resize(4 * len(cpuTimes))
idps.Resize(len(cpuTimes) * cpuStatesLen)
for i, cpuTime := range cpuTimes {
initializeCPUSecondsDataPoint(idps.At(4*i+0), startTime, cpuTime.CPU, UserStateLabelValue, int64(cpuTime.User))
initializeCPUSecondsDataPoint(idps.At(4*i+1), startTime, cpuTime.CPU, SystemStateLabelValue, int64(cpuTime.System))
initializeCPUSecondsDataPoint(idps.At(4*i+2), startTime, cpuTime.CPU, IdleStateLabelValue, int64(cpuTime.Idle))
initializeCPUSecondsDataPoint(idps.At(4*i+3), startTime, cpuTime.CPU, InterruptStateLabelValue, int64(cpuTime.Irq))
appendCPUStateTimes(idps, i*cpuStatesLen, startTime, cpuTime)
}
}

View File

@ -0,0 +1,36 @@
// Copyright 2020, 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
package cpuscraper
import (
"github.com/shirou/gopsutil/cpu"
"github.com/open-telemetry/opentelemetry-collector/consumer/pdata"
)
const cpuStatesLen = 8
func appendCPUStateTimes(idps pdata.Int64DataPointSlice, startIdx int, startTime pdata.TimestampUnixNano, cpuTime cpu.TimesStat) {
initializeCPUSecondsDataPoint(idps.At(startIdx+0), startTime, cpuTime.CPU, UserStateLabelValue, int64(cpuTime.User))
initializeCPUSecondsDataPoint(idps.At(startIdx+1), startTime, cpuTime.CPU, SystemStateLabelValue, int64(cpuTime.System))
initializeCPUSecondsDataPoint(idps.At(startIdx+2), startTime, cpuTime.CPU, IdleStateLabelValue, int64(cpuTime.Idle))
initializeCPUSecondsDataPoint(idps.At(startIdx+3), startTime, cpuTime.CPU, InterruptStateLabelValue, int64(cpuTime.Irq))
initializeCPUSecondsDataPoint(idps.At(startIdx+4), startTime, cpuTime.CPU, NiceStateLabelValue, int64(cpuTime.Nice))
initializeCPUSecondsDataPoint(idps.At(startIdx+5), startTime, cpuTime.CPU, SoftIRQStateLabelValue, int64(cpuTime.Softirq))
initializeCPUSecondsDataPoint(idps.At(startIdx+6), startTime, cpuTime.CPU, StealStateLabelValue, int64(cpuTime.Steal))
initializeCPUSecondsDataPoint(idps.At(startIdx+7), startTime, cpuTime.CPU, WaitStateLabelValue, int64(cpuTime.Iowait))
}

View File

@ -0,0 +1,32 @@
// Copyright 2020, 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
package cpuscraper
import (
"github.com/shirou/gopsutil/cpu"
"github.com/open-telemetry/opentelemetry-collector/consumer/pdata"
)
const cpuStatesLen = 4
func appendCPUStateTimes(idps pdata.Int64DataPointSlice, startIdx int, startTime pdata.TimestampUnixNano, cpuTime cpu.TimesStat) {
initializeCPUSecondsDataPoint(idps.At(startIdx+0), startTime, cpuTime.CPU, UserStateLabelValue, int64(cpuTime.User))
initializeCPUSecondsDataPoint(idps.At(startIdx+1), startTime, cpuTime.CPU, SystemStateLabelValue, int64(cpuTime.System))
initializeCPUSecondsDataPoint(idps.At(startIdx+2), startTime, cpuTime.CPU, IdleStateLabelValue, int64(cpuTime.Idle))
initializeCPUSecondsDataPoint(idps.At(startIdx+3), startTime, cpuTime.CPU, InterruptStateLabelValue, int64(cpuTime.Irq))
}

View File

@ -37,10 +37,10 @@ func TestScrapeMetrics_MinimalData(t *testing.T) {
// expect 1 metric
assert.Equal(t, 1, metrics.Len())
// for cpu seconds metric, expect 4 timeseries with appropriate labels
// for cpu seconds metric, expect a datapoint for each state label, including at least 4 standard states
hostCPUTimeMetric := metrics.At(0)
internal.AssertDescriptorEqual(t, MetricCPUSecondsDescriptor, hostCPUTimeMetric.MetricDescriptor())
assert.Equal(t, 4, hostCPUTimeMetric.Int64DataPoints().Len())
assert.GreaterOrEqual(t, hostCPUTimeMetric.Int64DataPoints().Len(), 4)
internal.AssertInt64MetricLabelDoesNotExist(t, hostCPUTimeMetric, 0, CPULabel)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 0, StateLabel, UserStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 1, StateLabel, SystemStateLabelValue)
@ -60,10 +60,10 @@ func TestScrapeMetrics_AllData(t *testing.T) {
// expect 1 metric
assert.Equal(t, 1, metrics.Len())
// for cpu seconds metric, expect 4*cores timeseries with appropriate labels
// for cpu seconds metric, expect a datapoint for each state label & core combination with at least 4 standard states
hostCPUTimeMetric := metrics.At(0)
internal.AssertDescriptorEqual(t, MetricCPUSecondsDescriptor, hostCPUTimeMetric.MetricDescriptor())
assert.Equal(t, 4*runtime.NumCPU(), hostCPUTimeMetric.Int64DataPoints().Len())
assert.GreaterOrEqual(t, hostCPUTimeMetric.Int64DataPoints().Len(), runtime.NumCPU()*4)
internal.AssertInt64MetricLabelExists(t, hostCPUTimeMetric, 0, CPULabel)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 0, StateLabel, UserStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 1, StateLabel, SystemStateLabelValue)
@ -72,6 +72,30 @@ func TestScrapeMetrics_AllData(t *testing.T) {
})
}
func TestScrapeMetrics_Linux(t *testing.T) {
if runtime.GOOS != "linux" {
return
}
createScraperAndValidateScrapedMetrics(t, &Config{}, func(t *testing.T, got []pdata.Metrics) {
metrics := internal.AssertSingleMetricDataAndGetMetricsSlice(t, got)
// for cpu seconds metric, expect a datapoint for all 8 state labels
hostCPUTimeMetric := metrics.At(0)
internal.AssertDescriptorEqual(t, MetricCPUSecondsDescriptor, hostCPUTimeMetric.MetricDescriptor())
assert.Equal(t, 8, hostCPUTimeMetric.Int64DataPoints().Len())
internal.AssertInt64MetricLabelDoesNotExist(t, hostCPUTimeMetric, 0, CPULabel)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 0, StateLabel, UserStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 1, StateLabel, SystemStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 2, StateLabel, IdleStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 3, StateLabel, InterruptStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 4, StateLabel, NiceStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 5, StateLabel, SoftIRQStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 6, StateLabel, StealStateLabelValue)
internal.AssertInt64MetricLabelHasValue(t, hostCPUTimeMetric, 7, StateLabel, WaitStateLabelValue)
})
}
func createScraperAndValidateScrapedMetrics(t *testing.T, config *Config, assertFn validationFn) {
config.SetCollectionInterval(100 * time.Millisecond)

View File

@ -16,8 +16,6 @@ package cpuscraper
import (
"context"
"errors"
"runtime"
"go.uber.org/zap"
@ -55,11 +53,6 @@ func (f *Factory) CreateMetricsScraper(
config internal.Config,
consumer consumer.MetricsConsumer,
) (internal.Scraper, error) {
if runtime.GOOS != "windows" {
return nil, errors.New("cpu scraper is currently only supported on windows")
}
cfg := config.(*Config)
return NewCPUScraper(ctx, cfg, consumer)
}

View File

@ -16,7 +16,6 @@ package cpuscraper
import (
"context"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
@ -29,12 +28,6 @@ func TestCreateMetricsScraper(t *testing.T) {
scraper, err := factory.CreateMetricsScraper(context.Background(), zap.NewNop(), cfg, nil)
switch os := runtime.GOOS; os {
case "windows":
assert.Nil(t, err)
assert.NotNil(t, scraper)
default:
assert.NotNil(t, err)
assert.Nil(t, scraper)
}
assert.Nil(t, err)
assert.NotNil(t, scraper)
}