diff --git a/go.mod b/go.mod index 4b35677be..4249c02b8 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( go.etcd.io/etcd/raft/v3 v3.5.15 go.etcd.io/etcd/server/v3 v3.5.15 go.opentelemetry.io/contrib/instrumentation/host v0.54.0 - go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0 + go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0 go.opentelemetry.io/otel v1.29.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 diff --git a/go.sum b/go.sum index 2b8e1c2f4..da6529216 100644 --- a/go.sum +++ b/go.sum @@ -374,8 +374,8 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/contrib/instrumentation/host v0.54.0 h1:ForLwWOQDIhZZdaf09H0Cy82jBWIQhWLn/tqzclD36E= go.opentelemetry.io/contrib/instrumentation/host v0.54.0/go.mod h1:LV9KQI6PdSf+TTg4npgD7FzPo/80CuyLekiHiCd1nt8= -go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0 h1:nOlJEAJyrcy8hexK65M+dsCHIx7CVVbybcFDNkcTcAc= -go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0/go.mod h1:u79lGGIlkg3Ryw425RbMjEkGYNxSnXRyR286O840+u4= +go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0 h1:KD+8SJvRaW9n0vE0UgkytT207J3CmV1hGf9GYYU73ns= +go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0/go.mod h1:/CsTuLR28IN3Vn13YEc72HljfHiGOMXiCbl4xiCSDhA= go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/doc.go b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/doc.go index 5cbcf1e45..2b5e78686 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/doc.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/doc.go @@ -19,4 +19,16 @@ // runtime.go.mem.heap_sys (bytes) Bytes of heap memory obtained from the OS // runtime.go.mem.live_objects - Number of live objects is the number of cumulative Mallocs - Frees // runtime.uptime (ms) Milliseconds since application was initialized +// +// When the OTEL_GO_X_DEPRECATED_RUNTIME_METRICS environment variable is set to +// false, the metrics produced are: +// +// go.memory.used By Memory used by the Go runtime. +// go.memory.limit By Go runtime memory limit configured by the user, if a limit exists. +// go.memory.allocated By Memory allocated to the heap by the application. +// go.memory.allocations {allocation} Count of allocations to the heap by the application. +// go.memory.gc.goal By Heap size target for the end of the GC cycle. +// go.goroutine.count {goroutine} Count of live goroutines. +// go.processor.limit {thread} The number of OS threads that can execute user-level Go code simultaneously. +// go.config.gogc % Heap size target percentage configured by the user, otherwise 100. package runtime // import "go.opentelemetry.io/contrib/instrumentation/runtime" diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime/runtime.go b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime/runtime.go index 487072e3b..86c7c9e34 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime/runtime.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime/runtime.go @@ -5,6 +5,7 @@ package deprecatedruntime // import "go.opentelemetry.io/contrib/instrumentation import ( "context" + "math" goruntime "runtime" "sync" "time" @@ -203,16 +204,16 @@ func (r *runtime) registerMemStats() error { lastMemStats = now } - o.ObserveInt64(heapAlloc, int64(memStats.HeapAlloc)) - o.ObserveInt64(heapIdle, int64(memStats.HeapIdle)) - o.ObserveInt64(heapInuse, int64(memStats.HeapInuse)) - o.ObserveInt64(heapObjects, int64(memStats.HeapObjects)) - o.ObserveInt64(heapReleased, int64(memStats.HeapReleased)) - o.ObserveInt64(heapSys, int64(memStats.HeapSys)) - o.ObserveInt64(liveObjects, int64(memStats.Mallocs-memStats.Frees)) - o.ObserveInt64(ptrLookups, int64(memStats.Lookups)) + o.ObserveInt64(heapAlloc, clampUint64(memStats.HeapAlloc)) + o.ObserveInt64(heapIdle, clampUint64(memStats.HeapIdle)) + o.ObserveInt64(heapInuse, clampUint64(memStats.HeapInuse)) + o.ObserveInt64(heapObjects, clampUint64(memStats.HeapObjects)) + o.ObserveInt64(heapReleased, clampUint64(memStats.HeapReleased)) + o.ObserveInt64(heapSys, clampUint64(memStats.HeapSys)) + o.ObserveInt64(liveObjects, clampUint64(memStats.Mallocs-memStats.Frees)) + o.ObserveInt64(ptrLookups, clampUint64(memStats.Lookups)) o.ObserveInt64(gcCount, int64(memStats.NumGC)) - o.ObserveInt64(pauseTotalNs, int64(memStats.PauseTotalNs)) + o.ObserveInt64(pauseTotalNs, clampUint64(memStats.PauseTotalNs)) computeGCPauses(ctx, gcPauseNs, memStats.PauseNs[:], lastNumGC, memStats.NumGC) @@ -239,6 +240,13 @@ func (r *runtime) registerMemStats() error { return nil } +func clampUint64(v uint64) int64 { + if v > math.MaxInt64 { + return math.MaxInt64 + } + return int64(v) // nolint: gosec // Overflow checked above. +} + func computeGCPauses( ctx context.Context, recorder metric.Int64Histogram, @@ -257,10 +265,16 @@ func computeGCPauses( return } - length := uint32(len(circular)) + n := len(circular) + if n < 0 { + // Only the case in error situations. + return + } + + length := uint64(n) // nolint: gosec // n >= 0 - i := lastNumGC % length - j := currentNumGC % length + i := uint64(lastNumGC) % length + j := uint64(currentNumGC) % length if j < i { // wrap around the circular buffer recordGCPauses(ctx, recorder, circular[i:]) @@ -277,6 +291,6 @@ func recordGCPauses( pauses []uint64, ) { for _, pause := range pauses { - recorder.Record(ctx, int64(pause)) + recorder.Record(ctx, clampUint64(pause)) } } diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/options.go b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/options.go new file mode 100644 index 000000000..30046ab35 --- /dev/null +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/options.go @@ -0,0 +1,76 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package runtime // import "go.opentelemetry.io/contrib/instrumentation/runtime" + +import ( + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/metric" +) + +// config contains optional settings for reporting runtime metrics. +type config struct { + // MinimumReadMemStatsInterval sets the minimum interval + // between calls to runtime.ReadMemStats(). Negative values + // are ignored. + MinimumReadMemStatsInterval time.Duration + + // MeterProvider sets the metric.MeterProvider. If nil, the global + // Provider will be used. + MeterProvider metric.MeterProvider +} + +// Option supports configuring optional settings for runtime metrics. +type Option interface { + apply(*config) +} + +// DefaultMinimumReadMemStatsInterval is the default minimum interval +// between calls to runtime.ReadMemStats(). Use the +// WithMinimumReadMemStatsInterval() option to modify this setting in +// Start(). +const DefaultMinimumReadMemStatsInterval time.Duration = 15 * time.Second + +// WithMinimumReadMemStatsInterval sets a minimum interval between calls to +// runtime.ReadMemStats(), which is a relatively expensive call to make +// frequently. This setting is ignored when `d` is negative. +func WithMinimumReadMemStatsInterval(d time.Duration) Option { + return minimumReadMemStatsIntervalOption(d) +} + +type minimumReadMemStatsIntervalOption time.Duration + +func (o minimumReadMemStatsIntervalOption) apply(c *config) { + if o >= 0 { + c.MinimumReadMemStatsInterval = time.Duration(o) + } +} + +// WithMeterProvider sets the Metric implementation to use for +// reporting. If this option is not used, the global metric.MeterProvider +// will be used. `provider` must be non-nil. +func WithMeterProvider(provider metric.MeterProvider) Option { + return metricProviderOption{provider} +} + +type metricProviderOption struct{ metric.MeterProvider } + +func (o metricProviderOption) apply(c *config) { + if o.MeterProvider != nil { + c.MeterProvider = o.MeterProvider + } +} + +// newConfig computes a config from the supplied Options. +func newConfig(opts ...Option) config { + c := config{ + MeterProvider: otel.GetMeterProvider(), + MinimumReadMemStatsInterval: DefaultMinimumReadMemStatsInterval, + } + for _, opt := range opts { + opt.apply(&c) + } + return c +} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/runtime.go b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/runtime.go index 3c520a499..f50d11895 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/runtime.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/runtime.go @@ -4,9 +4,14 @@ package runtime // import "go.opentelemetry.io/contrib/instrumentation/runtime" import ( + "context" + "math" + "runtime/metrics" + "sync" "time" "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime" @@ -16,70 +21,18 @@ import ( // ScopeName is the instrumentation scope name. const ScopeName = "go.opentelemetry.io/contrib/instrumentation/runtime" -// config contains optional settings for reporting runtime metrics. -type config struct { - // MinimumReadMemStatsInterval sets the minimum interval - // between calls to runtime.ReadMemStats(). Negative values - // are ignored. - MinimumReadMemStatsInterval time.Duration - - // MeterProvider sets the metric.MeterProvider. If nil, the global - // Provider will be used. - MeterProvider metric.MeterProvider -} - -// Option supports configuring optional settings for runtime metrics. -type Option interface { - apply(*config) -} - -// DefaultMinimumReadMemStatsInterval is the default minimum interval -// between calls to runtime.ReadMemStats(). Use the -// WithMinimumReadMemStatsInterval() option to modify this setting in -// Start(). -const DefaultMinimumReadMemStatsInterval time.Duration = 15 * time.Second - -// WithMinimumReadMemStatsInterval sets a minimum interval between calls to -// runtime.ReadMemStats(), which is a relatively expensive call to make -// frequently. This setting is ignored when `d` is negative. -func WithMinimumReadMemStatsInterval(d time.Duration) Option { - return minimumReadMemStatsIntervalOption(d) -} - -type minimumReadMemStatsIntervalOption time.Duration - -func (o minimumReadMemStatsIntervalOption) apply(c *config) { - if o >= 0 { - c.MinimumReadMemStatsInterval = time.Duration(o) - } -} - -// WithMeterProvider sets the Metric implementation to use for -// reporting. If this option is not used, the global metric.MeterProvider -// will be used. `provider` must be non-nil. -func WithMeterProvider(provider metric.MeterProvider) Option { - return metricProviderOption{provider} -} - -type metricProviderOption struct{ metric.MeterProvider } - -func (o metricProviderOption) apply(c *config) { - if o.MeterProvider != nil { - c.MeterProvider = o.MeterProvider - } -} - -// newConfig computes a config from the supplied Options. -func newConfig(opts ...Option) config { - c := config{ - MeterProvider: otel.GetMeterProvider(), - MinimumReadMemStatsInterval: DefaultMinimumReadMemStatsInterval, - } - for _, opt := range opts { - opt.apply(&c) - } - return c -} +const ( + goTotalMemory = "/memory/classes/total:bytes" + goMemoryReleased = "/memory/classes/heap/released:bytes" + goHeapMemory = "/memory/classes/heap/stacks:bytes" + goMemoryLimit = "/gc/gomemlimit:bytes" + goMemoryAllocated = "/gc/heap/allocs:bytes" + goMemoryAllocations = "/gc/heap/allocs:objects" + goMemoryGoal = "/gc/heap/goal:bytes" + goGoroutines = "/sched/goroutines:goroutines" + goMaxProcs = "/sched/gomaxprocs:threads" + goConfigGC = "/gc/gogc:percent" +) // Start initializes reporting of runtime metrics using the supplied config. func Start(opts ...Option) error { @@ -97,6 +50,179 @@ func Start(opts ...Option) error { if x.DeprecatedRuntimeMetrics.Enabled() { return deprecatedruntime.Start(meter, c.MinimumReadMemStatsInterval) } - // TODO (#5655) Implement new runtime conventions + memoryUsedInstrument, err := meter.Int64ObservableUpDownCounter( + "go.memory.used", + metric.WithUnit("By"), + metric.WithDescription("Memory used by the Go runtime."), + ) + if err != nil { + return err + } + memoryLimitInstrument, err := meter.Int64ObservableUpDownCounter( + "go.memory.limit", + metric.WithUnit("By"), + metric.WithDescription("Go runtime memory limit configured by the user, if a limit exists."), + ) + if err != nil { + return err + } + memoryAllocatedInstrument, err := meter.Int64ObservableCounter( + "go.memory.allocated", + metric.WithUnit("By"), + metric.WithDescription("Memory allocated to the heap by the application."), + ) + if err != nil { + return err + } + memoryAllocationsInstrument, err := meter.Int64ObservableCounter( + "go.memory.allocations", + metric.WithUnit("{allocation}"), + metric.WithDescription("Count of allocations to the heap by the application."), + ) + if err != nil { + return err + } + memoryGCGoalInstrument, err := meter.Int64ObservableUpDownCounter( + "go.memory.gc.goal", + metric.WithUnit("By"), + metric.WithDescription("Heap size target for the end of the GC cycle."), + ) + if err != nil { + return err + } + goroutineCountInstrument, err := meter.Int64ObservableUpDownCounter( + "go.goroutine.count", + metric.WithUnit("{goroutine}"), + metric.WithDescription("Count of live goroutines."), + ) + if err != nil { + return err + } + processorLimitInstrument, err := meter.Int64ObservableUpDownCounter( + "go.processor.limit", + metric.WithUnit("{thread}"), + metric.WithDescription("The number of OS threads that can execute user-level Go code simultaneously."), + ) + if err != nil { + return err + } + gogcConfigInstrument, err := meter.Int64ObservableUpDownCounter( + "go.config.gogc", + metric.WithUnit("%"), + metric.WithDescription("Heap size target percentage configured by the user, otherwise 100."), + ) + if err != nil { + return err + } + + otherMemoryOpt := metric.WithAttributeSet( + attribute.NewSet(attribute.String("go.memory.type", "other")), + ) + stackMemoryOpt := metric.WithAttributeSet( + attribute.NewSet(attribute.String("go.memory.type", "stack")), + ) + collector := newCollector(c.MinimumReadMemStatsInterval) + var lock sync.Mutex + _, err = meter.RegisterCallback( + func(ctx context.Context, o metric.Observer) error { + lock.Lock() + defer lock.Unlock() + collector.refresh() + stackMemory := collector.get(goHeapMemory) + o.ObserveInt64(memoryUsedInstrument, stackMemory, stackMemoryOpt) + totalMemory := collector.get(goTotalMemory) - collector.get(goMemoryReleased) + otherMemory := totalMemory - stackMemory + o.ObserveInt64(memoryUsedInstrument, otherMemory, otherMemoryOpt) + // Only observe the limit metric if a limit exists + if limit := collector.get(goMemoryLimit); limit != math.MaxInt64 { + o.ObserveInt64(memoryLimitInstrument, limit) + } + o.ObserveInt64(memoryAllocatedInstrument, collector.get(goMemoryAllocated)) + o.ObserveInt64(memoryAllocationsInstrument, collector.get(goMemoryAllocations)) + o.ObserveInt64(memoryGCGoalInstrument, collector.get(goMemoryGoal)) + o.ObserveInt64(goroutineCountInstrument, collector.get(goGoroutines)) + o.ObserveInt64(processorLimitInstrument, collector.get(goMaxProcs)) + o.ObserveInt64(gogcConfigInstrument, collector.get(goConfigGC)) + return nil + }, + memoryUsedInstrument, + memoryLimitInstrument, + memoryAllocatedInstrument, + memoryAllocationsInstrument, + memoryGCGoalInstrument, + goroutineCountInstrument, + processorLimitInstrument, + gogcConfigInstrument, + ) + if err != nil { + return err + } + // TODO (#5655) support go.schedule.duration return nil } + +// These are the metrics we actually fetch from the go runtime. +var runtimeMetrics = []string{ + goTotalMemory, + goMemoryReleased, + goHeapMemory, + goMemoryLimit, + goMemoryAllocated, + goMemoryAllocations, + goMemoryGoal, + goGoroutines, + goMaxProcs, + goConfigGC, +} + +type goCollector struct { + // now is used to replace the implementation of time.Now for testing + now func() time.Time + // lastCollect tracks the last time metrics were refreshed + lastCollect time.Time + // minimumInterval is the minimum amount of time between calls to metrics.Read + minimumInterval time.Duration + // sampleBuffer is populated by runtime/metrics + sampleBuffer []metrics.Sample + // sampleMap allows us to easily get the value of a single metric + sampleMap map[string]*metrics.Sample +} + +func newCollector(minimumInterval time.Duration) *goCollector { + g := &goCollector{ + sampleBuffer: make([]metrics.Sample, 0, len(runtimeMetrics)), + sampleMap: make(map[string]*metrics.Sample, len(runtimeMetrics)), + minimumInterval: minimumInterval, + now: time.Now, + } + for _, runtimeMetric := range runtimeMetrics { + g.sampleBuffer = append(g.sampleBuffer, metrics.Sample{Name: runtimeMetric}) + // sampleMap references a position in the sampleBuffer slice. If an + // element is appended to sampleBuffer, it must be added to sampleMap + // for the sample to be accessible in sampleMap. + g.sampleMap[runtimeMetric] = &g.sampleBuffer[len(g.sampleBuffer)-1] + } + return g +} + +func (g *goCollector) refresh() { + now := g.now() + if now.Sub(g.lastCollect) < g.minimumInterval { + // refresh was invoked more frequently than allowed by the minimum + // interval. Do nothing. + return + } + metrics.Read(g.sampleBuffer) + g.lastCollect = now +} + +func (g *goCollector) get(name string) int64 { + if s, ok := g.sampleMap[name]; ok && s.Value.Kind() == metrics.KindUint64 { + v := s.Value.Uint64() + if v > math.MaxInt64 { + return math.MaxInt64 + } + return int64(v) // nolint: gosec // Overflow checked above. + } + return 0 +} diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/version.go b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/version.go index 2b0b6c667..64f57a99f 100644 --- a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/version.go +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/version.go @@ -5,7 +5,7 @@ package runtime // import "go.opentelemetry.io/contrib/instrumentation/runtime" // Version is the current release version of the runtime instrumentation. func Version() string { - return "0.53.0" + return "0.54.0" // This string is updated by the pre_release.sh script during release } diff --git a/vendor/modules.txt b/vendor/modules.txt index fe5efa88c..910f102b0 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -382,7 +382,7 @@ go.etcd.io/etcd/server/v3/wal/walpb # go.opentelemetry.io/contrib/instrumentation/host v0.54.0 ## explicit; go 1.21 go.opentelemetry.io/contrib/instrumentation/host -# go.opentelemetry.io/contrib/instrumentation/runtime v0.53.0 +# go.opentelemetry.io/contrib/instrumentation/runtime v0.54.0 ## explicit; go 1.21 go.opentelemetry.io/contrib/instrumentation/runtime go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime