diff --git a/.chloggen/mdatagen_add_semconv_ref.yaml b/.chloggen/mdatagen_add_semconv_ref.yaml new file mode 100644 index 00000000000..654cb7186a1 --- /dev/null +++ b/.chloggen/mdatagen_add_semconv_ref.yaml @@ -0,0 +1,25 @@ +# 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. otlpreceiver) +component: mdatagen + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Introduce semantic convention reference for signal in metadata schema + +# One or more tracking issues or pull requests related to the change +issues: [13297] + +# (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: + +# 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: [user] diff --git a/cmd/mdatagen/internal/loader_test.go b/cmd/mdatagen/internal/loader_test.go index 7ab082a8f91..909e9e61be5 100644 --- a/cmd/mdatagen/internal/loader_test.go +++ b/cmd/mdatagen/internal/loader_test.go @@ -47,7 +47,7 @@ func TestLoadMetadata(t *testing.T) { GithubProject: "open-telemetry/opentelemetry-collector", GeneratedPackageName: "metadata", Type: "sample", - SemConvVersion: "1.9.0", + SemConvVersion: "1.37.0", PackageName: "go.opentelemetry.io/collector/cmd/mdatagen/internal/samplereceiver", Status: &Status{ DisableCodeCov: true, @@ -237,6 +237,21 @@ func TestLoadMetadata(t *testing.T) { Mono: Mono{Monotonic: true}, }, }, + "system.cpu.time": { + Signal: Signal{ + Enabled: true, + Stability: Stability{Level: "beta"}, + SemanticConvention: &SemanticConvention{SemanticConventionRef: "https://github.com/open-telemetry/semantic-conventions/blob/v1.37.0/docs/system/system-metrics.md#metric-systemcputime"}, + Description: "Monotonic cumulative sum int metric enabled by default.", + ExtendedDocumentation: "The metric will be become optional soon.", + }, + Unit: strPtr("s"), + Sum: &Sum{ + MetricValueType: MetricValueType{pmetric.NumberDataPointValueTypeInt}, + AggregationTemporality: AggregationTemporality{Aggregation: pmetric.AggregationTemporalityCumulative}, + Mono: Mono{Monotonic: true}, + }, + }, "optional.metric": { Signal: Signal{ Enabled: false, diff --git a/cmd/mdatagen/internal/metadata.go b/cmd/mdatagen/internal/metadata.go index 2524c974076..7aec759b13f 100644 --- a/cmd/mdatagen/internal/metadata.go +++ b/cmd/mdatagen/internal/metadata.go @@ -125,8 +125,8 @@ func (md *Metadata) validateMetricsAndEvents() error { var errs error usedAttrs := map[AttributeName]bool{} errs = errors.Join(errs, - validateMetrics(md.Metrics, md.Attributes, usedAttrs), - validateMetrics(md.Telemetry.Metrics, md.Attributes, usedAttrs), + validateMetrics(md.Metrics, md.Attributes, usedAttrs, md.SemConvVersion), + validateMetrics(md.Telemetry.Metrics, md.Attributes, usedAttrs, md.SemConvVersion), validateEvents(md.Events, md.Attributes, usedAttrs), md.validateAttributes(usedAttrs)) return errs @@ -172,10 +172,10 @@ func (md *Metadata) supportsSignal(signal string) bool { return false } -func validateMetrics(metrics map[MetricName]Metric, attributes map[AttributeName]Attribute, usedAttrs map[AttributeName]bool) error { +func validateMetrics(metrics map[MetricName]Metric, attributes map[AttributeName]Attribute, usedAttrs map[AttributeName]bool, semConvVersion string) error { var errs error for mn, m := range metrics { - if err := m.validate(); err != nil { + if err := m.validate(mn, semConvVersion); err != nil { errs = errors.Join(errs, fmt.Errorf(`metric "%v": %w`, mn, err)) continue } @@ -284,6 +284,10 @@ func (mvt ValueType) Primitive() string { } } +type SemanticConvention struct { + SemanticConventionRef string `mapstructure:"semconv_ref"` +} + type Warnings struct { // A warning that will be displayed if the field is enabled in user config. IfEnabled string `mapstructure:"if_enabled"` @@ -371,6 +375,9 @@ type Signal struct { // Description of the signal. Description string `mapstructure:"description"` + // The semantic convention reference of the signal. + SemanticConvention *SemanticConvention `mapstructure:"semantic_convention"` + // The stability level of the signal. Stability Stability `mapstructure:"stability"` diff --git a/cmd/mdatagen/internal/metric.go b/cmd/mdatagen/internal/metric.go index feb40872b28..662ee54ad4e 100644 --- a/cmd/mdatagen/internal/metric.go +++ b/cmd/mdatagen/internal/metric.go @@ -6,6 +6,7 @@ package internal // import "go.opentelemetry.io/collector/cmd/mdatagen/internal" import ( "errors" "fmt" + "regexp" "strings" "golang.org/x/text/cases" @@ -16,6 +17,8 @@ import ( "go.opentelemetry.io/collector/pdata/pmetric" ) +var reNonAlnum = regexp.MustCompile(`[^a-z0-9]+`) + type MetricName string func (mn MetricName) Render() (string, error) { @@ -62,7 +65,7 @@ func (s Stability) String() string { return fmt.Sprintf(" [%s]", s.Level) } -func (m *Metric) validate() error { +func (m *Metric) validate(metricName MetricName, semConvVersion string) error { var errs error if m.Sum == nil && m.Gauge == nil && m.Histogram == nil { errs = errors.Join(errs, errors.New("missing metric type key, "+ @@ -84,9 +87,48 @@ func (m *Metric) validate() error { if m.Gauge != nil { errs = errors.Join(errs, m.Gauge.Validate()) } + if m.SemanticConvention != nil { + if err := validateSemConvMetricURL(m.SemanticConvention.SemanticConventionRef, semConvVersion, string(metricName)); err != nil { + errs = errors.Join(errs, err) + } + } return errs } +func metricAnchor(metricName string) string { + m := strings.ToLower(strings.TrimSpace(metricName)) + m = reNonAlnum.ReplaceAllString(m, "") + return "metric-" + m +} + +// validateSemConvMetricURL verifies the URL matches exactly: +// https://github.com/open-telemetry/semantic-conventions/blob//*#metric- +func validateSemConvMetricURL(rawURL, semConvVersion, metricName string) error { + if strings.TrimSpace(rawURL) == "" { + return errors.New("url is empty") + } + if strings.TrimSpace(semConvVersion) == "" { + return errors.New("semConvVersion is empty") + } + if strings.TrimSpace(metricName) == "" { + return errors.New("metricName is empty") + } + semConvVersion = "v" + semConvVersion + + anchor := metricAnchor(metricName) + // Build a strict regex that enforces https, repo, blob, given version, any doc path, and exact anchor. + pattern := fmt.Sprintf(`^https://github\.com/open-telemetry/semantic-conventions/blob/%s/[^#\s]+#%s$`, + regexp.QuoteMeta(semConvVersion), + regexp.QuoteMeta(anchor), + ) + re := regexp.MustCompile(pattern) + if !re.MatchString(rawURL) { + return errors.New(fmt.Sprintf("invalid semantic-conventions URL: want https://github.com/open-telemetry/semantic-conventions/blob/%s/*#%s, got %q", + semConvVersion, anchor, rawURL)) + } + return nil +} + func (m *Metric) Unmarshal(parser *confmap.Conf) error { if !parser.IsSet("enabled") { return errors.New("missing required field: `enabled`") diff --git a/cmd/mdatagen/internal/samplereceiver/documentation.md b/cmd/mdatagen/internal/samplereceiver/documentation.md index acf905fb866..5c881c6df5c 100644 --- a/cmd/mdatagen/internal/samplereceiver/documentation.md +++ b/cmd/mdatagen/internal/samplereceiver/documentation.md @@ -62,6 +62,16 @@ Monotonic cumulative sum int metric with string input_type enabled by default. | slice_attr | Attribute with a slice value. | Any Slice | false | | map_attr | Attribute with a map value. | Any Map | false | +### system.cpu.time + +Monotonic cumulative sum int metric enabled by default. + +The metric will be become optional soon. + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | Stability | Semantic Convention | +| ---- | ----------- | ---------- | ----------------------- | --------- | --------- | ------------------- | +| s | Sum | Int | Cumulative | true | beta | [system.cpu.time](https://github.com/open-telemetry/semantic-conventions/blob/v1.37.0/docs/system/system-metrics.md#metric-systemcputime) | + ## Optional Metrics The following metrics are not emitted by default. Each of them can be enabled by applying the following configuration: diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config.go index 4668abbc732..8fede9dd04d 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config.go @@ -33,6 +33,7 @@ type MetricsConfig struct { MetricInputType MetricConfig `mapstructure:"metric.input_type"` OptionalMetric MetricConfig `mapstructure:"optional.metric"` OptionalMetricEmptyUnit MetricConfig `mapstructure:"optional.metric.empty_unit"` + SystemCPUTime MetricConfig `mapstructure:"system.cpu.time"` } func DefaultMetricsConfig() MetricsConfig { @@ -52,6 +53,9 @@ func DefaultMetricsConfig() MetricsConfig { OptionalMetricEmptyUnit: MetricConfig{ Enabled: false, }, + SystemCPUTime: MetricConfig{ + Enabled: true, + }, } } diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config_test.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config_test.go index 8f1e1b3d164..abe03b709a5 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config_test.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_config_test.go @@ -32,6 +32,7 @@ func TestMetricsBuilderConfig(t *testing.T) { MetricInputType: MetricConfig{Enabled: true}, OptionalMetric: MetricConfig{Enabled: true}, OptionalMetricEmptyUnit: MetricConfig{Enabled: true}, + SystemCPUTime: MetricConfig{Enabled: true}, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: true}, @@ -54,6 +55,7 @@ func TestMetricsBuilderConfig(t *testing.T) { MetricInputType: MetricConfig{Enabled: false}, OptionalMetric: MetricConfig{Enabled: false}, OptionalMetricEmptyUnit: MetricConfig{Enabled: false}, + SystemCPUTime: MetricConfig{Enabled: false}, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: false}, diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_logs.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_logs.go index ea414e8c615..797a8483fc0 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_logs.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_logs.go @@ -5,7 +5,7 @@ package metadata import ( "context" - conventions "go.opentelemetry.io/otel/semconv/v1.9.0" + conventions "go.opentelemetry.io/otel/semconv/v1.37.0" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/collector/component" diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go index 6f7473f89c8..e1d12eec36d 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics.go @@ -7,7 +7,7 @@ import ( "strconv" "time" - conventions "go.opentelemetry.io/otel/semconv/v1.9.0" + conventions "go.opentelemetry.io/otel/semconv/v1.37.0" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/filter" @@ -62,6 +62,9 @@ var MetricsInfo = metricsInfo{ OptionalMetricEmptyUnit: metricInfo{ Name: "optional.metric.empty_unit", }, + SystemCPUTime: metricInfo{ + Name: "system.cpu.time", + }, } type metricsInfo struct { @@ -70,6 +73,7 @@ type metricsInfo struct { MetricInputType metricInfo OptionalMetric metricInfo OptionalMetricEmptyUnit metricInfo + SystemCPUTime metricInfo } type metricInfo struct { @@ -374,6 +378,57 @@ func newMetricOptionalMetricEmptyUnit(cfg MetricConfig) metricOptionalMetricEmpt return m } +type metricSystemCPUTime 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 system.cpu.time metric with initial data. +func (m *metricSystemCPUTime) init() { + m.data.SetName("system.cpu.time") + m.data.SetDescription("Monotonic cumulative sum int metric enabled by default.") + m.data.SetUnit("s") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) +} + +func (m *metricSystemCPUTime) 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 *metricSystemCPUTime) 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 *metricSystemCPUTime) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricSystemCPUTime(cfg MetricConfig) metricSystemCPUTime { + m := metricSystemCPUTime{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + // MetricsBuilder provides an interface for scrapers to report metrics while taking care of all the transformations // required to produce metric representation defined in metadata and user config. type MetricsBuilder struct { @@ -389,6 +444,7 @@ type MetricsBuilder struct { metricMetricInputType metricMetricInputType metricOptionalMetric metricOptionalMetric metricOptionalMetricEmptyUnit metricOptionalMetricEmptyUnit + metricSystemCPUTime metricSystemCPUTime } // MetricBuilderOption applies changes to default metrics builder. @@ -440,6 +496,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, opt metricMetricInputType: newMetricMetricInputType(mbc.Metrics.MetricInputType), metricOptionalMetric: newMetricOptionalMetric(mbc.Metrics.OptionalMetric), metricOptionalMetricEmptyUnit: newMetricOptionalMetricEmptyUnit(mbc.Metrics.OptionalMetricEmptyUnit), + metricSystemCPUTime: newMetricSystemCPUTime(mbc.Metrics.SystemCPUTime), resourceAttributeIncludeFilter: make(map[string]filter.Filter), resourceAttributeExcludeFilter: make(map[string]filter.Filter), } @@ -566,6 +623,7 @@ func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { mb.metricMetricInputType.emit(ils.Metrics()) mb.metricOptionalMetric.emit(ils.Metrics()) mb.metricOptionalMetricEmptyUnit.emit(ils.Metrics()) + mb.metricSystemCPUTime.emit(ils.Metrics()) for _, op := range options { op.apply(rm) @@ -627,6 +685,11 @@ func (mb *MetricsBuilder) RecordOptionalMetricEmptyUnitDataPoint(ts pcommon.Time mb.metricOptionalMetricEmptyUnit.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, booleanAttrAttributeValue) } +// RecordSystemCPUTimeDataPoint adds a data point to system.cpu.time metric. +func (mb *MetricsBuilder) RecordSystemCPUTimeDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricSystemCPUTime.recordDataPoint(mb.startTime, ts, val) +} + // Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, // and metrics builder should update its startTime and reset it's internal state accordingly. func (mb *MetricsBuilder) Reset(options ...MetricBuilderOption) { diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go index e11ac2a2e84..d187d918329 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/generated_metrics_test.go @@ -115,6 +115,10 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 1, "string_attr-val", true) + defaultMetricsCount++ + allMetricsCount++ + mb.RecordSystemCPUTimeDataPoint(ts, 1) + rb := mb.NewResourceBuilder() rb.SetMapResourceAttr(map[string]any{"key1": "map.resource.attr-val1", "key2": "map.resource.attr-val2"}) rb.SetOptionalResourceAttr("optional.resource.attr-val") @@ -266,6 +270,20 @@ func TestMetricsBuilder(t *testing.T) { attrVal, ok = dp.Attributes().Get("boolean_attr") assert.True(t, ok) assert.True(t, attrVal.Bool()) + case "system.cpu.time": + assert.False(t, validatedMetrics["system.cpu.time"], "Found a duplicate in the metrics slice: system.cpu.time") + validatedMetrics["system.cpu.time"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Monotonic cumulative sum int metric enabled by default.", ms.At(i).Description()) + assert.Equal(t, "s", ms.At(i).Unit()) + assert.True(t, 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()) } } }) diff --git a/cmd/mdatagen/internal/samplereceiver/internal/metadata/testdata/config.yaml b/cmd/mdatagen/internal/samplereceiver/internal/metadata/testdata/config.yaml index b8e4438bb0d..6e461af19fe 100644 --- a/cmd/mdatagen/internal/samplereceiver/internal/metadata/testdata/config.yaml +++ b/cmd/mdatagen/internal/samplereceiver/internal/metadata/testdata/config.yaml @@ -11,6 +11,8 @@ all_set: enabled: true optional.metric.empty_unit: enabled: true + system.cpu.time: + enabled: true events: default.event: enabled: true @@ -47,6 +49,8 @@ none_set: enabled: false optional.metric.empty_unit: enabled: false + system.cpu.time: + enabled: false events: default.event: enabled: false diff --git a/cmd/mdatagen/internal/samplereceiver/metadata.yaml b/cmd/mdatagen/internal/samplereceiver/metadata.yaml index 95b22c30741..9bb6b2bac4d 100644 --- a/cmd/mdatagen/internal/samplereceiver/metadata.yaml +++ b/cmd/mdatagen/internal/samplereceiver/metadata.yaml @@ -4,7 +4,7 @@ type: sample scope_name: go.opentelemetry.io/collector/internal/receiver/samplereceiver github_project: open-telemetry/opentelemetry-collector -sem_conv_version: 1.9.0 +sem_conv_version: 1.37.0 status: disable_codecov_badge: true @@ -141,6 +141,20 @@ events: attributes: [ string_attr, overridden_int_attr, enum_attr, slice_attr, map_attr ] metrics: + system.cpu.time: + enabled: true + stability: + level: beta + description: Monotonic cumulative sum int metric enabled by default. + extended_documentation: The metric will be become optional soon. + unit: s + sum: + value_type: int + monotonic: true + aggregation_temporality: cumulative + semantic_convention: + semconv_ref: https://github.com/open-telemetry/semantic-conventions/blob/v1.37.0/docs/system/system-metrics.md#metric-systemcputime + default.metric: enabled: true description: Monotonic cumulative sum int metric enabled by default. diff --git a/cmd/mdatagen/internal/samplescraper/documentation.md b/cmd/mdatagen/internal/samplescraper/documentation.md index 5044a8669ef..2488948ea1e 100644 --- a/cmd/mdatagen/internal/samplescraper/documentation.md +++ b/cmd/mdatagen/internal/samplescraper/documentation.md @@ -60,6 +60,16 @@ Monotonic cumulative sum int metric with string input_type enabled by default. | slice_attr | Attribute with a slice value. | Any Slice | false | | map_attr | Attribute with a map value. | Any Map | false | +### system.cpu.time + +Monotonic cumulative sum int metric enabled by default. + +The metric will be become optional soon. + +| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic | Stability | Semantic Convention | +| ---- | ----------- | ---------- | ----------------------- | --------- | --------- | ------------------- | +| s | Sum | Int | Cumulative | true | beta | [system.cpu.time](https://github.com/open-telemetry/semantic-conventions/blob/v1.37.0/docs/system/system-metrics.md#metric-systemcputime) | + ## Optional Metrics The following metrics are not emitted by default. Each of them can be enabled by applying the following configuration: diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config.go index 05ddc47ac40..8a790277c38 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config.go @@ -33,6 +33,7 @@ type MetricsConfig struct { MetricInputType MetricConfig `mapstructure:"metric.input_type"` OptionalMetric MetricConfig `mapstructure:"optional.metric"` OptionalMetricEmptyUnit MetricConfig `mapstructure:"optional.metric.empty_unit"` + SystemCPUTime MetricConfig `mapstructure:"system.cpu.time"` } func DefaultMetricsConfig() MetricsConfig { @@ -52,6 +53,9 @@ func DefaultMetricsConfig() MetricsConfig { OptionalMetricEmptyUnit: MetricConfig{ Enabled: false, }, + SystemCPUTime: MetricConfig{ + Enabled: true, + }, } } diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config_test.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config_test.go index cc1a2cd0525..d68dcc807a0 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config_test.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_config_test.go @@ -32,6 +32,7 @@ func TestMetricsBuilderConfig(t *testing.T) { MetricInputType: MetricConfig{Enabled: true}, OptionalMetric: MetricConfig{Enabled: true}, OptionalMetricEmptyUnit: MetricConfig{Enabled: true}, + SystemCPUTime: MetricConfig{Enabled: true}, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: true}, @@ -54,6 +55,7 @@ func TestMetricsBuilderConfig(t *testing.T) { MetricInputType: MetricConfig{Enabled: false}, OptionalMetric: MetricConfig{Enabled: false}, OptionalMetricEmptyUnit: MetricConfig{Enabled: false}, + SystemCPUTime: MetricConfig{Enabled: false}, }, ResourceAttributes: ResourceAttributesConfig{ MapResourceAttr: ResourceAttributeConfig{Enabled: false}, diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_logs.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_logs.go index 0f936fce0a1..187b564f16a 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_logs.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_logs.go @@ -3,7 +3,7 @@ package metadata import ( - conventions "go.opentelemetry.io/otel/semconv/v1.9.0" + conventions "go.opentelemetry.io/otel/semconv/v1.37.0" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/pdata/pcommon" diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go index af8e1308cc8..296bacb7593 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics.go @@ -7,7 +7,7 @@ import ( "strconv" "time" - conventions "go.opentelemetry.io/otel/semconv/v1.9.0" + conventions "go.opentelemetry.io/otel/semconv/v1.37.0" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/filter" @@ -62,6 +62,9 @@ var MetricsInfo = metricsInfo{ OptionalMetricEmptyUnit: metricInfo{ Name: "optional.metric.empty_unit", }, + SystemCPUTime: metricInfo{ + Name: "system.cpu.time", + }, } type metricsInfo struct { @@ -70,6 +73,7 @@ type metricsInfo struct { MetricInputType metricInfo OptionalMetric metricInfo OptionalMetricEmptyUnit metricInfo + SystemCPUTime metricInfo } type metricInfo struct { @@ -346,6 +350,57 @@ func newMetricOptionalMetricEmptyUnit(cfg MetricConfig) metricOptionalMetricEmpt return m } +type metricSystemCPUTime 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 system.cpu.time metric with initial data. +func (m *metricSystemCPUTime) init() { + m.data.SetName("system.cpu.time") + m.data.SetDescription("Monotonic cumulative sum int metric enabled by default.") + m.data.SetUnit("s") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(true) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) +} + +func (m *metricSystemCPUTime) 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 *metricSystemCPUTime) 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 *metricSystemCPUTime) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricSystemCPUTime(cfg MetricConfig) metricSystemCPUTime { + m := metricSystemCPUTime{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + // MetricsBuilder provides an interface for scrapers to report metrics while taking care of all the transformations // required to produce metric representation defined in metadata and user config. type MetricsBuilder struct { @@ -361,6 +416,7 @@ type MetricsBuilder struct { metricMetricInputType metricMetricInputType metricOptionalMetric metricOptionalMetric metricOptionalMetricEmptyUnit metricOptionalMetricEmptyUnit + metricSystemCPUTime metricSystemCPUTime } // MetricBuilderOption applies changes to default metrics builder. @@ -412,6 +468,7 @@ func NewMetricsBuilder(mbc MetricsBuilderConfig, settings scraper.Settings, opti metricMetricInputType: newMetricMetricInputType(mbc.Metrics.MetricInputType), metricOptionalMetric: newMetricOptionalMetric(mbc.Metrics.OptionalMetric), metricOptionalMetricEmptyUnit: newMetricOptionalMetricEmptyUnit(mbc.Metrics.OptionalMetricEmptyUnit), + metricSystemCPUTime: newMetricSystemCPUTime(mbc.Metrics.SystemCPUTime), resourceAttributeIncludeFilter: make(map[string]filter.Filter), resourceAttributeExcludeFilter: make(map[string]filter.Filter), } @@ -538,6 +595,7 @@ func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) { mb.metricMetricInputType.emit(ils.Metrics()) mb.metricOptionalMetric.emit(ils.Metrics()) mb.metricOptionalMetricEmptyUnit.emit(ils.Metrics()) + mb.metricSystemCPUTime.emit(ils.Metrics()) for _, op := range options { op.apply(rm) @@ -599,6 +657,11 @@ func (mb *MetricsBuilder) RecordOptionalMetricEmptyUnitDataPoint(ts pcommon.Time mb.metricOptionalMetricEmptyUnit.recordDataPoint(mb.startTime, ts, val, stringAttrAttributeValue, booleanAttrAttributeValue) } +// RecordSystemCPUTimeDataPoint adds a data point to system.cpu.time metric. +func (mb *MetricsBuilder) RecordSystemCPUTimeDataPoint(ts pcommon.Timestamp, val int64) { + mb.metricSystemCPUTime.recordDataPoint(mb.startTime, ts, val) +} + // Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, // and metrics builder should update its startTime and reset it's internal state accordingly. func (mb *MetricsBuilder) Reset(options ...MetricBuilderOption) { diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go index 2261134d605..a2e2b5e55c0 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/generated_metrics_test.go @@ -115,6 +115,10 @@ func TestMetricsBuilder(t *testing.T) { allMetricsCount++ mb.RecordOptionalMetricEmptyUnitDataPoint(ts, 1, "string_attr-val", true) + defaultMetricsCount++ + allMetricsCount++ + mb.RecordSystemCPUTimeDataPoint(ts, 1) + rb := mb.NewResourceBuilder() rb.SetMapResourceAttr(map[string]any{"key1": "map.resource.attr-val1", "key2": "map.resource.attr-val2"}) rb.SetOptionalResourceAttr("optional.resource.attr-val") @@ -257,6 +261,20 @@ func TestMetricsBuilder(t *testing.T) { attrVal, ok = dp.Attributes().Get("boolean_attr") assert.True(t, ok) assert.True(t, attrVal.Bool()) + case "system.cpu.time": + assert.False(t, validatedMetrics["system.cpu.time"], "Found a duplicate in the metrics slice: system.cpu.time") + validatedMetrics["system.cpu.time"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Monotonic cumulative sum int metric enabled by default.", ms.At(i).Description()) + assert.Equal(t, "s", ms.At(i).Unit()) + assert.True(t, 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()) } } }) diff --git a/cmd/mdatagen/internal/samplescraper/internal/metadata/testdata/config.yaml b/cmd/mdatagen/internal/samplescraper/internal/metadata/testdata/config.yaml index c4b7edd6568..d936342fd3f 100644 --- a/cmd/mdatagen/internal/samplescraper/internal/metadata/testdata/config.yaml +++ b/cmd/mdatagen/internal/samplescraper/internal/metadata/testdata/config.yaml @@ -11,6 +11,8 @@ all_set: enabled: true optional.metric.empty_unit: enabled: true + system.cpu.time: + enabled: true resource_attributes: map.resource.attr: enabled: true @@ -40,6 +42,8 @@ none_set: enabled: false optional.metric.empty_unit: enabled: false + system.cpu.time: + enabled: false resource_attributes: map.resource.attr: enabled: false diff --git a/cmd/mdatagen/internal/samplescraper/metadata.yaml b/cmd/mdatagen/internal/samplescraper/metadata.yaml index efb1a963bb9..d4ad7a1d728 100644 --- a/cmd/mdatagen/internal/samplescraper/metadata.yaml +++ b/cmd/mdatagen/internal/samplescraper/metadata.yaml @@ -3,7 +3,7 @@ type: sample github_project: open-telemetry/opentelemetry-collector -sem_conv_version: 1.9.0 +sem_conv_version: 1.37.0 status: disable_codecov_badge: true @@ -100,6 +100,19 @@ attributes: type: map metrics: + system.cpu.time: + enabled: true + stability: + level: beta + description: Monotonic cumulative sum int metric enabled by default. + extended_documentation: The metric will be become optional soon. + unit: s + sum: + value_type: int + monotonic: true + aggregation_temporality: cumulative + semantic_convention: + semconv_ref: https://github.com/open-telemetry/semantic-conventions/blob/v1.37.0/docs/system/system-metrics.md#metric-systemcputime default.metric: enabled: true description: Monotonic cumulative sum int metric enabled by default. diff --git a/cmd/mdatagen/internal/templates/documentation.md.tmpl b/cmd/mdatagen/internal/templates/documentation.md.tmpl index abdca112594..9111f29e867 100644 --- a/cmd/mdatagen/internal/templates/documentation.md.tmpl +++ b/cmd/mdatagen/internal/templates/documentation.md.tmpl @@ -12,12 +12,13 @@ {{- end }} -| Unit | Metric Type | Value Type |{{ if $metric.Data.HasAggregated }} Aggregation Temporality |{{ end }}{{ if $metric.Data.HasMonotonic }} Monotonic |{{ end }}{{ if $metric.Stability.Level }} Stability |{{ end }} -| ---- | ----------- | ---------- |{{ if $metric.Data.HasAggregated }} ----------------------- |{{ end }}{{ if $metric.Data.HasMonotonic }} --------- |{{ end }}{{ if $metric.Stability.Level }} --------- |{{ end }} +| Unit | Metric Type | Value Type |{{ if $metric.Data.HasAggregated }} Aggregation Temporality |{{ end }}{{ if $metric.Data.HasMonotonic }} Monotonic |{{ end }}{{ if $metric.Stability.Level }} Stability |{{ end }}{{ if $metric.SemanticConvention }} Semantic Convention |{{ end }} +| ---- | ----------- | ---------- |{{ if $metric.Data.HasAggregated }} ----------------------- |{{ end }}{{ if $metric.Data.HasMonotonic }} --------- |{{ end }}{{ if $metric.Stability.Level }} --------- |{{ end }}{{ if $metric.SemanticConvention }} ------------------- |{{ end }} | {{ $metric.Unit }} | {{ $metric.Data.Type }} | {{ $metric.Data.MetricValueType }} | {{- if $metric.Data.HasAggregated }} {{ $metric.Data.AggregationTemporality }} |{{ end }} {{- if $metric.Data.HasMonotonic }} {{ $metric.Data.Monotonic }} |{{ end }} {{- if $metric.Stability.Level }} {{ $metric.Stability.Level }} |{{ end }} +{{- if $metric.SemanticConvention }} [{{ $metricName }}]({{ $metric.SemanticConvention.SemanticConventionRef }}) |{{ end }} {{- if $metric.Attributes }} @@ -79,11 +80,12 @@ {{- end }} -| Unit | Metric Type | Value Type |{{ if $metric.Data.HasMonotonic }} Monotonic |{{ end }}{{ if $metric.Stability.Level }} Stability |{{ end }} -| ---- | ----------- | ---------- |{{ if $metric.Data.HasMonotonic }} --------- |{{ end }}{{ if $metric.Stability.Level }} --------- |{{ end }} +| Unit | Metric Type | Value Type |{{ if $metric.Data.HasMonotonic }} Monotonic |{{ end }}{{ if $metric.Stability.Level }} Stability |{{ end }}{{ if $metric.SemanticConvention }} Semantic Convention |{{ end }} +| ---- | ----------- | ---------- |{{ if $metric.Data.HasMonotonic }} --------- |{{ end }}{{ if $metric.Stability.Level }} --------- |{{ end }}{{ if $metric.SemanticConvention }} ------------------- |{{ end }} | {{ $metric.Unit }} | {{ $metric.Data.Type }} | {{ $metric.Data.MetricValueType }} | {{- if $metric.Data.HasMonotonic }} {{ $metric.Data.Monotonic }} |{{ end }} {{- if $metric.Stability.Level }} {{ $metric.Stability.Level }} |{{ end }} +{{- if $metric.SemanticConvention }} [{{ $metricName }}]({{ $metric.SemanticConvention.SemanticConventionRef }}) |{{ end }} {{- if $metric.Attributes }} diff --git a/cmd/mdatagen/metadata-schema.yaml b/cmd/mdatagen/metadata-schema.yaml index 353cdb4c20b..3f9524af876 100644 --- a/cmd/mdatagen/metadata-schema.yaml +++ b/cmd/mdatagen/metadata-schema.yaml @@ -124,6 +124,9 @@ metrics: level: # Optional: the version current stability was introduced from: + # Optional: the reference to a semantic convention. Required when stability_level equals stable + semantic_convention: + semconv_ref: # Optional: map of event names with the key being the event name and value # being described below.