Scott Baker | 2c1c482 | 2019-10-16 11:02:41 -0700 | [diff] [blame] | 1 | package metrics |
| 2 | |
| 3 | import ( |
| 4 | "runtime/debug" |
| 5 | "sync" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | var ( |
| 10 | debugMetrics struct { |
| 11 | GCStats struct { |
| 12 | LastGC Gauge |
| 13 | NumGC Gauge |
| 14 | Pause Histogram |
| 15 | //PauseQuantiles Histogram |
| 16 | PauseTotal Gauge |
| 17 | } |
| 18 | ReadGCStats Timer |
| 19 | } |
| 20 | gcStats debug.GCStats |
| 21 | registerDebugMetricsOnce = sync.Once{} |
| 22 | ) |
| 23 | |
| 24 | // Capture new values for the Go garbage collector statistics exported in |
| 25 | // debug.GCStats. This is designed to be called as a goroutine. |
| 26 | func CaptureDebugGCStats(r Registry, d time.Duration) { |
| 27 | for _ = range time.Tick(d) { |
| 28 | CaptureDebugGCStatsOnce(r) |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | // Capture new values for the Go garbage collector statistics exported in |
| 33 | // debug.GCStats. This is designed to be called in a background goroutine. |
| 34 | // Giving a registry which has not been given to RegisterDebugGCStats will |
| 35 | // panic. |
| 36 | // |
| 37 | // Be careful (but much less so) with this because debug.ReadGCStats calls |
| 38 | // the C function runtime·lock(runtime·mheap) which, while not a stop-the-world |
| 39 | // operation, isn't something you want to be doing all the time. |
| 40 | func CaptureDebugGCStatsOnce(r Registry) { |
| 41 | lastGC := gcStats.LastGC |
| 42 | t := time.Now() |
| 43 | debug.ReadGCStats(&gcStats) |
| 44 | debugMetrics.ReadGCStats.UpdateSince(t) |
| 45 | |
| 46 | debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano())) |
| 47 | debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC)) |
| 48 | if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) { |
| 49 | debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0])) |
| 50 | } |
| 51 | //debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles) |
| 52 | debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal)) |
| 53 | } |
| 54 | |
| 55 | // Register metrics for the Go garbage collector statistics exported in |
| 56 | // debug.GCStats. The metrics are named by their fully-qualified Go symbols, |
| 57 | // i.e. debug.GCStats.PauseTotal. |
| 58 | func RegisterDebugGCStats(r Registry) { |
| 59 | registerDebugMetricsOnce.Do(func() { |
| 60 | debugMetrics.GCStats.LastGC = NewGauge() |
| 61 | debugMetrics.GCStats.NumGC = NewGauge() |
| 62 | debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015)) |
| 63 | //debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015)) |
| 64 | debugMetrics.GCStats.PauseTotal = NewGauge() |
| 65 | debugMetrics.ReadGCStats = NewTimer() |
| 66 | |
| 67 | r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC) |
| 68 | r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC) |
| 69 | r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause) |
| 70 | //r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles) |
| 71 | r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal) |
| 72 | r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats) |
| 73 | }) |
| 74 | } |
| 75 | |
| 76 | // Allocate an initial slice for gcStats.Pause to avoid allocations during |
| 77 | // normal operation. |
| 78 | func init() { |
| 79 | gcStats.Pause = make([]time.Duration, 11) |
| 80 | } |