1package metrics 2 3import ( 4 "runtime" 5 "time" 6) 7 8func (m *Metrics) SetGauge(key []string, val float32) { 9 if m.HostName != "" && m.EnableHostname { 10 key = insert(0, m.HostName, key) 11 } 12 if m.EnableTypePrefix { 13 key = insert(0, "gauge", key) 14 } 15 if m.ServiceName != "" { 16 key = insert(0, m.ServiceName, key) 17 } 18 m.sink.SetGauge(key, val) 19} 20 21func (m *Metrics) EmitKey(key []string, val float32) { 22 if m.EnableTypePrefix { 23 key = insert(0, "kv", key) 24 } 25 if m.ServiceName != "" { 26 key = insert(0, m.ServiceName, key) 27 } 28 m.sink.EmitKey(key, val) 29} 30 31func (m *Metrics) IncrCounter(key []string, val float32) { 32 if m.EnableTypePrefix { 33 key = insert(0, "counter", key) 34 } 35 if m.ServiceName != "" { 36 key = insert(0, m.ServiceName, key) 37 } 38 m.sink.IncrCounter(key, val) 39} 40 41func (m *Metrics) AddSample(key []string, val float32) { 42 if m.EnableTypePrefix { 43 key = insert(0, "sample", key) 44 } 45 if m.ServiceName != "" { 46 key = insert(0, m.ServiceName, key) 47 } 48 m.sink.AddSample(key, val) 49} 50 51func (m *Metrics) MeasureSince(key []string, start time.Time) { 52 if m.EnableTypePrefix { 53 key = insert(0, "timer", key) 54 } 55 if m.ServiceName != "" { 56 key = insert(0, m.ServiceName, key) 57 } 58 now := time.Now() 59 elapsed := now.Sub(start) 60 msec := float32(elapsed.Nanoseconds()) / float32(m.TimerGranularity) 61 m.sink.AddSample(key, msec) 62} 63 64// Periodically collects runtime stats to publish 65func (m *Metrics) collectStats() { 66 for { 67 time.Sleep(m.ProfileInterval) 68 m.emitRuntimeStats() 69 } 70} 71 72// Emits various runtime statsitics 73func (m *Metrics) emitRuntimeStats() { 74 // Export number of Goroutines 75 numRoutines := runtime.NumGoroutine() 76 m.SetGauge([]string{"runtime", "num_goroutines"}, float32(numRoutines)) 77 78 // Export memory stats 79 var stats runtime.MemStats 80 runtime.ReadMemStats(&stats) 81 m.SetGauge([]string{"runtime", "alloc_bytes"}, float32(stats.Alloc)) 82 m.SetGauge([]string{"runtime", "sys_bytes"}, float32(stats.Sys)) 83 m.SetGauge([]string{"runtime", "malloc_count"}, float32(stats.Mallocs)) 84 m.SetGauge([]string{"runtime", "free_count"}, float32(stats.Frees)) 85 m.SetGauge([]string{"runtime", "heap_objects"}, float32(stats.HeapObjects)) 86 m.SetGauge([]string{"runtime", "total_gc_pause_ns"}, float32(stats.PauseTotalNs)) 87 m.SetGauge([]string{"runtime", "total_gc_runs"}, float32(stats.NumGC)) 88 89 // Export info about the last few GC runs 90 num := stats.NumGC 91 92 // Handle wrap around 93 if num < m.lastNumGC { 94 m.lastNumGC = 0 95 } 96 97 // Ensure we don't scan more than 256 98 if num-m.lastNumGC >= 256 { 99 m.lastNumGC = num - 255 100 } 101 102 for i := m.lastNumGC; i < num; i++ { 103 pause := stats.PauseNs[i%256] 104 m.AddSample([]string{"runtime", "gc_pause_ns"}, float32(pause)) 105 } 106 m.lastNumGC = num 107} 108 109// Inserts a string value at an index into the slice 110func insert(i int, v string, s []string) []string { 111 s = append(s, "") 112 copy(s[i+1:], s[i:]) 113 s[i] = v 114 return s 115} 116