Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion collector/cpu_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,13 @@ func (c *cpuCollector) updateInfo(ch chan<- prometheus.Metric) error {
cpu.CacheSize)
}

// The cpufreq collector exports the same metric name with a different
// help string and label set. Only export the /proc/cpuinfo frequency
// when cpufreq is disabled or uses its own metric prefix.
cpuFreqEnabled, ok := collectorState["cpufreq"]
if !ok || cpuFreqEnabled == nil {
c.logger.Debug("cpufreq key missing or nil value in collectorState map")
} else if *cpuFreqEnabled {
} else if !*cpuFreqEnabled || *useCPUFreqPrefix {
for _, cpu := range info {
ch <- prometheus.MustNewConstMetric(c.cpuFrequencyHz,
prometheus.GaugeValue,
Expand Down
110 changes: 68 additions & 42 deletions collector/cpufreq_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,48 +16,74 @@
package collector

import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus/client_golang/prometheus"
)

var (
cpuFreqHertzDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "frequency_hertz"),
"Current CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
)
cpuFreqAvgDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "frequency_avg_hertz"),
"Average CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
)
cpuFreqMinDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "frequency_min_hertz"),
"Minimum CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
)
cpuFreqMaxDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "frequency_max_hertz"),
"Maximum CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
)
cpuFreqScalingFreqDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "scaling_frequency_hertz"),
"Current scaled CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
)
cpuFreqScalingFreqMinDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "scaling_frequency_min_hertz"),
"Minimum scaled CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
)
cpuFreqScalingFreqMaxDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "scaling_frequency_max_hertz"),
"Maximum scaled CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
)
cpuFreqScalingGovernorDesc = prometheus.NewDesc(
prometheus.BuildFQName(namespace, cpuCollectorSubsystem, "scaling_governor"),
"Current enabled CPU frequency governor.",
[]string{"cpu", "governor"}, nil,
)
)
const cpuFreqCollectorSubsystem = "cpufreq"

var useCPUFreqPrefix = kingpin.Flag("collector.cpufreq.enable-cpufreq-prefix",
"Expose cpufreq metrics with the node_cpufreq_ prefix instead of node_cpu_. This avoids a metric name collision with the cpu collector and will be the default behavior in 2.x.").Bool()

type cpuFreqDescs struct {
hertz *prometheus.Desc
avgHertz *prometheus.Desc
minHertz *prometheus.Desc
maxHertz *prometheus.Desc
scalingHertz *prometheus.Desc
scalingMinHertz *prometheus.Desc
scalingMaxHertz *prometheus.Desc
scalingGovernor *prometheus.Desc
}

// newCPUFreqDescs builds the cpufreq metric descriptions. It must run after
// flag parsing because the metric prefix depends on
// --collector.cpufreq.enable-cpufreq-prefix.
func newCPUFreqDescs() cpuFreqDescs {
subsystem := cpuCollectorSubsystem
if *useCPUFreqPrefix {
subsystem = cpuFreqCollectorSubsystem
}
return cpuFreqDescs{
hertz: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "frequency_hertz"),
"Current CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
),
avgHertz: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "frequency_avg_hertz"),
"Average CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
),
minHertz: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "frequency_min_hertz"),
"Minimum CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
),
maxHertz: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "frequency_max_hertz"),
"Maximum CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
),
scalingHertz: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "scaling_frequency_hertz"),
"Current scaled CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
),
scalingMinHertz: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "scaling_frequency_min_hertz"),
"Minimum scaled CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
),
scalingMaxHertz: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "scaling_frequency_max_hertz"),
"Maximum scaled CPU thread frequency in hertz.",
[]string{"cpu"}, nil,
),
scalingGovernor: prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "scaling_governor"),
"Current enabled CPU frequency governor.",
[]string{"cpu", "governor"}, nil,
),
}
}
18 changes: 10 additions & 8 deletions collector/cpufreq_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

type cpuFreqCollector struct {
fs sysfs.FS
descs cpuFreqDescs
logger *slog.Logger
}

