Use the runtime/metrics package for the Go collector for 1.17+ (#955)
This change introduces use of the runtime/metrics package in place of
runtime.MemStats for Go 1.17 or later. The runtime/metrics package was
introduced in Go 1.16, but not all the old metrics were accounted for
until 1.17.
The runtime/metrics package offers several advantages over using
runtime.MemStats:
* The list of metrics and their descriptions are machine-readable,
allowing new metrics to get added without any additional work.
* Detailed histogram-based metrics are now available, offering much
deeper insights into the Go runtime.
* The runtime/metrics API is significantly more efficient than
runtime.MemStats, even with the additional metrics added, because
it does not require any stop-the-world events.
That being said, integrating the package comes with some caveats, some
of which were discussed in #842. Namely:
* The old MemStats-based metrics need to continue working, so they're
exported under their old names backed by equivalent runtime/metrics
metrics.
* Earlier versions of Go need to continue working, so the old code
remains, but behind a build tag.
Finally, a few notes about the implementation:
* This change includes a whole bunch of refactoring to avoid significant
code duplication.
* This change adds a new histogram metric type specifically optimized
for runtime/metrics histograms. This type's methods also include
additional logic to deal with differences in bounds conventions.
* This change makes a whole bunch of decisions about how runtime/metrics
names are translated.
* This change adds a `go generate` script to generate a list of expected
runtime/metrics names for a given Go version for auditing. Users of
new versions of Go will transparently be allowed to use new metrics,
however.
Signed-off-by: Michael Anthony Knyszek <mknyszek@google.com>
2022-01-16 19:41:56 +03:00
|
|
|
// Copyright 2021 The Prometheus Authors
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
//go:build !go1.17
|
|
|
|
// +build !go1.17
|
|
|
|
|
|
|
|
package prometheus
|
|
|
|
|
|
|
|
import (
|
|
|
|
"runtime"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type goCollector struct {
|
|
|
|
base baseGoCollector
|
|
|
|
|
|
|
|
// ms... are memstats related.
|
|
|
|
msLast *runtime.MemStats // Previously collected memstats.
|
|
|
|
msLastTimestamp time.Time
|
|
|
|
msMtx sync.Mutex // Protects msLast and msLastTimestamp.
|
|
|
|
msMetrics memStatsMetrics
|
|
|
|
msRead func(*runtime.MemStats) // For mocking in tests.
|
|
|
|
msMaxWait time.Duration // Wait time for fresh memstats.
|
|
|
|
msMaxAge time.Duration // Maximum allowed age of old memstats.
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewGoCollector is the obsolete version of collectors.NewGoCollector.
|
|
|
|
// See there for documentation.
|
|
|
|
//
|
|
|
|
// Deprecated: Use collectors.NewGoCollector instead.
|
|
|
|
func NewGoCollector() Collector {
|
2022-04-13 11:55:22 +03:00
|
|
|
msMetrics := goRuntimeMemStats()
|
|
|
|
msMetrics = append(msMetrics, struct {
|
|
|
|
desc *Desc
|
|
|
|
eval func(*runtime.MemStats) float64
|
|
|
|
valType ValueType
|
|
|
|
}{
|
|
|
|
// This metric is omitted in Go1.17+, see https://github.com/prometheus/client_golang/issues/842#issuecomment-861812034
|
|
|
|
desc: NewDesc(
|
|
|
|
memstatNamespace("gc_cpu_fraction"),
|
|
|
|
"The fraction of this program's available CPU time used by the GC since the program started.",
|
|
|
|
nil, nil,
|
|
|
|
),
|
|
|
|
eval: func(ms *runtime.MemStats) float64 { return ms.GCCPUFraction },
|
|
|
|
valType: GaugeValue,
|
|
|
|
})
|
Use the runtime/metrics package for the Go collector for 1.17+ (#955)
This change introduces use of the runtime/metrics package in place of
runtime.MemStats for Go 1.17 or later. The runtime/metrics package was
introduced in Go 1.16, but not all the old metrics were accounted for
until 1.17.
The runtime/metrics package offers several advantages over using
runtime.MemStats:
* The list of metrics and their descriptions are machine-readable,
allowing new metrics to get added without any additional work.
* Detailed histogram-based metrics are now available, offering much
deeper insights into the Go runtime.
* The runtime/metrics API is significantly more efficient than
runtime.MemStats, even with the additional metrics added, because
it does not require any stop-the-world events.
That being said, integrating the package comes with some caveats, some
of which were discussed in #842. Namely:
* The old MemStats-based metrics need to continue working, so they're
exported under their old names backed by equivalent runtime/metrics
metrics.
* Earlier versions of Go need to continue working, so the old code
remains, but behind a build tag.
Finally, a few notes about the implementation:
* This change includes a whole bunch of refactoring to avoid significant
code duplication.
* This change adds a new histogram metric type specifically optimized
for runtime/metrics histograms. This type's methods also include
additional logic to deal with differences in bounds conventions.
* This change makes a whole bunch of decisions about how runtime/metrics
names are translated.
* This change adds a `go generate` script to generate a list of expected
runtime/metrics names for a given Go version for auditing. Users of
new versions of Go will transparently be allowed to use new metrics,
however.
Signed-off-by: Michael Anthony Knyszek <mknyszek@google.com>
2022-01-16 19:41:56 +03:00
|
|
|
return &goCollector{
|
|
|
|
base: newBaseGoCollector(),
|
|
|
|
msLast: &runtime.MemStats{},
|
|
|
|
msRead: runtime.ReadMemStats,
|
|
|
|
msMaxWait: time.Second,
|
|
|
|
msMaxAge: 5 * time.Minute,
|
2022-04-13 11:55:22 +03:00
|
|
|
msMetrics: msMetrics,
|
Use the runtime/metrics package for the Go collector for 1.17+ (#955)
This change introduces use of the runtime/metrics package in place of
runtime.MemStats for Go 1.17 or later. The runtime/metrics package was
introduced in Go 1.16, but not all the old metrics were accounted for
until 1.17.
The runtime/metrics package offers several advantages over using
runtime.MemStats:
* The list of metrics and their descriptions are machine-readable,
allowing new metrics to get added without any additional work.
* Detailed histogram-based metrics are now available, offering much
deeper insights into the Go runtime.
* The runtime/metrics API is significantly more efficient than
runtime.MemStats, even with the additional metrics added, because
it does not require any stop-the-world events.
That being said, integrating the package comes with some caveats, some
of which were discussed in #842. Namely:
* The old MemStats-based metrics need to continue working, so they're
exported under their old names backed by equivalent runtime/metrics
metrics.
* Earlier versions of Go need to continue working, so the old code
remains, but behind a build tag.
Finally, a few notes about the implementation:
* This change includes a whole bunch of refactoring to avoid significant
code duplication.
* This change adds a new histogram metric type specifically optimized
for runtime/metrics histograms. This type's methods also include
additional logic to deal with differences in bounds conventions.
* This change makes a whole bunch of decisions about how runtime/metrics
names are translated.
* This change adds a `go generate` script to generate a list of expected
runtime/metrics names for a given Go version for auditing. Users of
new versions of Go will transparently be allowed to use new metrics,
however.
Signed-off-by: Michael Anthony Knyszek <mknyszek@google.com>
2022-01-16 19:41:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Describe returns all descriptions of the collector.
|
|
|
|
func (c *goCollector) Describe(ch chan<- *Desc) {
|
|
|
|
c.base.Describe(ch)
|
|
|
|
for _, i := range c.msMetrics {
|
|
|
|
ch <- i.desc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect returns the current state of all metrics of the collector.
|
|
|
|
func (c *goCollector) Collect(ch chan<- Metric) {
|
|
|
|
var (
|
|
|
|
ms = &runtime.MemStats{}
|
|
|
|
done = make(chan struct{})
|
|
|
|
)
|
|
|
|
// Start reading memstats first as it might take a while.
|
|
|
|
go func() {
|
|
|
|
c.msRead(ms)
|
|
|
|
c.msMtx.Lock()
|
|
|
|
c.msLast = ms
|
|
|
|
c.msLastTimestamp = time.Now()
|
|
|
|
c.msMtx.Unlock()
|
|
|
|
close(done)
|
|
|
|
}()
|
|
|
|
|
|
|
|
// Collect base non-memory metrics.
|
|
|
|
c.base.Collect(ch)
|
|
|
|
|
|
|
|
timer := time.NewTimer(c.msMaxWait)
|
|
|
|
select {
|
|
|
|
case <-done: // Our own ReadMemStats succeeded in time. Use it.
|
|
|
|
timer.Stop() // Important for high collection frequencies to not pile up timers.
|
|
|
|
c.msCollect(ch, ms)
|
|
|
|
return
|
|
|
|
case <-timer.C: // Time out, use last memstats if possible. Continue below.
|
|
|
|
}
|
|
|
|
c.msMtx.Lock()
|
|
|
|
if time.Since(c.msLastTimestamp) < c.msMaxAge {
|
|
|
|
// Last memstats are recent enough. Collect from them under the lock.
|
|
|
|
c.msCollect(ch, c.msLast)
|
|
|
|
c.msMtx.Unlock()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// If we are here, the last memstats are too old or don't exist. We have
|
|
|
|
// to wait until our own ReadMemStats finally completes. For that to
|
|
|
|
// happen, we have to release the lock.
|
|
|
|
c.msMtx.Unlock()
|
|
|
|
<-done
|
|
|
|
c.msCollect(ch, ms)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *goCollector) msCollect(ch chan<- Metric, ms *runtime.MemStats) {
|
|
|
|
for _, i := range c.msMetrics {
|
|
|
|
ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms))
|
|
|
|
}
|
|
|
|
}
|