summaryrefslogtreecommitdiff
path: root/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime/runtime.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime/runtime.go')
-rw-r--r--vendor/go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime/runtime.go296
1 files changed, 296 insertions, 0 deletions
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
new file mode 100644
index 000000000..86c7c9e34
--- /dev/null
+++ b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime/runtime.go
@@ -0,0 +1,296 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+package deprecatedruntime // import "go.opentelemetry.io/contrib/instrumentation/runtime/internal/deprecatedruntime"
+
+import (
+ "context"
+ "math"
+ goruntime "runtime"
+ "sync"
+ "time"
+
+ "go.opentelemetry.io/otel/metric"
+)
+
+// Runtime reports the work-in-progress conventional runtime metrics specified by OpenTelemetry.
+type runtime struct {
+ minimumReadMemStatsInterval time.Duration
+ meter metric.Meter
+}
+
+// Start initializes reporting of runtime metrics using the supplied config.
+func Start(meter metric.Meter, minimumReadMemStatsInterval time.Duration) error {
+ r := &runtime{
+ meter: meter,
+ minimumReadMemStatsInterval: minimumReadMemStatsInterval,
+ }
+ return r.register()
+}
+
+func (r *runtime) register() error {
+ startTime := time.Now()
+ uptime, err := r.meter.Int64ObservableCounter(
+ "runtime.uptime",
+ metric.WithUnit("ms"),
+ metric.WithDescription("Milliseconds since application was initialized"),
+ )
+ if err != nil {
+ return err
+ }
+
+ goroutines, err := r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.goroutines",
+ metric.WithDescription("Number of goroutines that currently exist"),
+ )
+ if err != nil {
+ return err
+ }
+
+ cgoCalls, err := r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.cgo.calls",
+ metric.WithDescription("Number of cgo calls made by the current process"),
+ )
+ if err != nil {
+ return err
+ }
+
+ _, err = r.meter.RegisterCallback(
+ func(ctx context.Context, o metric.Observer) error {
+ o.ObserveInt64(uptime, time.Since(startTime).Milliseconds())
+ o.ObserveInt64(goroutines, int64(goruntime.NumGoroutine()))
+ o.ObserveInt64(cgoCalls, goruntime.NumCgoCall())
+ return nil
+ },
+ uptime,
+ goroutines,
+ cgoCalls,
+ )
+ if err != nil {
+ return err
+ }
+
+ return r.registerMemStats()
+}
+
+func (r *runtime) registerMemStats() error {
+ var (
+ err error
+
+ heapAlloc metric.Int64ObservableUpDownCounter
+ heapIdle metric.Int64ObservableUpDownCounter
+ heapInuse metric.Int64ObservableUpDownCounter
+ heapObjects metric.Int64ObservableUpDownCounter
+ heapReleased metric.Int64ObservableUpDownCounter
+ heapSys metric.Int64ObservableUpDownCounter
+ liveObjects metric.Int64ObservableUpDownCounter
+
+ // TODO: is ptrLookups useful? I've not seen a value
+ // other than zero.
+ ptrLookups metric.Int64ObservableCounter
+
+ gcCount metric.Int64ObservableCounter
+ pauseTotalNs metric.Int64ObservableCounter
+ gcPauseNs metric.Int64Histogram
+
+ lastNumGC uint32
+ lastMemStats time.Time
+ memStats goruntime.MemStats
+
+ // lock prevents a race between batch observer and instrument registration.
+ lock sync.Mutex
+ )
+
+ lock.Lock()
+ defer lock.Unlock()
+
+ if heapAlloc, err = r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.mem.heap_alloc",
+ metric.WithUnit("By"),
+ metric.WithDescription("Bytes of allocated heap objects"),
+ ); err != nil {
+ return err
+ }
+
+ if heapIdle, err = r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.mem.heap_idle",
+ metric.WithUnit("By"),
+ metric.WithDescription("Bytes in idle (unused) spans"),
+ ); err != nil {
+ return err
+ }
+
+ if heapInuse, err = r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.mem.heap_inuse",
+ metric.WithUnit("By"),
+ metric.WithDescription("Bytes in in-use spans"),
+ ); err != nil {
+ return err
+ }
+
+ if heapObjects, err = r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.mem.heap_objects",
+ metric.WithDescription("Number of allocated heap objects"),
+ ); err != nil {
+ return err
+ }
+
+ // FYI see https://github.com/golang/go/issues/32284 to help
+ // understand the meaning of this value.
+ if heapReleased, err = r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.mem.heap_released",
+ metric.WithUnit("By"),
+ metric.WithDescription("Bytes of idle spans whose physical memory has been returned to the OS"),
+ ); err != nil {
+ return err
+ }
+
+ if heapSys, err = r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.mem.heap_sys",
+ metric.WithUnit("By"),
+ metric.WithDescription("Bytes of heap memory obtained from the OS"),
+ ); err != nil {
+ return err
+ }
+
+ if ptrLookups, err = r.meter.Int64ObservableCounter(
+ "process.runtime.go.mem.lookups",
+ metric.WithDescription("Number of pointer lookups performed by the runtime"),
+ ); err != nil {
+ return err
+ }
+
+ if liveObjects, err = r.meter.Int64ObservableUpDownCounter(
+ "process.runtime.go.mem.live_objects",
+ metric.WithDescription("Number of live objects is the number of cumulative Mallocs - Frees"),
+ ); err != nil {
+ return err
+ }
+
+ if gcCount, err = r.meter.Int64ObservableCounter(
+ "process.runtime.go.gc.count",
+ metric.WithDescription("Number of completed garbage collection cycles"),
+ ); err != nil {
+ return err
+ }
+
+ // Note that the following could be derived as a sum of
+ // individual pauses, but we may lose individual pauses if the
+ // observation interval is too slow.
+ if pauseTotalNs, err = r.meter.Int64ObservableCounter(
+ "process.runtime.go.gc.pause_total_ns",
+ // TODO: nanoseconds units
+ metric.WithDescription("Cumulative nanoseconds in GC stop-the-world pauses since the program started"),
+ ); err != nil {
+ return err
+ }
+
+ if gcPauseNs, err = r.meter.Int64Histogram(
+ "process.runtime.go.gc.pause_ns",
+ // TODO: nanoseconds units
+ metric.WithDescription("Amount of nanoseconds in GC stop-the-world pauses"),
+ ); err != nil {
+ return err
+ }
+
+ _, err = r.meter.RegisterCallback(
+ func(ctx context.Context, o metric.Observer) error {
+ lock.Lock()
+ defer lock.Unlock()
+
+ now := time.Now()
+ if now.Sub(lastMemStats) >= r.minimumReadMemStatsInterval {
+ goruntime.ReadMemStats(&memStats)
+ lastMemStats = now
+ }
+
+ 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, clampUint64(memStats.PauseTotalNs))
+
+ computeGCPauses(ctx, gcPauseNs, memStats.PauseNs[:], lastNumGC, memStats.NumGC)
+
+ lastNumGC = memStats.NumGC
+
+ return nil
+ },
+ heapAlloc,
+ heapIdle,
+ heapInuse,
+ heapObjects,
+ heapReleased,
+ heapSys,
+ liveObjects,
+
+ ptrLookups,
+
+ gcCount,
+ pauseTotalNs,
+ )
+ if err != nil {
+ return err
+ }
+ 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,
+ circular []uint64,
+ lastNumGC, currentNumGC uint32,
+) {
+ delta := int(int64(currentNumGC) - int64(lastNumGC))
+
+ if delta == 0 {
+ return
+ }
+
+ if delta >= len(circular) {
+ // There were > 256 collections, some may have been lost.
+ recordGCPauses(ctx, recorder, circular)
+ return
+ }
+
+ n := len(circular)
+ if n < 0 {
+ // Only the case in error situations.
+ return
+ }
+
+ length := uint64(n) // nolint: gosec // n >= 0
+
+ i := uint64(lastNumGC) % length
+ j := uint64(currentNumGC) % length
+
+ if j < i { // wrap around the circular buffer
+ recordGCPauses(ctx, recorder, circular[i:])
+ recordGCPauses(ctx, recorder, circular[:j])
+ return
+ }
+
+ recordGCPauses(ctx, recorder, circular[i:j])
+}
+
+func recordGCPauses(
+ ctx context.Context,
+ recorder metric.Int64Histogram,
+ pauses []uint64,
+) {
+ for _, pause := range pauses {
+ recorder.Record(ctx, clampUint64(pause))
+ }
+}