diff options
Diffstat (limited to 'vendor/go.opentelemetry.io/contrib/instrumentation/runtime/producer.go')
| -rw-r--r-- | vendor/go.opentelemetry.io/contrib/instrumentation/runtime/producer.go | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/producer.go b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/producer.go new file mode 100644 index 000000000..82426b3f2 --- /dev/null +++ b/vendor/go.opentelemetry.io/contrib/instrumentation/runtime/producer.go @@ -0,0 +1,120 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package runtime // import "go.opentelemetry.io/contrib/instrumentation/runtime" + +import ( + "context" + "errors" + "math" + "runtime/metrics" + "sync" + "time" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/instrumentation" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +var startTime time.Time + +func init() { + startTime = time.Now() +} + +var histogramMetrics = []string{goSchedLatencies} + +// Producer is a metric.Producer, which provides precomputed histogram metrics from the go runtime. +type Producer struct { + lock sync.Mutex + collector *goCollector +} + +var _ metric.Producer = (*Producer)(nil) + +// NewProducer creates a Producer which provides precomputed histogram metrics from the go runtime. +func NewProducer(opts ...ProducerOption) *Producer { + c := newProducerConfig(opts...) + return &Producer{ + collector: newCollector(c.MinimumReadMemStatsInterval, histogramMetrics), + } +} + +// Produce returns precomputed histogram metrics from the go runtime, or an error if unsuccessful. +func (p *Producer) Produce(context.Context) ([]metricdata.ScopeMetrics, error) { + p.lock.Lock() + p.collector.refresh() + schedHist := p.collector.getHistogram(goSchedLatencies) + p.lock.Unlock() + // Use the last collection time (which may or may not be now) for the timestamp. + histDp := convertRuntimeHistogram(schedHist, p.collector.lastCollect) + if len(histDp) == 0 { + return nil, errors.New("unable to obtain go.schedule.duration metric from the runtime") + } + return []metricdata.ScopeMetrics{ + { + Scope: instrumentation.Scope{ + Name: ScopeName, + Version: Version(), + }, + Metrics: []metricdata.Metrics{ + { + Name: "go.schedule.duration", + Description: "The time goroutines have spent in the scheduler in a runnable state before actually running.", + Unit: "s", + Data: metricdata.Histogram[float64]{ + Temporality: metricdata.CumulativeTemporality, + DataPoints: histDp, + }, + }, + }, + }, + }, nil +} + +var emptySet = attribute.EmptySet() + +func convertRuntimeHistogram(runtimeHist *metrics.Float64Histogram, ts time.Time) []metricdata.HistogramDataPoint[float64] { + if runtimeHist == nil { + return nil + } + bounds := runtimeHist.Buckets + counts := runtimeHist.Counts + if len(bounds) < 2 { + // runtime histograms are guaranteed to have at least two bucket boundaries. + return nil + } + // trim the first bucket since it is a lower bound. OTel histogram boundaries only have an upper bound. + bounds = bounds[1:] + if bounds[len(bounds)-1] == math.Inf(1) { + // trim the last bucket if it is +Inf, since the +Inf boundary is implicit in OTel. + bounds = bounds[:len(bounds)-1] + } else { + // if the last bucket is not +Inf, append an extra zero count since + // the implicit +Inf bucket won't have any observations. + counts = append(counts, 0) + } + count := uint64(0) + sum := float64(0) + for i, c := range counts { + count += c + // This computed sum is an underestimate, since it assumes each + // observation happens at the bucket's lower bound. + if i > 0 && count != 0 { + sum += bounds[i-1] * float64(count) + } + } + + return []metricdata.HistogramDataPoint[float64]{ + { + StartTime: startTime, + Count: count, + Sum: sum, + Time: ts, + Bounds: bounds, + BucketCounts: counts, + Attributes: *emptySet, + }, + } +} |
