Skip to content

Commit

Permalink
[hostmetricsreceiver] Set receiver.hostmetrics.normalizeProcessCPUUti…
Browse files Browse the repository at this point in the history
…lization feature gate as stable (#34764)

**Description:** receiver.hostmetrics.normalizeProcessCPUUtilization
feature gate

**Link to tracking Issue:** #34763

---------

Signed-off-by: Israel Blancas <iblancasa@gmail.com>
Signed-off-by: Israel Blancas <iblancas@redhat.com>
Co-authored-by: Pablo Baeyens <pbaeyens31+github@gmail.com>
  • Loading branch information
iblancasa and mx-psi committed Sep 18, 2024
1 parent 715f1dd commit bb96a89
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: deprecation

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: hostmetricsreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Set the receiver.hostmetrics.normalizeProcessCPUUtilization feature gate to stable.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [34763]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
16 changes: 0 additions & 16 deletions receiver/hostmetricsreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,19 +201,3 @@ export OTEL_RESOURCE_ATTRIBUTES="service.name=<the name of your service>,service
## Entity Events
**Entity Events as logs are experimental** and might eventually be replaced by the result of [the OTEP](https://github.com/open-telemetry/oteps/blob/main/text/entities/0256-entities-data-model.md#entity-events). For now, the hostmetrics receiver can send the host entity event as a log records. By default, the hostmetrics receiver sends periodic EntityState events every 5 minutes. You can change that by setting `metadata_collection_interval`. Entity Events as logs are experimental. The result of the OTEP might eventually replace that.
## Feature Gates
See the [Collector feature gates](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md#collector-feature-gates) for an overview of feature gates in the collector.
### `receiver.hostmetrics.normalizeProcessCPUUtilization`
When enabled, normalizes the `process.cpu.utilization` metric onto the interval [0-1] by dividing the value by the number of logical processors. With this feature gate disabled, the value of the `process.cpu.utilization` metric may exceed 1.
For example, if you have 4 logical cores on your system, and a process is occupying 2 logical cores for an entire scrape interval, with this feature gate disabled a `process.cpu.utilization` metric will be emitted with a value of 2. if this feature gate is enabled in the same scenario, the value of the emitted metric will be 0.5.
The schedule for this feature gate is:
- Introduced in v0.97.0 (March 2024) as `alpha` - disabled by default.
- Moved to `beta` in v0.100.0 (May 2024) - enabled by default.
- Moved to `stable` in v0.102.0 (June 2024) - cannot be disabled.
- Removed three releases after `stable`.
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,24 @@
package ucal // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/hostmetricsreceiver/internal/scraper/processscraper/ucal"

import (
"fmt"
"time"

"github.com/shirou/gopsutil/v4/cpu"
"go.opentelemetry.io/collector/featuregate"
"go.opentelemetry.io/collector/pdata/pcommon"
)

var normalizeProcessCPUUtilizationFeatureGate = featuregate.GlobalRegistry().MustRegister(
"receiver.hostmetrics.normalizeProcessCPUUtilization",
featuregate.StageBeta,
featuregate.WithRegisterDescription("When enabled, normalizes the process.cpu.utilization metric onto the interval [0-1] by dividing the value by the number of logical processors."),
featuregate.WithRegisterFromVersion("v0.97.0"),
featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/31368"),
)
func init() {
_ = featuregate.GlobalRegistry().MustRegister(
"receiver.hostmetrics.normalizeProcessCPUUtilization",
featuregate.StageStable,
featuregate.WithRegisterDescription("When enabled, normalizes the process.cpu.utilization metric onto the interval [0-1] by dividing the value by the number of logical processors."),
featuregate.WithRegisterFromVersion("v0.97.0"),
featuregate.WithRegisterToVersion("v0.112.0"),
featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/31368"),
)
}

// CPUUtilization stores the utilization percents [0-1] for the different cpu states
type CPUUtilization struct {
Expand All @@ -37,6 +41,10 @@ type CPUUtilizationCalculator struct {
// stored []cpu.TimesStat and time.Time and current []cpu.TimesStat and current time.Time
// If no previous data is stored it will return empty slice of CPUUtilization and no error
func (c *CPUUtilizationCalculator) CalculateAndRecord(now pcommon.Timestamp, logicalCores int, currentCPUStats *cpu.TimesStat, recorder func(pcommon.Timestamp, CPUUtilization)) error {
if logicalCores < 1 {
return fmt.Errorf("number of logical cores is %d", logicalCores)
}

if c.previousCPUStats != nil {
recorder(now, cpuUtilization(logicalCores, c.previousCPUStats, c.previousReadTime, currentCPUStats, now))
}
Expand All @@ -57,12 +65,9 @@ func cpuUtilization(logicalCores int, startStats *cpu.TimesStat, startTime pcomm
systemUtilization := (endStats.System - startStats.System) / elapsedTime
ioWaitUtilization := (endStats.Iowait - startStats.Iowait) / elapsedTime

if normalizeProcessCPUUtilizationFeatureGate.IsEnabled() && logicalCores > 0 {
// Normalize onto the [0-1] interval by dividing by the number of logical cores
userUtilization /= float64(logicalCores)
systemUtilization /= float64(logicalCores)
ioWaitUtilization /= float64(logicalCores)
}
userUtilization /= float64(logicalCores)
systemUtilization /= float64(logicalCores)
ioWaitUtilization /= float64(logicalCores)

return CPUUtilization{
User: userUtilization,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (

"github.com/shirou/gopsutil/v4/cpu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/featuregate"
"go.opentelemetry.io/collector/pdata/pcommon"
)

Expand All @@ -32,7 +30,7 @@ func TestCpuUtilizationCalculator_Calculate(t *testing.T) {
previousReadTime pcommon.Timestamp
previousCPUStat *cpu.TimesStat
expectedUtilization *CPUUtilization
normalize bool
shouldError bool
}{
{
name: "no previous times",
Expand Down Expand Up @@ -96,28 +94,6 @@ func TestCpuUtilizationCalculator_Calculate(t *testing.T) {
System: 0.15,
Iowait: 0.0005,
},
normalize: true,
},
{
name: "one second time delta, 2 logical cores, not normalized",
logicalCores: 2,
previousReadTime: 1640097430772858000,
currentReadTime: 1640097431772858000,
previousCPUStat: &cpu.TimesStat{
User: 8258.4,
System: 6193.3,
Iowait: 34.201,
},
currentCPUStat: &cpu.TimesStat{
User: 8258.5,
System: 6193.6,
Iowait: 34.202,
},
expectedUtilization: &CPUUtilization{
User: 0.1,
System: 0.3,
Iowait: 0.001,
},
},
{
name: "0 logical cores",
Expand All @@ -134,27 +110,27 @@ func TestCpuUtilizationCalculator_Calculate(t *testing.T) {
System: 6193.6,
Iowait: 34.202,
},
expectedUtilization: &CPUUtilization{
User: 0.1,
System: 0.3,
Iowait: 0.001,
},
shouldError: true,
},
}
for _, test := range testCases {
test := test
t.Run(test.name, func(t *testing.T) {
setNormalizeProcessCPUUtilizationFeatureGate(t, test.normalize)
recorder := inMemoryRecorder{}
calculator := CPUUtilizationCalculator{
previousReadTime: test.previousReadTime,
previousCPUStats: test.previousCPUStat,
}
err := calculator.CalculateAndRecord(test.currentReadTime, test.logicalCores, test.currentCPUStat, recorder.record)
assert.NoError(t, err)
assert.InDelta(t, test.expectedUtilization.System, recorder.cpuUtilization.System, 0.00001)
assert.InDelta(t, test.expectedUtilization.User, recorder.cpuUtilization.User, 0.00001)
assert.InDelta(t, test.expectedUtilization.Iowait, recorder.cpuUtilization.Iowait, 0.00001)
if test.shouldError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.InDelta(t, test.expectedUtilization.System, recorder.cpuUtilization.System, 0.00001)
assert.InDelta(t, test.expectedUtilization.User, recorder.cpuUtilization.User, 0.00001)
assert.InDelta(t, test.expectedUtilization.Iowait, recorder.cpuUtilization.Iowait, 0.00001)
}

})
}
}
Expand Down Expand Up @@ -185,20 +161,3 @@ func Test_cpuUtilization(t *testing.T) {
assert.InDelta(t, expectedUtilization.Iowait, actualUtilization.Iowait, 0.00001)

}

func setNormalizeProcessCPUUtilizationFeatureGate(t *testing.T, val bool) {
wasEnabled := normalizeProcessCPUUtilizationFeatureGate.IsEnabled()
err := featuregate.GlobalRegistry().Set(
normalizeProcessCPUUtilizationFeatureGate.ID(),
val,
)
require.NoError(t, err)

t.Cleanup(func() {
err := featuregate.GlobalRegistry().Set(
normalizeProcessCPUUtilizationFeatureGate.ID(),
wasEnabled,
)
require.NoError(t, err)
})
}

0 comments on commit bb96a89

Please sign in to comment.