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