diff --git a/CHANGELOG.md b/CHANGELOG.md index a83f1bef35f..43232ab0e77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,33 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - The semantic conventions have been upgraded from `v1.26.0` to `v1.32.0` in `go.opentelemetry.io/contrib/bridges/otelzap`. (#7389) - The semantic conventions have been upgraded from `v1.26.0` to `v1.32.0` in `go.opentelemetry.io/contrib/detectors/gcp`. (#7378) - The semantic conventions have been upgraded from `v1.21.0` to `v1.32.0` in `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda`. (#7400) +- The semantic conventions in `go.opentelemetry.io/contrib/instrumentation/host` have been upgraded to `v1.32.0`. (#7390) + - The description of `process.cpu.time` is updated to comply with semantic conventions. + - `process.cpu.time` now uses the `state` attribute instead of `cpu.mode`. + - The `system.cpu.time` metric is renamed to `cpu.time`. + - `cpu.time` now uses the `state` attribute instead of `cpu.mode`. + - `system.memory.usage` now uses the `state` attribute instead of `system.memory.state`. + - `system.memory.utilization` now uses the `state` attribute instead of `system.memory.state`. + - The `system.memory.state` attribute (now `state`) value of `available` is now `free` instead. + +### Deprecated + +- `AttributeCPUTimeUser` in `go.opentelemetry.io/contrib/instrumentation/host` is deprecated. + Use `go.opentelemetry.io/otel/semconv` instead. (#7390) +- `AttributeCPUTimeSystem` in `go.opentelemetry.io/contrib/instrumentation/host` is deprecated. + Use `go.opentelemetry.io/otel/semconv` instead. (#7390) +- `AttributeCPUTimeOther` in `go.opentelemetry.io/contrib/instrumentation/host` is deprecated. + Use `go.opentelemetry.io/otel/semconv` instead. (#7390) +- `AttributeCPUTimeIdle` in `go.opentelemetry.io/contrib/instrumentation/host` is deprecated. + Use `go.opentelemetry.io/otel/semconv` instead. (#7390) +- `AttributeMemoryAvailable` in `go.opentelemetry.io/contrib/instrumentation/host` is deprecated. + Use `go.opentelemetry.io/otel/semconv` instead. (#7390) +- `AttributeMemoryUsed` in `go.opentelemetry.io/contrib/instrumentation/host` is deprecated. + Use `go.opentelemetry.io/otel/semconv` instead. (#7390) +- `AttributeNetworkTransmit` in `go.opentelemetry.io/contrib/instrumentation/host` is deprecated. + Use `go.opentelemetry.io/otel/semconv` instead. (#7390) +- `AttributeNetworkReceive` in `go.opentelemetry.io/contrib/instrumentation/host` is deprecated. + Use `go.opentelemetry.io/otel/semconv` instead. (#7390) ### Fixed diff --git a/instrumentation/host/example/main.go b/instrumentation/host/example/main.go index e6c27203607..d2dd7bffc93 100644 --- a/instrumentation/host/example/main.go +++ b/instrumentation/host/example/main.go @@ -17,7 +17,7 @@ import ( "go.opentelemetry.io/otel/exporters/stdout/stdoutmetric" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.17.0" + semconv "go.opentelemetry.io/otel/semconv/v1.32.0" ) var res = resource.NewWithAttributes( diff --git a/instrumentation/host/go.mod b/instrumentation/host/go.mod index 6e5918ef91a..01a3df252a2 100644 --- a/instrumentation/host/go.mod +++ b/instrumentation/host/go.mod @@ -7,6 +7,8 @@ require ( github.com/stretchr/testify v1.10.0 go.opentelemetry.io/otel v1.36.0 go.opentelemetry.io/otel/metric v1.36.0 + go.opentelemetry.io/otel/sdk v1.36.0 + go.opentelemetry.io/otel/sdk/metric v1.36.0 ) require ( @@ -15,6 +17,7 @@ require ( github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect diff --git a/instrumentation/host/go.sum b/instrumentation/host/go.sum index 876f725fcad..e86ea6a128e 100644 --- a/instrumentation/host/go.sum +++ b/instrumentation/host/go.sum @@ -12,6 +12,8 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -40,6 +42,10 @@ go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= +go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= +go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= +go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/instrumentation/host/host.go b/instrumentation/host/host.go index 1f92f3cca74..6cec838fc45 100644 --- a/instrumentation/host/host.go +++ b/instrumentation/host/host.go @@ -19,6 +19,9 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/semconv/v1.32.0/cpuconv" + "go.opentelemetry.io/otel/semconv/v1.32.0/processconv" + "go.opentelemetry.io/otel/semconv/v1.32.0/systemconv" ) // ScopeName is the instrumentation scope name. @@ -61,20 +64,28 @@ func (o metricProviderOption) apply(c *config) { var ( // Attribute sets for CPU time measurements. - AttributeCPUTimeUser = attribute.NewSet(attribute.String("state", "user")) + // Deprecated: Use go.opentelemetry.io/otel/semconv instead. + AttributeCPUTimeUser = attribute.NewSet(attribute.String("state", "user")) + // Deprecated: Use go.opentelemetry.io/otel/semconv instead. AttributeCPUTimeSystem = attribute.NewSet(attribute.String("state", "system")) - AttributeCPUTimeOther = attribute.NewSet(attribute.String("state", "other")) - AttributeCPUTimeIdle = attribute.NewSet(attribute.String("state", "idle")) + // Deprecated: Use go.opentelemetry.io/otel/semconv instead. + AttributeCPUTimeOther = attribute.NewSet(attribute.String("state", "other")) + // Deprecated: Use go.opentelemetry.io/otel/semconv instead. + AttributeCPUTimeIdle = attribute.NewSet(attribute.String("state", "idle")) // Attribute sets used for Memory measurements. + // Deprecated: Use go.opentelemetry.io/otel/semconv instead. AttributeMemoryAvailable = attribute.NewSet(attribute.String("state", "available")) - AttributeMemoryUsed = attribute.NewSet(attribute.String("state", "used")) + // Deprecated: Use go.opentelemetry.io/otel/semconv instead. + AttributeMemoryUsed = attribute.NewSet(attribute.String("state", "used")) // Attribute sets used for Network measurements. + // Deprecated: Use go.opentelemetry.io/otel/semconv instead. AttributeNetworkTransmit = attribute.NewSet(attribute.String("direction", "transmit")) - AttributeNetworkReceive = attribute.NewSet(attribute.String("direction", "receive")) + // Deprecated: Use go.opentelemetry.io/otel/semconv instead. + AttributeNetworkReceive = attribute.NewSet(attribute.String("direction", "receive")) ) // newConfig computes a config from a list of Options. @@ -108,13 +119,51 @@ func (h *host) register() error { var ( err error - processCPUTime metric.Float64ObservableCounter - hostCPUTime metric.Float64ObservableCounter - - hostMemoryUsage metric.Int64ObservableGauge - hostMemoryUtilization metric.Float64ObservableGauge - - networkIOUsage metric.Int64ObservableCounter + procCPUTime processconv.CPUTime + procCPUTimeModeUser = metric.WithAttributes( + procCPUTime.AttrCPUMode(processconv.CPUModeUser), + ) + procCPUTimeModeSystem = metric.WithAttributes( + procCPUTime.AttrCPUMode(processconv.CPUModeSystem), + ) + + cpuTime cpuconv.Time + cpuTimeModeUser = metric.WithAttributes( + cpuTime.AttrMode(cpuconv.ModeUser), + ) + cpuTimeModeSystem = metric.WithAttributes( + cpuTime.AttrMode(cpuconv.ModeSystem), + ) + cpuTimeModeIdle = metric.WithAttributes( + cpuTime.AttrMode(cpuconv.ModeIdle), + ) + cpuTimeModeOther = metric.WithAttributes( + cpuTime.AttrMode(cpuconv.ModeAttr("other")), + ) + + memUse systemconv.MemoryUsage + memUseStateFree = metric.WithAttributes( + memUse.AttrMemoryState(systemconv.MemoryStateFree), + ) + memUseStateUsed = metric.WithAttributes( + memUse.AttrMemoryState(systemconv.MemoryStateUsed), + ) + + memUtil systemconv.MemoryUtilization + memUtilStateFree = metric.WithAttributes( + memUtil.AttrMemoryState(systemconv.MemoryStateFree), + ) + memUtilStateUsed = metric.WithAttributes( + memUtil.AttrMemoryState(systemconv.MemoryStateUsed), + ) + + netIO systemconv.NetworkIO + netIOStateTransmit = metric.WithAttributes( + netIO.AttrNetworkIODirection(systemconv.NetworkIODirectionTransmit), + ) + netIOStateReceive = metric.WithAttributes( + netIO.AttrNetworkIODirection(systemconv.NetworkIODirectionReceive), + ) // lock prevents a race between batch observer and instrument registration. lock sync.Mutex @@ -132,56 +181,19 @@ func (h *host) register() error { lock.Lock() defer lock.Unlock() - // TODO: .time units are in seconds, but "unit" package does - // not include this string. - // https://github.com/open-telemetry/opentelemetry-specification/issues/705 - if processCPUTime, err = h.meter.Float64ObservableCounter( - "process.cpu.time", - metric.WithUnit("s"), - metric.WithDescription( - "Accumulated CPU time spent by this process attributed by state (User, System, ...)", - ), - ); err != nil { + if procCPUTime, err = processconv.NewCPUTime(h.meter); err != nil { return err } - - if hostCPUTime, err = h.meter.Float64ObservableCounter( - "system.cpu.time", - metric.WithUnit("s"), - metric.WithDescription( - "Accumulated CPU time spent by this host attributed by state (User, System, Other, Idle)", - ), - ); err != nil { + if cpuTime, err = cpuconv.NewTime(h.meter); err != nil { return err } - - if hostMemoryUsage, err = h.meter.Int64ObservableGauge( - "system.memory.usage", - metric.WithUnit("By"), - metric.WithDescription( - "Memory usage of this process attributed by memory state (Used, Available)", - ), - ); err != nil { + if memUse, err = systemconv.NewMemoryUsage(h.meter); err != nil { return err } - - if hostMemoryUtilization, err = h.meter.Float64ObservableGauge( - "system.memory.utilization", - metric.WithUnit("1"), - metric.WithDescription( - "Memory utilization of this process attributed by memory state (Used, Available)", - ), - ); err != nil { + if memUtil, err = systemconv.NewMemoryUtilization(h.meter); err != nil { return err } - - if networkIOUsage, err = h.meter.Int64ObservableCounter( - "system.network.io", - metric.WithUnit("By"), - metric.WithDescription( - "Bytes transferred attributed by direction (Transmit, Receive)", - ), - ); err != nil { + if netIO, err = systemconv.NewNetworkIO(h.meter); err != nil { return err } @@ -222,13 +234,11 @@ func (h *host) register() error { } hostTime := hostTimeSlice[0] - opt := metric.WithAttributeSet(AttributeCPUTimeUser) - o.ObserveFloat64(processCPUTime, processTimes.User, opt) - o.ObserveFloat64(hostCPUTime, hostTime.User, opt) + o.ObserveFloat64(procCPUTime.Inst(), processTimes.User, procCPUTimeModeUser) + o.ObserveFloat64(procCPUTime.Inst(), processTimes.System, procCPUTimeModeSystem) - opt = metric.WithAttributeSet(AttributeCPUTimeSystem) - o.ObserveFloat64(processCPUTime, processTimes.System, opt) - o.ObserveFloat64(hostCPUTime, hostTime.System, opt) + o.ObserveFloat64(cpuTime.Inst(), hostTime.User, cpuTimeModeUser) + o.ObserveFloat64(cpuTime.Inst(), hostTime.System, cpuTimeModeSystem) // TODO(#244): "other" is a placeholder for actually dealing // with these states. Do users actually want this @@ -245,40 +255,47 @@ func (h *host) register() error { hostTime.Guest + hostTime.GuestNice - opt = metric.WithAttributeSet(AttributeCPUTimeOther) - o.ObserveFloat64(hostCPUTime, other, opt) - opt = metric.WithAttributeSet(AttributeCPUTimeIdle) - o.ObserveFloat64(hostCPUTime, hostTime.Idle, opt) + o.ObserveFloat64(cpuTime.Inst(), other, cpuTimeModeOther) + o.ObserveFloat64(cpuTime.Inst(), hostTime.Idle, cpuTimeModeIdle) // Host memory usage - opt = metric.WithAttributeSet(AttributeMemoryUsed) - o.ObserveInt64(hostMemoryUsage, clampInt64(vmStats.Used), opt) - opt = metric.WithAttributeSet(AttributeMemoryAvailable) - o.ObserveInt64(hostMemoryUsage, clampInt64(vmStats.Available), opt) + o.ObserveInt64(memUse.Inst(), clampInt64(vmStats.Used), memUseStateUsed) + o.ObserveInt64(memUse.Inst(), clampInt64(vmStats.Available), memUseStateFree) // Host memory utilization - opt = metric.WithAttributeSet(AttributeMemoryUsed) - o.ObserveFloat64(hostMemoryUtilization, float64(vmStats.Used)/float64(vmStats.Total), opt) - opt = metric.WithAttributeSet(AttributeMemoryAvailable) - o.ObserveFloat64(hostMemoryUtilization, float64(vmStats.Available)/float64(vmStats.Total), opt) + o.ObserveFloat64( + memUtil.Inst(), + float64(vmStats.Used)/float64(vmStats.Total), memUtilStateUsed, + ) + o.ObserveFloat64( + memUtil.Inst(), + float64(vmStats.Available)/float64(vmStats.Total), + memUtilStateFree, + ) // Host network usage // // TODO: These can be broken down by network // interface, with similar questions to those posed // about per-CPU measurements above. - opt = metric.WithAttributeSet(AttributeNetworkTransmit) - o.ObserveInt64(networkIOUsage, clampInt64(ioStats[0].BytesSent), opt) - opt = metric.WithAttributeSet(AttributeNetworkReceive) - o.ObserveInt64(networkIOUsage, clampInt64(ioStats[0].BytesRecv), opt) + o.ObserveInt64( + netIO.Inst(), + clampInt64(ioStats[0].BytesSent), + netIOStateTransmit, + ) + o.ObserveInt64( + netIO.Inst(), + clampInt64(ioStats[0].BytesRecv), + netIOStateReceive, + ) return nil }, - processCPUTime, - hostCPUTime, - hostMemoryUsage, - hostMemoryUtilization, - networkIOUsage, + procCPUTime.Inst(), + cpuTime.Inst(), + memUse.Inst(), + memUtil.Inst(), + netIO.Inst(), ) if err != nil { return err diff --git a/instrumentation/host/host_test.go b/instrumentation/host/host_test.go index 713fe5ff74f..8d28b0c2726 100644 --- a/instrumentation/host/host_test.go +++ b/instrumentation/host/host_test.go @@ -3,9 +3,128 @@ package host_test -// TODO(#2755): Add integration tests for the host instrumentation. These tests -// depend on https://github.com/open-telemetry/opentelemetry-go/issues/3031 -// being resolved. -// -// The added tests will depend on the metric SDK. Therefore, they should be -// added to a sub-directory called "test" instead of this file. +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" + "go.opentelemetry.io/otel/semconv/v1.32.0/cpuconv" + "go.opentelemetry.io/otel/semconv/v1.32.0/processconv" + "go.opentelemetry.io/otel/semconv/v1.32.0/systemconv" + + "go.opentelemetry.io/contrib/instrumentation/host" +) + +func TestHostMetrics(t *testing.T) { + reader := metric.NewManualReader() + mp := metric.NewMeterProvider(metric.WithReader(reader)) + err := host.Start(host.WithMeterProvider(mp)) + require.NoError(t, err) + rm := metricdata.ResourceMetrics{} + err = reader.Collect(context.Background(), &rm) + require.NoError(t, err) + require.Len(t, rm.ScopeMetrics, 1) + + want := metricdata.ScopeMetrics{ + Scope: instrumentation.Scope{ + Name: host.ScopeName, + Version: host.Version(), + }, + Metrics: []metricdata.Metrics{ + { + Name: processconv.CPUTime{}.Name(), + Description: processconv.CPUTime{}.Description(), + Unit: processconv.CPUTime{}.Unit(), + Data: metricdata.Sum[float64]{ + DataPoints: []metricdata.DataPoint[float64]{ + {Attributes: attribute.NewSet( + processconv.CPUTime{}.AttrCPUMode(processconv.CPUModeUser), + )}, + {Attributes: attribute.NewSet( + processconv.CPUTime{}.AttrCPUMode(processconv.CPUModeSystem), + )}, + }, + Temporality: metricdata.CumulativeTemporality, + IsMonotonic: true, + }, + }, + { + Name: cpuconv.Time{}.Name(), + Description: cpuconv.Time{}.Description(), + Unit: cpuconv.Time{}.Unit(), + Data: metricdata.Sum[float64]{ + DataPoints: []metricdata.DataPoint[float64]{ + {Attributes: attribute.NewSet( + cpuconv.Time{}.AttrMode(cpuconv.ModeUser), + )}, + {Attributes: attribute.NewSet( + cpuconv.Time{}.AttrMode(cpuconv.ModeSystem), + )}, + {Attributes: attribute.NewSet( + cpuconv.Time{}.AttrMode(cpuconv.ModeAttr("other")), + )}, + {Attributes: attribute.NewSet( + cpuconv.Time{}.AttrMode(cpuconv.ModeIdle), + )}, + }, + Temporality: metricdata.CumulativeTemporality, + IsMonotonic: true, + }, + }, + { + Name: systemconv.MemoryUsage{}.Name(), + Description: systemconv.MemoryUsage{}.Description(), + Unit: systemconv.MemoryUsage{}.Unit(), + Data: metricdata.Gauge[int64]{ + DataPoints: []metricdata.DataPoint[int64]{ + {Attributes: attribute.NewSet( + systemconv.MemoryUsage{}.AttrMemoryState(systemconv.MemoryStateUsed), + )}, + {Attributes: attribute.NewSet( + systemconv.MemoryUsage{}.AttrMemoryState(systemconv.MemoryStateFree), + )}, + }, + }, + }, + { + Name: systemconv.MemoryUtilization{}.Name(), + // No description given in semantic conventions. + Unit: systemconv.MemoryUtilization{}.Unit(), + Data: metricdata.Gauge[float64]{ + DataPoints: []metricdata.DataPoint[float64]{ + {Attributes: attribute.NewSet( + systemconv.MemoryUtilization{}.AttrMemoryState(systemconv.MemoryStateUsed), + )}, + {Attributes: attribute.NewSet( + systemconv.MemoryUtilization{}.AttrMemoryState(systemconv.MemoryStateFree), + )}, + }, + }, + }, + { + Name: systemconv.NetworkIO{}.Name(), + // No description given in semantic conventions. + Unit: systemconv.NetworkIO{}.Unit(), + Data: metricdata.Sum[int64]{ + DataPoints: []metricdata.DataPoint[int64]{ + {Attributes: attribute.NewSet( + systemconv.NetworkIO{}.AttrNetworkIODirection(systemconv.NetworkIODirectionReceive), + )}, + {Attributes: attribute.NewSet( + systemconv.NetworkIO{}.AttrNetworkIODirection(systemconv.NetworkIODirectionTransmit), + )}, + }, + Temporality: metricdata.CumulativeTemporality, + IsMonotonic: true, + }, + }, + }, + } + metricdatatest.AssertEqual(t, want, rm.ScopeMetrics[0], metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreValue()) +}