Expand All @@ -42,6 +43,7 @@ func NewCPUFreqCollector(logger *slog.Logger) (Collector, error) {

return &cpuFreqCollector{
fs: fs,
descs: newCPUFreqDescs(),
logger: logger,
}, nil
}
Expand All @@ -58,55 +60,55 @@ func (c *cpuFreqCollector) Update(ch chan<- prometheus.Metric) error {
for _, stats := range cpuFreqs {
if stats.CpuinfoCurrentFrequency != nil {
ch <- prometheus.MustNewConstMetric(
cpuFreqHertzDesc,
c.descs.hertz,
prometheus.GaugeValue,
float64(*stats.CpuinfoCurrentFrequency)*1000.0,
stats.Name,
)
}
if stats.CpuinfoAverageFrequency != nil {
ch <- prometheus.MustNewConstMetric(
cpuFreqAvgDesc,
c.descs.avgHertz,
prometheus.GaugeValue,
float64(*stats.CpuinfoAverageFrequency)*1000.0,
stats.Name,
)
}
if stats.CpuinfoMinimumFrequency != nil {
ch <- prometheus.MustNewConstMetric(
cpuFreqMinDesc,
c.descs.minHertz,
prometheus.GaugeValue,
float64(*stats.CpuinfoMinimumFrequency)*1000.0,
stats.Name,
)
}
if stats.CpuinfoMaximumFrequency != nil {
ch <- prometheus.MustNewConstMetric(
cpuFreqMaxDesc,
c.descs.maxHertz,
prometheus.GaugeValue,
float64(*stats.CpuinfoMaximumFrequency)*1000.0,
stats.Name,
)
}
if stats.ScalingCurrentFrequency != nil {
ch <- prometheus.MustNewConstMetric(
cpuFreqScalingFreqDesc,
c.descs.scalingHertz,
prometheus.GaugeValue,
float64(*stats.ScalingCurrentFrequency)*1000.0,
stats.Name,
)
}
if stats.ScalingMinimumFrequency != nil {
ch <- prometheus.MustNewConstMetric(
cpuFreqScalingFreqMinDesc,
c.descs.scalingMinHertz,
prometheus.GaugeValue,
float64(*stats.ScalingMinimumFrequency)*1000.0,
stats.Name,
)
}
if stats.ScalingMaximumFrequency != nil {
ch <- prometheus.MustNewConstMetric(
cpuFreqScalingFreqMaxDesc,
c.descs.scalingMaxHertz,
prometheus.GaugeValue,
float64(*stats.ScalingMaximumFrequency)*1000.0,
stats.Name,
Expand All @@ -120,7 +122,7 @@ func (c *cpuFreqCollector) Update(ch chan<- prometheus.Metric) error {
state = 1
}
ch <- prometheus.MustNewConstMetric(
cpuFreqScalingGovernorDesc,
c.descs.scalingGovernor,
prometheus.GaugeValue,
float64(state),
stats.Name,
Expand Down
106 changes: 106 additions & 0 deletions collector/cpufreq_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright The Prometheus 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.

//go:build !nocpu

package collector

import (
"fmt"
"io"
"log/slog"
"strings"
"testing"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/testutil"
)

type testCPUFreqCollector struct {
mc Collector
}

func (c testCPUFreqCollector) Collect(ch chan<- prometheus.Metric) {
c.mc.Update(ch)
}

func (c testCPUFreqCollector) Describe(ch chan<- *prometheus.Desc) {
prometheus.DescribeByCollect(c, ch)
}

func newTestCPUFreqCollector(logger *slog.Logger) (prometheus.Collector, error) {
mc, err := NewCPUFreqCollector(logger)
if err != nil {
return testCPUFreqCollector{}, err
}
return &testCPUFreqCollector{mc}, nil
}

const cpuFreqFixtureMetrics = `# HELP node_%[1]s_scaling_frequency_hertz Current scaled CPU thread frequency in hertz.
# TYPE node_%[1]s_scaling_frequency_hertz gauge
node_%[1]s_scaling_frequency_hertz{cpu="0"} 1.699981e+09
node_%[1]s_scaling_frequency_hertz{cpu="1"} 1.699981e+09
node_%[1]s_scaling_frequency_hertz{cpu="2"} 8e+06
node_%[1]s_scaling_frequency_hertz{cpu="3"} 8e+06
# HELP node_%[1]s_scaling_frequency_max_hertz Maximum scaled CPU thread frequency in hertz.
# TYPE node_%[1]s_scaling_frequency_max_hertz gauge
node_%[1]s_scaling_frequency_max_hertz{cpu="0"} 3.7e+09
node_%[1]s_scaling_frequency_max_hertz{cpu="1"} 3.7e+09
node_%[1]s_scaling_frequency_max_hertz{cpu="2"} 4.2e+09
node_%[1]s_scaling_frequency_max_hertz{cpu="3"} 4.2e+09
# HELP node_%[1]s_scaling_frequency_min_hertz Minimum scaled CPU thread frequency in hertz.
# TYPE node_%[1]s_scaling_frequency_min_hertz gauge
node_%[1]s_scaling_frequency_min_hertz{cpu="0"} 8e+08
node_%[1]s_scaling_frequency_min_hertz{cpu="1"} 8e+08
node_%[1]s_scaling_frequency_min_hertz{cpu="2"} 1e+06
node_%[1]s_scaling_frequency_min_hertz{cpu="3"} 1e+06
# HELP node_%[1]s_scaling_governor Current enabled CPU frequency governor.
# TYPE node_%[1]s_scaling_governor gauge
node_%[1]s_scaling_governor{cpu="0",governor="performance"} 0
node_%[1]s_scaling_governor{cpu="0",governor="powersave"} 1
node_%[1]s_scaling_governor{cpu="1",governor="performance"} 0
node_%[1]s_scaling_governor{cpu="1",governor="powersave"} 1
node_%[1]s_scaling_governor{cpu="2",governor="performance"} 0
node_%[1]s_scaling_governor{cpu="2",governor="powersave"} 1
node_%[1]s_scaling_governor{cpu="3",governor="performance"} 0
node_%[1]s_scaling_governor{cpu="3",governor="powersave"} 1
`

func TestCPUFreqMetrics(t *testing.T) {
*sysPath = "fixtures/sys"

for _, tc := range []struct {
name string
cpufreqPrefix bool
subsystem string
}{
{name: "cpu prefix", cpufreqPrefix: false, subsystem: "cpu"},
{name: "cpufreq prefix", cpufreqPrefix: true, subsystem: "cpufreq"},
} {
t.Run(tc.name, func(t *testing.T) {
*useCPUFreqPrefix = tc.cpufreqPrefix
defer func() { *useCPUFreqPrefix = false }()

logger := slog.New(slog.NewTextHandler(io.Discard, nil))
collector, err := newTestCPUFreqCollector(logger)
if err != nil {
t.Fatal(err)
}

expected := fmt.Sprintf(cpuFreqFixtureMetrics, tc.subsystem)
if err := testutil.CollectAndCompare(collector, strings.NewReader(expected)); err != nil {
t.Fatal(err)
}
})
}
}
6 changes: 4 additions & 2 deletions collector/cpufreq_solaris.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
import "C"

type cpuFreqCollector struct {
descs cpuFreqDescs
logger *slog.Logger
}

Expand All @@ -37,6 +38,7 @@ func init() {

func NewCpuFreqCollector(logger *slog.Logger) (Collector, error) {
return &cpuFreqCollector{
descs: newCPUFreqDescs(),
logger: logger,
}, nil
}
Expand Down Expand Up @@ -68,14 +70,14 @@ func (c *cpuFreqCollector) Update(ch chan<- prometheus.Metric) error {

lcpu := strconv.Itoa(cpu)
ch <- prometheus.MustNewConstMetric(
cpuFreqHertzDesc,
c.descs.hertz,
prometheus.GaugeValue,
float64(cpuFreqV.UintVal),
lcpu,
)
// Multiply by 1e+6 to convert MHz to Hz.
ch <- prometheus.MustNewConstMetric(
cpuFreqMaxDesc,
c.descs.maxHertz,
prometheus.GaugeValue,
float64(cpuFreqMaxV.IntVal)*1e+6,
lcpu,
Expand Down
10 changes: 0 additions & 10 deletions collector/fixtures/e2e-output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -902,16 +902,6 @@ node_cpu_flag_info{flag="aes"} 1
node_cpu_flag_info{flag="avx"} 1
node_cpu_flag_info{flag="avx2"} 1
node_cpu_flag_info{flag="constant_tsc"} 1
# HELP node_cpu_frequency_hertz CPU frequency in hertz from /proc/cpuinfo.
# TYPE node_cpu_frequency_hertz gauge
node_cpu_frequency_hertz{core="0",cpu="0",package="0"} 7.99998e+08
node_cpu_frequency_hertz{core="0",cpu="4",package="0"} 7.99989e+08
node_cpu_frequency_hertz{core="1",cpu="1",package="0"} 8.00037e+08
node_cpu_frequency_hertz{core="1",cpu="5",package="0"} 8.00083e+08
node_cpu_frequency_hertz{core="2",cpu="2",package="0"} 8.0001e+08
node_cpu_frequency_hertz{core="2",cpu="6",package="0"} 8.00017e+08
node_cpu_frequency_hertz{core="3",cpu="3",package="0"} 8.00028e+08
node_cpu_frequency_hertz{core="3",cpu="7",package="0"} 8.0003e+08
# HELP node_cpu_guest_seconds_total Seconds the CPUs spent in guests (VMs) for each mode.
# TYPE node_cpu_guest_seconds_total counter
node_cpu_guest_seconds_total{cpu="0",mode="nice"} 0.01
Expand Down
Loading