diff --git a/.chloggen/kubeletstats-uptime.yaml b/.chloggen/kubeletstats-uptime.yaml new file mode 100755 index 000000000000..8420b268a157 --- /dev/null +++ b/.chloggen/kubeletstats-uptime.yaml @@ -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: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: kubeletstatsreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add a new `uptime` metric for nodes, pods, and containers to track how many seconds have passed since the object started + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [25867] + +# (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: [] diff --git a/receiver/kubeletstatsreceiver/documentation.md b/receiver/kubeletstatsreceiver/documentation.md index ce731241a56b..97f5c83f90e4 100644 --- a/receiver/kubeletstatsreceiver/documentation.md +++ b/receiver/kubeletstatsreceiver/documentation.md @@ -376,6 +376,40 @@ The inodes used by the filesystem. This may not equal inodes - free because file | ---- | ----------- | ---------- | | 1 | Gauge | Int | +## Optional Metrics + +The following metrics are not emitted by default. Each of them can be enabled by applying the following configuration: + +```yaml +metrics: + : + enabled: true +``` + +### container.uptime + +The time since the container started + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| s | Sum | Int | Cumulative | true | + +### k8s.node.uptime + +The time since the node started + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| s | Sum | Int | Cumulative | true | + +### k8s.pod.uptime + +The time since the pod started + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | +| ---- | ----------- | ---------- | ----------------------- | --------- | +| s | Sum | Int | Cumulative | true | + ## Resource Attributes | Name | Description | Values | Enabled | diff --git a/receiver/kubeletstatsreceiver/internal/kubelet/accumulator.go b/receiver/kubeletstatsreceiver/internal/kubelet/accumulator.go index 78831593febe..858b969900cc 100644 --- a/receiver/kubeletstatsreceiver/internal/kubelet/accumulator.go +++ b/receiver/kubeletstatsreceiver/internal/kubelet/accumulator.go @@ -9,6 +9,7 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.uber.org/zap" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" stats "k8s.io/kubelet/pkg/apis/stats/v1alpha1" "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver/internal/metadata" @@ -41,12 +42,20 @@ type metricDataAccumulator struct { mbs *metadata.MetricsBuilders } +func addUptimeMetric(mb *metadata.MetricsBuilder, uptimeMetric metadata.RecordIntDataPointFunc, startTime v1.Time, currentTime pcommon.Timestamp) { + if !startTime.IsZero() { + value := int64(time.Since(startTime.Time).Seconds()) + uptimeMetric(mb, currentTime, value) + } +} + func (a *metricDataAccumulator) nodeStats(s stats.NodeStats) { if !a.metricGroupsToCollect[NodeMetricGroup] { return } currentTime := pcommon.NewTimestampFromTime(a.time) + addUptimeMetric(a.mbs.NodeMetricsBuilder, metadata.NodeUptimeMetrics.Uptime, s.StartTime, currentTime) addCPUMetrics(a.mbs.NodeMetricsBuilder, metadata.NodeCPUMetrics, s.CPU, currentTime) addMemoryMetrics(a.mbs.NodeMetricsBuilder, metadata.NodeMemoryMetrics, s.Memory, currentTime) addFilesystemMetrics(a.mbs.NodeMetricsBuilder, metadata.NodeFilesystemMetrics, s.Fs, currentTime) @@ -66,6 +75,7 @@ func (a *metricDataAccumulator) podStats(s stats.PodStats) { } currentTime := pcommon.NewTimestampFromTime(a.time) + addUptimeMetric(a.mbs.PodMetricsBuilder, metadata.PodUptimeMetrics.Uptime, s.StartTime, currentTime) addCPUMetrics(a.mbs.PodMetricsBuilder, metadata.PodCPUMetrics, s.CPU, currentTime) addMemoryMetrics(a.mbs.PodMetricsBuilder, metadata.PodMemoryMetrics, s.Memory, currentTime) addFilesystemMetrics(a.mbs.PodMetricsBuilder, metadata.PodFilesystemMetrics, s.EphemeralStorage, currentTime) @@ -98,6 +108,7 @@ func (a *metricDataAccumulator) containerStats(sPod stats.PodStats, s stats.Cont } currentTime := pcommon.NewTimestampFromTime(a.time) + addUptimeMetric(a.mbs.ContainerMetricsBuilder, metadata.ContainerUptimeMetrics.Uptime, s.StartTime, currentTime) addCPUMetrics(a.mbs.ContainerMetricsBuilder, metadata.ContainerCPUMetrics, s.CPU, currentTime) addMemoryMetrics(a.mbs.ContainerMetricsBuilder, metadata.ContainerMemoryMetrics, s.Memory, currentTime) addFilesystemMetrics(a.mbs.ContainerMetricsBuilder, metadata.ContainerFilesystemMetrics, s.Rootfs, currentTime) diff --git a/receiver/kubeletstatsreceiver/internal/kubelet/metrics_test.go b/receiver/kubeletstatsreceiver/internal/kubelet/metrics_test.go index 11701eb08db6..22b9ee9b681a 100644 --- a/receiver/kubeletstatsreceiver/internal/kubelet/metrics_test.go +++ b/receiver/kubeletstatsreceiver/internal/kubelet/metrics_test.go @@ -105,7 +105,7 @@ func requireResourceOk(t *testing.T, resource pcommon.Resource) { } func TestWorkingSetMem(t *testing.T) { - metrics := indexedFakeMetrics() + metrics := indexedFakeMetrics(fakeMetrics()) requireContains(t, metrics, "k8s.pod.memory.working_set") requireContains(t, metrics, "container.memory.working_set") @@ -115,7 +115,7 @@ func TestWorkingSetMem(t *testing.T) { } func TestPageFaults(t *testing.T) { - metrics := indexedFakeMetrics() + metrics := indexedFakeMetrics(fakeMetrics()) requireContains(t, metrics, "k8s.pod.memory.page_faults") requireContains(t, metrics, "container.memory.page_faults") @@ -125,7 +125,7 @@ func TestPageFaults(t *testing.T) { } func TestMajorPageFaults(t *testing.T) { - metrics := indexedFakeMetrics() + metrics := indexedFakeMetrics(fakeMetrics()) requireContains(t, metrics, "k8s.pod.memory.major_page_faults") requireContains(t, metrics, "container.memory.major_page_faults") @@ -134,8 +134,36 @@ func TestMajorPageFaults(t *testing.T) { require.Equal(t, int64(12), value) } +func TestUptime(t *testing.T) { + rc := &fakeRestClient{} + statsProvider := NewStatsProvider(rc) + summary, _ := statsProvider.StatsSummary() + mgs := map[MetricGroup]bool{ + ContainerMetricGroup: true, + PodMetricGroup: true, + NodeMetricGroup: true, + } + + cfg := metadata.DefaultMetricsBuilderConfig() + cfg.Metrics.K8sNodeUptime.Enabled = true + cfg.Metrics.K8sPodUptime.Enabled = true + cfg.Metrics.ContainerUptime.Enabled = true + + mbs := &metadata.MetricsBuilders{ + NodeMetricsBuilder: metadata.NewMetricsBuilder(cfg, receivertest.NewNopCreateSettings()), + PodMetricsBuilder: metadata.NewMetricsBuilder(cfg, receivertest.NewNopCreateSettings()), + ContainerMetricsBuilder: metadata.NewMetricsBuilder(cfg, receivertest.NewNopCreateSettings()), + } + + metrics := indexedFakeMetrics(MetricsData(zap.NewNop(), summary, Metadata{}, mgs, mbs)) + + requireContains(t, metrics, "k8s.node.uptime") + requireContains(t, metrics, "k8s.pod.uptime") + requireContains(t, metrics, "container.uptime") +} + func TestEmitMetrics(t *testing.T) { - metrics := indexedFakeMetrics() + metrics := indexedFakeMetrics(fakeMetrics()) metricNames := []string{ "k8s.node.network.io", "k8s.node.network.errors", @@ -158,8 +186,7 @@ func requireContains(t *testing.T, metrics map[string][]pmetric.Metric, metricNa require.True(t, found) } -func indexedFakeMetrics() map[string][]pmetric.Metric { - mds := fakeMetrics() +func indexedFakeMetrics(mds []pmetric.Metrics) map[string][]pmetric.Metric { metrics := make(map[string][]pmetric.Metric) for _, md := range mds { for i := 0; i < md.ResourceMetrics().Len(); i++ { diff --git a/receiver/kubeletstatsreceiver/internal/metadata/generated_config.go b/receiver/kubeletstatsreceiver/internal/metadata/generated_config.go index d6cf6f86d3c9..5ee2e99b88ce 100644 --- a/receiver/kubeletstatsreceiver/internal/metadata/generated_config.go +++ b/receiver/kubeletstatsreceiver/internal/metadata/generated_config.go @@ -36,6 +36,7 @@ type MetricsConfig struct { ContainerMemoryRss MetricConfig `mapstructure:"container.memory.rss"` ContainerMemoryUsage MetricConfig `mapstructure:"container.memory.usage"` ContainerMemoryWorkingSet MetricConfig `mapstructure:"container.memory.working_set"` + ContainerUptime MetricConfig `mapstructure:"container.uptime"` K8sNodeCPUTime MetricConfig `mapstructure:"k8s.node.cpu.time"` K8sNodeCPUUtilization MetricConfig `mapstructure:"k8s.node.cpu.utilization"` K8sNodeFilesystemAvailable MetricConfig `mapstructure:"k8s.node.filesystem.available"` @@ -49,6 +50,7 @@ type MetricsConfig struct { K8sNodeMemoryWorkingSet MetricConfig `mapstructure:"k8s.node.memory.working_set"` K8sNodeNetworkErrors MetricConfig `mapstructure:"k8s.node.network.errors"` K8sNodeNetworkIo MetricConfig `mapstructure:"k8s.node.network.io"` + K8sNodeUptime MetricConfig `mapstructure:"k8s.node.uptime"` K8sPodCPUTime MetricConfig `mapstructure:"k8s.pod.cpu.time"` K8sPodCPUUtilization MetricConfig `mapstructure:"k8s.pod.cpu.utilization"` K8sPodFilesystemAvailable MetricConfig `mapstructure:"k8s.pod.filesystem.available"` @@ -62,6 +64,7 @@ type MetricsConfig struct { K8sPodMemoryWorkingSet MetricConfig `mapstructure:"k8s.pod.memory.working_set"` K8sPodNetworkErrors MetricConfig `mapstructure:"k8s.pod.network.errors"` K8sPodNetworkIo MetricConfig `mapstructure:"k8s.pod.network.io"` + K8sPodUptime MetricConfig `mapstructure:"k8s.pod.uptime"` K8sVolumeAvailable MetricConfig `mapstructure:"k8s.volume.available"` K8sVolumeCapacity MetricConfig `mapstructure:"k8s.volume.capacity"` K8sVolumeInodes MetricConfig `mapstructure:"k8s.volume.inodes"` @@ -104,6 +107,9 @@ func DefaultMetricsConfig() MetricsConfig { ContainerMemoryWorkingSet: MetricConfig{ Enabled: true, }, + ContainerUptime: MetricConfig{ + Enabled: false, + }, K8sNodeCPUTime: MetricConfig{ Enabled: true, }, @@ -143,6 +149,9 @@ func DefaultMetricsConfig() MetricsConfig { K8sNodeNetworkIo: MetricConfig{ Enabled: true, }, + K8sNodeUptime: MetricConfig{ + Enabled: false, + }, K8sPodCPUTime: MetricConfig{ Enabled: true, }, @@ -182,6 +191,9 @@ func DefaultMetricsConfig() MetricsConfig { K8sPodNetworkIo: MetricConfig{ Enabled: true, }, + K8sPodUptime: MetricConfig{ + Enabled: false, + }, K8sVolumeAvailable: MetricConfig{ Enabled: true, }, diff --git a/receiver/kubeletstatsreceiver/internal/metadata/generated_config_test.go b/receiver/kubeletstatsreceiver/internal/metadata/generated_config_test.go index 3d2b0ff02228..c54a8f787935 100644 --- a/receiver/kubeletstatsreceiver/internal/metadata/generated_config_test.go +++ b/receiver/kubeletstatsreceiver/internal/metadata/generated_config_test.go @@ -37,6 +37,7 @@ func TestMetricsBuilderConfig(t *testing.T) { ContainerMemoryRss: MetricConfig{Enabled: true}, ContainerMemoryUsage: MetricConfig{Enabled: true}, ContainerMemoryWorkingSet: MetricConfig{Enabled: true}, + ContainerUptime: MetricConfig{Enabled: true}, K8sNodeCPUTime: MetricConfig{Enabled: true}, K8sNodeCPUUtilization: MetricConfig{Enabled: true}, K8sNodeFilesystemAvailable: MetricConfig{Enabled: true}, @@ -50,6 +51,7 @@ func TestMetricsBuilderConfig(t *testing.T) { K8sNodeMemoryWorkingSet: MetricConfig{Enabled: true}, K8sNodeNetworkErrors: MetricConfig{Enabled: true}, K8sNodeNetworkIo: MetricConfig{Enabled: true}, + K8sNodeUptime: MetricConfig{Enabled: true}, K8sPodCPUTime: MetricConfig{Enabled: true}, K8sPodCPUUtilization: MetricConfig{Enabled: true}, K8sPodFilesystemAvailable: MetricConfig{Enabled: true}, @@ -63,6 +65,7 @@ func TestMetricsBuilderConfig(t *testing.T) { K8sPodMemoryWorkingSet: MetricConfig{Enabled: true}, K8sPodNetworkErrors: MetricConfig{Enabled: true}, K8sPodNetworkIo: MetricConfig{Enabled: true}, + K8sPodUptime: MetricConfig{Enabled: true}, K8sVolumeAvailable: MetricConfig{Enabled: true}, K8sVolumeCapacity: MetricConfig{Enabled: true}, K8sVolumeInodes: MetricConfig{Enabled: true}, @@ -103,6 +106,7 @@ func TestMetricsBuilderConfig(t *testing.T) { ContainerMemoryRss: MetricConfig{Enabled: false}, ContainerMemoryUsage: MetricConfig{Enabled: false}, ContainerMemoryWorkingSet: MetricConfig{Enabled: false}, + ContainerUptime: MetricConfig{Enabled: false}, K8sNodeCPUTime: MetricConfig{Enabled: false}, K8sNodeCPUUtilization: MetricConfig{Enabled: false}, K8sNodeFilesystemAvailable: MetricConfig{Enabled: false}, @@ -116,6 +120,7 @@ func TestMetricsBuilderConfig(t *testing.T) { K8sNodeMemoryWorkingSet: MetricConfig{Enabled: false}, K8sNodeNetworkErrors: MetricConfig{Enabled: false}, K8sNodeNetworkIo: MetricConfig{Enabled: false}, + K8sNodeUptime: MetricConfig{Enabled: false}, K8sPodCPUTime: MetricConfig{Enabled: false}, K8sPodCPUUtilization: MetricConfig{Enabled: false}, K8sPodFilesystemAvailable: MetricConfig{Enabled: false}, @@ -129,6 +134,7 @@ func TestMetricsBuilderConfig(t *testing.T) { K8sPodMemoryWorkingSet: MetricConfig{Enabled: false}, K8sPodNetworkErrors: MetricConfig{Enabled: false}, K8sPodNetworkIo: MetricConfig{Enabled: false}, + K8sPodUptime: MetricConfig{Enabled: false}, K8sVolumeAvailable: MetricConfig{Enabled: false}, K8sVolumeCapacity: MetricConfig{Enabled: false}, K8sVolumeInodes: MetricConfig{Enabled: false}, diff --git a/receiver/kubeletstatsreceiver/internal/metadata/generated_metrics.go b/receiver/kubeletstatsreceiver/internal/metadata/generated_metrics.go index de324e254be0..1c5511b47c62 100644 --- a/receiver/kubeletstatsreceiver/internal/metadata/generated_metrics.go +++ b/receiver/kubeletstatsreceiver/internal/metadata/generated_metrics.go @@ -578,6 +578,57 @@ func newMetricContainerMemoryWorkingSet(cfg MetricConfig) metricContainerMemoryW return m } +type metricContainerUptime struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills container.uptime metric with initial data. +func (m *metricContainerUptime) init() { + m.data.SetName("container.uptime") + m.data.SetDescription("The time since the container started") + m.data.SetUnit("s") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) +} + +func (m *metricContainerUptime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricContainerUptime) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricContainerUptime) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricContainerUptime(cfg MetricConfig) metricContainerUptime { + m := metricContainerUptime{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricK8sNodeCPUTime struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -1227,6 +1278,57 @@ func newMetricK8sNodeNetworkIo(cfg MetricConfig) metricK8sNodeNetworkIo { return m } +type metricK8sNodeUptime struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.node.uptime metric with initial data. +func (m *metricK8sNodeUptime) init() { + m.data.SetName("k8s.node.uptime") + m.data.SetDescription("The time since the node started") + m.data.SetUnit("s") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) +} + +func (m *metricK8sNodeUptime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricK8sNodeUptime) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sNodeUptime) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sNodeUptime(cfg MetricConfig) metricK8sNodeUptime { + m := metricK8sNodeUptime{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricK8sPodCPUTime struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -1876,6 +1978,57 @@ func newMetricK8sPodNetworkIo(cfg MetricConfig) metricK8sPodNetworkIo { return m } +type metricK8sPodUptime struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills k8s.pod.uptime metric with initial data. +func (m *metricK8sPodUptime) init() { + m.data.SetName("k8s.pod.uptime") + m.data.SetDescription("The time since the pod started") + m.data.SetUnit("s") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) +} + +func (m *metricK8sPodUptime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricK8sPodUptime) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricK8sPodUptime) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricK8sPodUptime(cfg MetricConfig) metricK8sPodUptime { + m := metricK8sPodUptime{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + type metricK8sVolumeAvailable struct { data pmetric.Metric // data buffer for generated metric. config MetricConfig // metric config provided by user. @@ -2140,6 +2293,7 @@ type MetricsBuilder struct { metricContainerMemoryRss metricContainerMemoryRss metricContainerMemoryUsage metricContainerMemoryUsage metricContainerMemoryWorkingSet metricContainerMemoryWorkingSet + metricContainerUptime metricContainerUptime metricK8sNodeCPUTime metricK8sNodeCPUTime metricK8sNodeCPUUtilization metricK8sNodeCPUUtilization metricK8sNodeFilesystemAvailable metricK8sNodeFilesystemAvailable @@ -2153,6 +2307,7 @@ type MetricsBuilder struct { metricK8sNodeMemoryWorkingSet metricK8sNodeMemoryWorkingSet metricK8sNodeNetworkErrors metricK8sNodeNetworkErrors metricK8sNodeNetworkIo metricK8sNodeNetworkIo + metricK8sNodeUptime metricK8sNodeUptime metricK8sPodCPUTime metricK8sPodCPUTime metricK8sPodCPUUtilization metricK8sPodCPUUtilization metricK8sPodFilesystemAvailable metricK8sPodFilesystemAvailable @@ -2166,6 +2321,7 @@ type MetricsBuilder struct { metricK8sPodMemoryWorkingSet metricK8sPodMemoryWorkingSet metricK8sPodNetworkErrors metricK8sPodNetworkErrors metricK8sPodNetworkIo metricK8sPodNetworkIo + metricK8sPodUptime metricK8sPodUptime metricK8sVolumeAvailable metricK8sVolumeAvailable metricK8sVolumeCapacity metricK8sVolumeCapacity metricK8sVolumeInodes metricK8sVolumeInodes @@ -2200,6 +2356,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSetting metricContainerMemoryRss: newMetricContainerMemoryRss(mbc.Metrics.ContainerMemoryRss), metricContainerMemoryUsage: newMetricContainerMemoryUsage(mbc.Metrics.ContainerMemoryUsage), metricContainerMemoryWorkingSet: newMetricContainerMemoryWorkingSet(mbc.Metrics.ContainerMemoryWorkingSet), + metricContainerUptime: newMetricContainerUptime(mbc.Metrics.ContainerUptime), metricK8sNodeCPUTime: newMetricK8sNodeCPUTime(mbc.Metrics.K8sNodeCPUTime), metricK8sNodeCPUUtilization: newMetricK8sNodeCPUUtilization(mbc.Metrics.K8sNodeCPUUtilization), metricK8sNodeFilesystemAvailable: newMetricK8sNodeFilesystemAvailable(mbc.Metrics.K8sNodeFilesystemAvailable), @@ -2213,6 +2370,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSetting metricK8sNodeMemoryWorkingSet: newMetricK8sNodeMemoryWorkingSet(mbc.Metrics.K8sNodeMemoryWorkingSet), metricK8sNodeNetworkErrors: newMetricK8sNodeNetworkErrors(mbc.Metrics.K8sNodeNetworkErrors), metricK8sNodeNetworkIo: newMetricK8sNodeNetworkIo(mbc.Metrics.K8sNodeNetworkIo), + metricK8sNodeUptime: newMetricK8sNodeUptime(mbc.Metrics.K8sNodeUptime), metricK8sPodCPUTime: newMetricK8sPodCPUTime(mbc.Metrics.K8sPodCPUTime), metricK8sPodCPUUtilization: newMetricK8sPodCPUUtilization(mbc.Metrics.K8sPodCPUUtilization), metricK8sPodFilesystemAvailable: newMetricK8sPodFilesystemAvailable(mbc.Metrics.K8sPodFilesystemAvailable), @@ -2226,6 +2384,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSetting metricK8sPodMemoryWorkingSet: newMetricK8sPodMemoryWorkingSet(mbc.Metrics.K8sPodMemoryWorkingSet), metricK8sPodNetworkErrors: newMetricK8sPodNetworkErrors(mbc.Metrics.K8sPodNetworkErrors), metricK8sPodNetworkIo: newMetricK8sPodNetworkIo(mbc.Metrics.K8sPodNetworkIo), + metricK8sPodUptime: newMetricK8sPodUptime(mbc.Metrics.K8sPodUptime), metricK8sVolumeAvailable: newMetricK8sVolumeAvailable(mbc.Metrics.K8sVolumeAvailable), metricK8sVolumeCapacity: newMetricK8sVolumeCapacity(mbc.Metrics.K8sVolumeCapacity), metricK8sVolumeInodes: newMetricK8sVolumeInodes(mbc.Metrics.K8sVolumeInodes), @@ -2303,6 +2462,7 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricContainerMemoryRss.emit(ils.Metrics()) mb.metricContainerMemoryUsage.emit(ils.Metrics()) mb.metricContainerMemoryWorkingSet.emit(ils.Metrics()) + mb.metricContainerUptime.emit(ils.Metrics()) mb.metricK8sNodeCPUTime.emit(ils.Metrics()) mb.metricK8sNodeCPUUtilization.emit(ils.Metrics()) mb.metricK8sNodeFilesystemAvailable.emit(ils.Metrics()) @@ -2316,6 +2476,7 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricK8sNodeMemoryWorkingSet.emit(ils.Metrics()) mb.metricK8sNodeNetworkErrors.emit(ils.Metrics()) mb.metricK8sNodeNetworkIo.emit(ils.Metrics()) + mb.metricK8sNodeUptime.emit(ils.Metrics()) mb.metricK8sPodCPUTime.emit(ils.Metrics()) mb.metricK8sPodCPUUtilization.emit(ils.Metrics()) mb.metricK8sPodFilesystemAvailable.emit(ils.Metrics()) @@ -2329,6 +2490,7 @@ func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { mb.metricK8sPodMemoryWorkingSet.emit(ils.Metrics()) mb.metricK8sPodNetworkErrors.emit(ils.Metrics()) mb.metricK8sPodNetworkIo.emit(ils.Metrics()) + mb.metricK8sPodUptime.emit(ils.Metrics()) mb.metricK8sVolumeAvailable.emit(ils.Metrics()) mb.metricK8sVolumeCapacity.emit(ils.Metrics()) mb.metricK8sVolumeInodes.emit(ils.Metrics()) @@ -2409,6 +2571,11 @@ func (mb *MetricsBuilder) RecordContainerMemoryWorkingSetDataPoint(ts pcommon.Ti mb.metricContainerMemoryWorkingSet.recordDataPoint(mb.startTime, ts, val) } +// RecordContainerUptimeDataPoint adds a data point to container.uptime metric. +func (mb *MetricsBuilder) RecordContainerUptimeDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricContainerUptime.recordDataPoint(mb.startTime, ts, val) +} + // RecordK8sNodeCPUTimeDataPoint adds a data point to k8s.node.cpu.time metric. func (mb *MetricsBuilder) RecordK8sNodeCPUTimeDataPoint(ts pcommon.Timestamp, val float64) { mb.metricK8sNodeCPUTime.recordDataPoint(mb.startTime, ts, val) @@ -2474,6 +2641,11 @@ func (mb *MetricsBuilder) RecordK8sNodeNetworkIoDataPoint(ts pcommon.Timestamp, mb.metricK8sNodeNetworkIo.recordDataPoint(mb.startTime, ts, val, interfaceAttributeValue, directionAttributeValue.String()) } +// RecordK8sNodeUptimeDataPoint adds a data point to k8s.node.uptime metric. +func (mb *MetricsBuilder) RecordK8sNodeUptimeDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricK8sNodeUptime.recordDataPoint(mb.startTime, ts, val) +} + // RecordK8sPodCPUTimeDataPoint adds a data point to k8s.pod.cpu.time metric. func (mb *MetricsBuilder) RecordK8sPodCPUTimeDataPoint(ts pcommon.Timestamp, val float64) { mb.metricK8sPodCPUTime.recordDataPoint(mb.startTime, ts, val) @@ -2539,6 +2711,11 @@ func (mb *MetricsBuilder) RecordK8sPodNetworkIoDataPoint(ts pcommon.Timestamp, v mb.metricK8sPodNetworkIo.recordDataPoint(mb.startTime, ts, val, interfaceAttributeValue, directionAttributeValue.String()) } +// RecordK8sPodUptimeDataPoint adds a data point to k8s.pod.uptime metric. +func (mb *MetricsBuilder) RecordK8sPodUptimeDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricK8sPodUptime.recordDataPoint(mb.startTime, ts, val) +} + // RecordK8sVolumeAvailableDataPoint adds a data point to k8s.volume.available metric. func (mb *MetricsBuilder) RecordK8sVolumeAvailableDataPoint(ts pcommon.Timestamp, val int64) { mb.metricK8sVolumeAvailable.recordDataPoint(mb.startTime, ts, val) diff --git a/receiver/kubeletstatsreceiver/internal/metadata/generated_metrics_test.go b/receiver/kubeletstatsreceiver/internal/metadata/generated_metrics_test.go index 9aee6b9b887b..9dc39efa019b 100644 --- a/receiver/kubeletstatsreceiver/internal/metadata/generated_metrics_test.go +++ b/receiver/kubeletstatsreceiver/internal/metadata/generated_metrics_test.go @@ -98,6 +98,9 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordContainerMemoryWorkingSetDataPoint(ts, 1) + allMetricsCount++ + mb.RecordContainerUptimeDataPoint(ts, 1) + defaultMetricsCount++ allMetricsCount++ mb.RecordK8sNodeCPUTimeDataPoint(ts, 1) @@ -150,6 +153,9 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordK8sNodeNetworkIoDataPoint(ts, 1, "interface-val", AttributeDirectionReceive) + allMetricsCount++ + mb.RecordK8sNodeUptimeDataPoint(ts, 1) + defaultMetricsCount++ allMetricsCount++ mb.RecordK8sPodCPUTimeDataPoint(ts, 1) @@ -202,6 +208,9 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordK8sPodNetworkIoDataPoint(ts, 1, "interface-val", AttributeDirectionReceive) + allMetricsCount++ + mb.RecordK8sPodUptimeDataPoint(ts, 1) + defaultMetricsCount++ allMetricsCount++ mb.RecordK8sVolumeAvailableDataPoint(ts, 1) @@ -394,6 +403,20 @@ func TestMetricsBuilder(t *testing.T) { assert.Equal(t, ts, dp.Timestamp()) assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) assert.Equal(t, int64(1), dp.IntValue()) + case "container.uptime": + assert.False(t, validatedMetrics["container.uptime"], "Found a duplicate in the metrics slice: container.uptime") + validatedMetrics["container.uptime"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "The time since the container started", ms.At(i).Description()) + assert.Equal(t, "s", ms.At(i).Unit()) + assert.Equal(t, true, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) case "k8s.node.cpu.time": assert.False(t, validatedMetrics["k8s.node.cpu.time"], "Found a duplicate in the metrics slice: k8s.node.cpu.time") validatedMetrics["k8s.node.cpu.time"] = true @@ -568,6 +591,20 @@ func TestMetricsBuilder(t *testing.T) { attrVal, ok = dp.Attributes().Get("direction") assert.True(t, ok) assert.EqualValues(t, "receive", attrVal.Str()) + case "k8s.node.uptime": + assert.False(t, validatedMetrics["k8s.node.uptime"], "Found a duplicate in the metrics slice: k8s.node.uptime") + validatedMetrics["k8s.node.uptime"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "The time since the node started", ms.At(i).Description()) + assert.Equal(t, "s", ms.At(i).Unit()) + assert.Equal(t, true, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) case "k8s.pod.cpu.time": assert.False(t, validatedMetrics["k8s.pod.cpu.time"], "Found a duplicate in the metrics slice: k8s.pod.cpu.time") validatedMetrics["k8s.pod.cpu.time"] = true @@ -742,6 +779,20 @@ func TestMetricsBuilder(t *testing.T) { attrVal, ok = dp.Attributes().Get("direction") assert.True(t, ok) assert.EqualValues(t, "receive", attrVal.Str()) + case "k8s.pod.uptime": + assert.False(t, validatedMetrics["k8s.pod.uptime"], "Found a duplicate in the metrics slice: k8s.pod.uptime") + validatedMetrics["k8s.pod.uptime"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "The time since the pod started", ms.At(i).Description()) + assert.Equal(t, "s", ms.At(i).Unit()) + assert.Equal(t, true, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) case "k8s.volume.available": assert.False(t, validatedMetrics["k8s.volume.available"], "Found a duplicate in the metrics slice: k8s.volume.available") validatedMetrics["k8s.volume.available"] = true diff --git a/receiver/kubeletstatsreceiver/internal/metadata/metrics.go b/receiver/kubeletstatsreceiver/internal/metadata/metrics.go index c95dfe8f9451..838a53efd3ba 100644 --- a/receiver/kubeletstatsreceiver/internal/metadata/metrics.go +++ b/receiver/kubeletstatsreceiver/internal/metadata/metrics.go @@ -128,3 +128,19 @@ var K8sVolumeMetrics = VolumeMetrics{ InodesFree: (*MetricsBuilder).RecordK8sVolumeInodesFreeDataPoint, InodesUsed: (*MetricsBuilder).RecordK8sVolumeInodesUsedDataPoint, } + +type UptimeMetrics struct { + Uptime RecordIntDataPointFunc +} + +var NodeUptimeMetrics = UptimeMetrics{ + Uptime: (*MetricsBuilder).RecordK8sNodeUptimeDataPoint, +} + +var PodUptimeMetrics = UptimeMetrics{ + Uptime: (*MetricsBuilder).RecordK8sPodUptimeDataPoint, +} + +var ContainerUptimeMetrics = UptimeMetrics{ + Uptime: (*MetricsBuilder).RecordContainerUptimeDataPoint, +} diff --git a/receiver/kubeletstatsreceiver/internal/metadata/testdata/config.yaml b/receiver/kubeletstatsreceiver/internal/metadata/testdata/config.yaml index bd960d239a07..e79fe3db7171 100644 --- a/receiver/kubeletstatsreceiver/internal/metadata/testdata/config.yaml +++ b/receiver/kubeletstatsreceiver/internal/metadata/testdata/config.yaml @@ -23,6 +23,8 @@ all_set: enabled: true container.memory.working_set: enabled: true + container.uptime: + enabled: true k8s.node.cpu.time: enabled: true k8s.node.cpu.utilization: @@ -49,6 +51,8 @@ all_set: enabled: true k8s.node.network.io: enabled: true + k8s.node.uptime: + enabled: true k8s.pod.cpu.time: enabled: true k8s.pod.cpu.utilization: @@ -75,6 +79,8 @@ all_set: enabled: true k8s.pod.network.io: enabled: true + k8s.pod.uptime: + enabled: true k8s.volume.available: enabled: true k8s.volume.capacity: @@ -140,6 +146,8 @@ none_set: enabled: false container.memory.working_set: enabled: false + container.uptime: + enabled: false k8s.node.cpu.time: enabled: false k8s.node.cpu.utilization: @@ -166,6 +174,8 @@ none_set: enabled: false k8s.node.network.io: enabled: false + k8s.node.uptime: + enabled: false k8s.pod.cpu.time: enabled: false k8s.pod.cpu.utilization: @@ -192,6 +202,8 @@ none_set: enabled: false k8s.pod.network.io: enabled: false + k8s.pod.uptime: + enabled: false k8s.volume.available: enabled: false k8s.volume.capacity: diff --git a/receiver/kubeletstatsreceiver/metadata.yaml b/receiver/kubeletstatsreceiver/metadata.yaml index 994ccc65e5ff..d162d5f5ed45 100644 --- a/receiver/kubeletstatsreceiver/metadata.yaml +++ b/receiver/kubeletstatsreceiver/metadata.yaml @@ -178,6 +178,15 @@ metrics: monotonic: true aggregation_temporality: cumulative attributes: ["interface", "direction"] + k8s.node.uptime: + enabled: false + description: "The time since the node started" + unit: s + sum: + value_type: int + monotonic: true + aggregation_temporality: cumulative + attributes: [] k8s.pod.cpu.utilization: enabled: true description: "Pod CPU utilization" @@ -275,6 +284,15 @@ metrics: monotonic: true aggregation_temporality: cumulative attributes: ["interface", "direction"] + k8s.pod.uptime: + enabled: false + description: "The time since the pod started" + unit: s + sum: + value_type: int + monotonic: true + aggregation_temporality: cumulative + attributes: [] container.cpu.utilization: enabled: true description: "Container CPU utilization" @@ -354,6 +372,15 @@ metrics: gauge: value_type: int attributes: [] + container.uptime: + enabled: false + description: "The time since the container started" + unit: s + sum: + value_type: int + monotonic: true + aggregation_temporality: cumulative + attributes: [] k8s.volume.available: enabled: true description: "The number of available bytes in the volume."