├── base ├── runtime_no_cgo.go ├── runtime_cgo.go ├── runtime_no_gccpufraction.go ├── runtime_gccpufraction.go ├── debug_test.go ├── runtime_test.go ├── debug.go └── runtime.go ├── version.go ├── perfcounter.json ├── example ├── perfcounter.json ├── .gitignore ├── README.md ├── scripts │ └── debug ├── main.go └── control ├── .gitignore ├── doc ├── BENCH.md └── API.md ├── metrics.go ├── config.go ├── http.go ├── README.md ├── falcon.go └── perfcounter.go /base/runtime_no_cgo.go: -------------------------------------------------------------------------------- 1 | // +build !cgo appengine 2 | 3 | package base 4 | 5 | func numCgoCall() int64 { 6 | return 0 7 | } 8 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package goperfcounter 2 | 3 | // changelog: 4 | // 0.0.1: init project 5 | const ( 6 | VERSION = "0.0.1" 7 | ) 8 | -------------------------------------------------------------------------------- /base/runtime_cgo.go: -------------------------------------------------------------------------------- 1 | // +build cgo 2 | // +build !appengine 3 | 4 | package base 5 | 6 | import "runtime" 7 | 8 | func numCgoCall() int64 { 9 | return runtime.NumCgoCall() 10 | } 11 | -------------------------------------------------------------------------------- /base/runtime_no_gccpufraction.go: -------------------------------------------------------------------------------- 1 | // +build !go1.5 2 | 3 | package base 4 | 5 | import "runtime" 6 | 7 | func gcCPUFraction(memStats *runtime.MemStats) float64 { 8 | return 0 9 | } 10 | -------------------------------------------------------------------------------- /base/runtime_gccpufraction.go: -------------------------------------------------------------------------------- 1 | // +build go1.5 2 | 3 | package base 4 | 5 | import "runtime" 6 | 7 | func gcCPUFraction(memStats *runtime.MemStats) float64 { 8 | return memStats.GCCPUFraction 9 | } 10 | -------------------------------------------------------------------------------- /perfcounter.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug": false, 3 | "hostname": "", 4 | "tags": "", 5 | "step": 60, 6 | "bases": ["runtime"], 7 | "push": { 8 | "enabled": true, 9 | "api": "http://127.0.0.1:6060/api/push" 10 | }, 11 | "http": { 12 | "enabled": false, 13 | "listen": "0.0.0.0:2015" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/perfcounter.json: -------------------------------------------------------------------------------- 1 | { 2 | "debug": true, 3 | "hostname": "", 4 | "tags": "cop=xiaomi,owt=inf,pdl=falcon,module=perfcounter", 5 | "step": 20, 6 | "bases":["runtime","debug"], 7 | "push": { 8 | "enabled": true, 9 | "api": "http://127.0.0.1:6060/api/push" 10 | }, 11 | "http": { 12 | "enabled": true, 13 | "listen": "0.0.0.0:2015" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | /.idea 26 | .DS_Store 27 | *.bak 28 | 29 | # Project 30 | .gitversion 31 | /*_test.go -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | /.idea 26 | .DS_Store 27 | *.bak 28 | 29 | # Project 30 | perfcounter 31 | .gitversion 32 | /*_test.go 33 | /var -------------------------------------------------------------------------------- /doc/BENCH.md: -------------------------------------------------------------------------------- 1 | Bench Test 2 | ==== 3 | 4 | Run 5 | ---- 6 | 7 | ```bash 8 | cd $GOPATH/src/github.com/niean/goperfcounter && go test -test.bench=".*" 9 | 10 | ``` 11 | 12 | Result 13 | ---- 14 | 15 | ``` 16 | PASS 17 | BenchmarkMeter 2000000 918 ns/op 18 | BenchmarkMeterMulti 2000000 916 ns/op 19 | BenchmarkGauge 5000000 342 ns/op 20 | BenchmarkGaugeMulti 5000000 333 ns/op 21 | BenchmarkHistogram 2000000 800 ns/op 22 | BenchmarkHistogramMulti 2000000 780 ns/op 23 | ok github.com/niean/goperfcounter 14.568s 24 | 25 | ``` -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | Example Of GoPerfcounter 2 | ===== 3 | 4 | 5 | 这里是,goperfcounter提供的一个完整的例子。 6 | 7 | 按照如下指令,运行这个例子。 8 | 9 | ```bash 10 | # install 11 | cd $GOPATH/src/github.com/niean 12 | git clone https://github.com/niean/goperfcounter.git 13 | cd $GOPATH/src/github.com/niean/goperfcounter 14 | go get ./... 15 | 16 | # run 17 | cd $GOPATH/src/github.com/niean/goperfcounter/example/scripts 18 | ./debug build && ./debug start 19 | 20 | # proc 21 | ./debug proc metrics/json # list all metrics in json 22 | ./debug proc metrics/falcon # list all metrics in falcon-model 23 | 24 | ``` 25 | -------------------------------------------------------------------------------- /example/scripts/debug: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ## test home 3 | testdir=$(cd $(dirname $0)/; pwd) 4 | ## word home 5 | workdir=$(dirname $testdir) 6 | cd $workdir 7 | 8 | control=./control 9 | 10 | cfg=./perfcounter.json 11 | httpport=`cat perfcounter.json | grep -A3 "\"http\":" | grep "\"listen\"" | cut -d\" -f4 | cut -d: -f2` 12 | httpprex="127.0.0.1:$httpport" 13 | 14 | ## pfc 15 | function pfc(){ 16 | args=$@ 17 | for i in $@; do 18 | url="$url/$i" 19 | done 20 | echo $url 21 | curl -s "$httpprex$url" | python -m json.tool 22 | } 23 | 24 | ## control 25 | function control(){ 26 | $control $@ 27 | } 28 | 29 | action=$1 30 | case $action in 31 | "proc") 32 | pfc "pfc" $@ 33 | ;; 34 | "pfc") 35 | pfc $@ 36 | ;; 37 | "") 38 | pfc "pfc/proc/metrics/gauge,counter,meter,histogram/json" 39 | ;; 40 | *) 41 | control $@ 42 | ;; 43 | esac 44 | 45 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | 7 | pfc "github.com/niean/goperfcounter" 8 | ) 9 | 10 | func main() { 11 | go basic() // 基础统计器 12 | go senior() // 高级统计器 13 | select {} 14 | } 15 | 16 | func basic() { 17 | for _ = range time.Tick(time.Second * time.Duration(10)) { 18 | // (常用) Meter,用于累加求和、计算变化率。使用场景如,统计首页访问次数、gvm的CG次数等。 19 | pv := int64(rand.Int() % 100) 20 | pfc.Meter("test.meter", pv) 21 | pfc.Meter("test.meter.2", pv-50) 22 | 23 | // (常用) Gauge,用于保存数值类型的瞬时记录值。使用场景如,统计队列长度、统计CPU使用率等 24 | queueSize := int64(rand.Int()%100 - 50) 25 | pfc.Gauge("test.gauge", queueSize) 26 | 27 | cpuUtil := float64(rand.Int()%10000) / float64(100) 28 | pfc.GaugeFloat64("test.gauge.float64", cpuUtil) 29 | } 30 | } 31 | 32 | func senior() { 33 | for _ = range time.Tick(time.Second) { 34 | // Histogram,使用指数衰减抽样的方式,计算被统计对象的概率分布情况。使用场景如,统计主页访问延时的概率分布 35 | delay := int64(rand.Int() % 100) 36 | pfc.Histogram("test.histogram", delay) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /base/debug_test.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "runtime" 5 | "runtime/debug" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func BenchmarkDebugGCStats(b *testing.B) { 11 | r := NewRegistry() 12 | RegisterDebugGCStats(r) 13 | b.ResetTimer() 14 | for i := 0; i < b.N; i++ { 15 | CaptureDebugGCStatsOnce(r) 16 | } 17 | } 18 | 19 | func TestDebugGCStatsBlocking(t *testing.T) { 20 | if g := runtime.GOMAXPROCS(0); g < 2 { 21 | t.Skipf("skipping TestDebugGCMemStatsBlocking with GOMAXPROCS=%d\n", g) 22 | return 23 | } 24 | ch := make(chan int) 25 | go testDebugGCStatsBlocking(ch) 26 | var gcStats debug.GCStats 27 | t0 := time.Now() 28 | debug.ReadGCStats(&gcStats) 29 | t1 := time.Now() 30 | t.Log("i++ during debug.ReadGCStats:", <-ch) 31 | go testDebugGCStatsBlocking(ch) 32 | d := t1.Sub(t0) 33 | t.Log(d) 34 | time.Sleep(d) 35 | t.Log("i++ during time.Sleep:", <-ch) 36 | } 37 | 38 | func testDebugGCStatsBlocking(ch chan int) { 39 | i := 0 40 | for { 41 | select { 42 | case ch <- i: 43 | return 44 | default: 45 | i++ 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /metrics.go: -------------------------------------------------------------------------------- 1 | package goperfcounter 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/niean/go-metrics-lite" 7 | "github.com/niean/goperfcounter/base" 8 | ) 9 | 10 | var ( 11 | gpGaugeFloat64 = metrics.NewRegistry() 12 | gpCounter = metrics.NewRegistry() 13 | gpMeter = metrics.NewRegistry() 14 | gpHistogram = metrics.NewRegistry() 15 | gpDebug = metrics.NewRegistry() 16 | gpRuntime = metrics.NewRegistry() 17 | gpSelf = metrics.NewRegistry() 18 | values = make(map[string]metrics.Registry) //readonly,mappings of metrics 19 | ) 20 | 21 | func init() { 22 | values["gauge"] = gpGaugeFloat64 23 | values["counter"] = gpCounter 24 | values["meter"] = gpMeter 25 | values["histogram"] = gpHistogram 26 | values["debug"] = gpDebug 27 | values["runtime"] = gpRuntime 28 | values["self"] = gpSelf 29 | } 30 | 31 | // 32 | func rawMetric(types []string) map[string]interface{} { 33 | data := make(map[string]interface{}) 34 | for _, mtype := range types { 35 | if v, ok := values[mtype]; ok { 36 | data[mtype] = v.Values() 37 | } 38 | } 39 | return data 40 | } 41 | 42 | func rawMetrics() map[string]interface{} { 43 | data := make(map[string]interface{}) 44 | for key, v := range values { 45 | data[key] = v.Values() 46 | } 47 | return data 48 | } 49 | 50 | func rawSizes() map[string]int64 { 51 | data := map[string]int64{} 52 | all := int64(0) 53 | for key, v := range values { 54 | kv := v.Size() 55 | all += kv 56 | data[key] = kv 57 | } 58 | data["all"] = all 59 | return data 60 | } 61 | 62 | func collectBase(bases []string) { 63 | // start base collect after 30sec 64 | time.Sleep(time.Duration(30) * time.Second) 65 | 66 | if contains(bases, "debug") { 67 | base.RegisterAndCaptureDebugGCStats(gpDebug, 5e9) 68 | } 69 | 70 | if contains(bases, "runtime") { 71 | base.RegisterAndCaptureRuntimeMemStats(gpRuntime, 5e9) 72 | } 73 | } 74 | 75 | func contains(bases []string, name string) bool { 76 | for _, n := range bases { 77 | if n == name { 78 | return true 79 | } 80 | } 81 | return false 82 | } 83 | -------------------------------------------------------------------------------- /base/runtime_test.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func BenchmarkRuntimeMemStats(b *testing.B) { 10 | r := NewRegistry() 11 | RegisterRuntimeMemStats(r) 12 | b.ResetTimer() 13 | for i := 0; i < b.N; i++ { 14 | CaptureRuntimeMemStatsOnce(r) 15 | } 16 | } 17 | 18 | func TestRuntimeMemStats(t *testing.T) { 19 | r := NewRegistry() 20 | RegisterRuntimeMemStats(r) 21 | CaptureRuntimeMemStatsOnce(r) 22 | zero := runtimeMetrics.MemStats.PauseNs.Count() // Get a "zero" since GC may have run before these tests. 23 | runtime.GC() 24 | CaptureRuntimeMemStatsOnce(r) 25 | if count := runtimeMetrics.MemStats.PauseNs.Count(); 1 != count-zero { 26 | t.Fatal(count - zero) 27 | } 28 | runtime.GC() 29 | runtime.GC() 30 | CaptureRuntimeMemStatsOnce(r) 31 | if count := runtimeMetrics.MemStats.PauseNs.Count(); 3 != count-zero { 32 | t.Fatal(count - zero) 33 | } 34 | for i := 0; i < 256; i++ { 35 | runtime.GC() 36 | } 37 | CaptureRuntimeMemStatsOnce(r) 38 | if count := runtimeMetrics.MemStats.PauseNs.Count(); 259 != count-zero { 39 | t.Fatal(count - zero) 40 | } 41 | for i := 0; i < 257; i++ { 42 | runtime.GC() 43 | } 44 | CaptureRuntimeMemStatsOnce(r) 45 | if count := runtimeMetrics.MemStats.PauseNs.Count(); 515 != count-zero { // We lost one because there were too many GCs between captures. 46 | t.Fatal(count - zero) 47 | } 48 | } 49 | 50 | func TestRuntimeMemStatsBlocking(t *testing.T) { 51 | if g := runtime.GOMAXPROCS(0); g < 2 { 52 | t.Skipf("skipping TestRuntimeMemStatsBlocking with GOMAXPROCS=%d\n", g) 53 | } 54 | ch := make(chan int) 55 | go testRuntimeMemStatsBlocking(ch) 56 | var memStats runtime.MemStats 57 | t0 := time.Now() 58 | runtime.ReadMemStats(&memStats) 59 | t1 := time.Now() 60 | t.Log("i++ during runtime.ReadMemStats:", <-ch) 61 | go testRuntimeMemStatsBlocking(ch) 62 | d := t1.Sub(t0) 63 | t.Log(d) 64 | time.Sleep(d) 65 | t.Log("i++ during time.Sleep:", <-ch) 66 | } 67 | 68 | func testRuntimeMemStatsBlocking(ch chan int) { 69 | i := 0 70 | for { 71 | select { 72 | case ch <- i: 73 | return 74 | default: 75 | i++ 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /base/debug.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "runtime/debug" 5 | "time" 6 | 7 | "github.com/niean/go-metrics-lite" 8 | ) 9 | 10 | var ( 11 | debugMetrics struct { 12 | GCStats struct { 13 | LastGC metrics.Gauge 14 | NumGC metrics.Gauge 15 | Pause metrics.Histogram 16 | PauseTotal metrics.Gauge 17 | } 18 | ReadGCStats metrics.Histogram 19 | } 20 | gcStats = debug.GCStats{Pause: make([]time.Duration, 11)} 21 | ) 22 | 23 | func RegisterAndCaptureDebugGCStats(r metrics.Registry, d time.Duration) { 24 | registerDebugGCStats(r) 25 | go captureDebugGCStats(r, d) 26 | } 27 | 28 | // Capture new values for the Go garbage collector statistics exported in 29 | // debug.GCStats. This is designed to be called as a goroutine. 30 | func captureDebugGCStats(r metrics.Registry, d time.Duration) { 31 | for _ = range time.Tick(d) { 32 | captureDebugGCStatsOnce(r) 33 | } 34 | } 35 | 36 | // Capture new values for the Go garbage collector statistics exported in 37 | // debug.GCStats. This is designed to be called in a background goroutine. 38 | // Giving a registry which has not been given to RegisterDebugGCStats will 39 | // panic. 40 | // 41 | // Be careful (but much less so) with this because debug.ReadGCStats calls 42 | // the C function runtime·lock(runtime·mheap) which, while not a stop-the-world 43 | // operation, isn't something you want to be doing all the time. 44 | func captureDebugGCStatsOnce(r metrics.Registry) { 45 | lastGC := gcStats.LastGC 46 | t := time.Now() 47 | debug.ReadGCStats(&gcStats) 48 | debugMetrics.ReadGCStats.Update(int64(time.Since(t))) 49 | 50 | debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano())) 51 | debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC)) 52 | if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) { 53 | debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0])) 54 | } 55 | //debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles) 56 | debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal)) 57 | } 58 | 59 | // Register metrics for the Go garbage collector statistics exported in 60 | // debug.GCStats. The metrics are named by their fully-qualified Go symbols, 61 | // i.e. debug.GCStats.PauseTotal. 62 | func registerDebugGCStats(r metrics.Registry) { 63 | debugMetrics.GCStats.LastGC = metrics.NewGauge() 64 | debugMetrics.GCStats.NumGC = metrics.NewGauge() 65 | debugMetrics.GCStats.Pause = metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015)) 66 | debugMetrics.GCStats.PauseTotal = metrics.NewGauge() 67 | debugMetrics.ReadGCStats = metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015)) 68 | 69 | r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC) 70 | r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC) 71 | r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause) 72 | r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal) 73 | r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats) 74 | } 75 | -------------------------------------------------------------------------------- /example/control: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | workspace=$(cd $(dirname $0) && pwd) 3 | cd $workspace 4 | 5 | module=perfcounter 6 | app=$module 7 | conf=cfg.json 8 | pidfile=var/app.pid 9 | logfile=var/app.log 10 | gitversion=.gitversion 11 | 12 | mkdir -p var &>/dev/null 13 | 14 | 15 | ## build & pack 16 | function build() { 17 | #update_gitversion 18 | go build -o $app main.go 19 | sc=$? 20 | if [ $sc -ne 0 ];then 21 | echo "build error" 22 | exit $sc 23 | else 24 | echo -n "build ok, vsn=" 25 | #version 26 | fi 27 | } 28 | 29 | function pack() { 30 | build 31 | version=`./$app -v` 32 | tar zcvf $app-$version.tar.gz control $app cfg.example.json $gitversion ./scripts/debug 33 | } 34 | 35 | function packbin() { 36 | build 37 | version=`./$app -v` 38 | tar zcvf $app-bin-$version.tar.gz $app gitversion 39 | } 40 | 41 | ## opt 42 | function start() { 43 | check_pid 44 | running=$? 45 | if [ $running -gt 0 ];then 46 | echo -n "started, pid=" 47 | cat $pidfile 48 | return 1 49 | fi 50 | 51 | nohup ./$app >>$logfile 2>&1 & 52 | echo $! > $pidfile 53 | echo "start ok, pid=$!" 54 | } 55 | 56 | function stop() { 57 | pid=`cat $pidfile` 58 | kill $pid 59 | echo "stoped" 60 | } 61 | 62 | function restart() { 63 | stop && sleep 1 && start 64 | } 65 | 66 | function reload() { 67 | build && stop && sleep 1 && start && sleep 1 && printf "\n" && tailf 68 | } 69 | 70 | ## other 71 | function status() { 72 | check_pid 73 | running=$? 74 | if [ $running -gt 0 ];then 75 | echo -n "running, pid=" 76 | cat $pidfile 77 | else 78 | echo "stoped" 79 | fi 80 | } 81 | 82 | function version() { 83 | v=`./$app -v` 84 | if [ -f $gitversion ];then 85 | g=`cat $gitversion` 86 | fi 87 | echo "$v $g" 88 | } 89 | 90 | function tailf() { 91 | tail -f $logfile 92 | } 93 | 94 | ## internal 95 | function check_pid() { 96 | if [ -f $pidfile ];then 97 | pid=`cat $pidfile` 98 | if [ -n $pid ]; then 99 | running=`ps -p $pid|grep -v "PID TTY" |wc -l` 100 | return $running 101 | fi 102 | fi 103 | return 0 104 | } 105 | 106 | function update_gitversion() { 107 | git log -1 --pretty=%h > $gitversion 108 | } 109 | 110 | ## usage 111 | function usage() { 112 | echo "$0 build|pack|packbin|start|stop|restart|reload|status|tail|version" 113 | } 114 | 115 | ## main 116 | action=$1 117 | case $action in 118 | ## build 119 | "build" ) 120 | build 121 | ;; 122 | "pack" ) 123 | pack 124 | ;; 125 | "packbin" ) 126 | packbin 127 | ;; 128 | ## opt 129 | "start" ) 130 | start 131 | ;; 132 | "stop" ) 133 | stop 134 | ;; 135 | "restart" ) 136 | restart 137 | ;; 138 | "reload" ) 139 | reload 140 | ;; 141 | ## other 142 | "status" ) 143 | status 144 | ;; 145 | "version" ) 146 | version 147 | ;; 148 | "tail" ) 149 | tailf 150 | ;; 151 | * ) 152 | usage 153 | ;; 154 | esac 155 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package goperfcounter 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | type GlobalConfig struct { 13 | Debug bool `json:"debug"` 14 | Hostname string `json:"hostname"` 15 | Tags string `json:"tags"` 16 | Step int64 `json:"step"` 17 | Bases []string `json:"bases"` 18 | Push *PushConfig `json:"push"` 19 | Http *HttpConfig `json:"http"` 20 | } 21 | type HttpConfig struct { 22 | Enabled bool `json:"enabled"` 23 | Listen string `json:"listen"` 24 | } 25 | type PushConfig struct { 26 | Enabled bool `json:"enabled"` 27 | Api string `json:"api"` 28 | } 29 | 30 | var ( 31 | configFn = "./perfcounter.json" 32 | defaultTags = "" 33 | defaultStep = int64(60) //time in sec 34 | defaultBases = []string{} 35 | defaultPush = &PushConfig{Enabled: true, Api: "http://127.0.0.1:1988/v1/push"} 36 | defaultHttp = &HttpConfig{Enabled: false, Listen: ""} 37 | ) 38 | 39 | var ( 40 | cfg *GlobalConfig 41 | cfgLock = new(sync.RWMutex) 42 | ) 43 | 44 | // 45 | func config() *GlobalConfig { 46 | cfgLock.RLock() 47 | defer cfgLock.RUnlock() 48 | return cfg 49 | } 50 | 51 | func loadConfig() error { 52 | if !isFileExist(configFn) { 53 | return fmt.Errorf("config file not found: %s", configFn) 54 | } 55 | 56 | c, err := parseConfig(configFn) 57 | if err != nil { 58 | return err 59 | } 60 | 61 | updateConfig(c) 62 | return nil 63 | } 64 | 65 | func setDefaultConfig() { 66 | dcfg := defaultConfig() 67 | updateConfig(dcfg) 68 | } 69 | 70 | func defaultConfig() GlobalConfig { 71 | return GlobalConfig{ 72 | Debug: false, 73 | Hostname: defaultHostname(), 74 | Tags: defaultTags, 75 | Step: defaultStep, 76 | Bases: defaultBases, 77 | Push: defaultPush, 78 | Http: defaultHttp, 79 | } 80 | } 81 | 82 | // 83 | func updateConfig(c GlobalConfig) { 84 | nc := formatConfig(c) 85 | cfgLock.Lock() 86 | defer cfgLock.Unlock() 87 | cfg = &nc 88 | } 89 | 90 | func formatConfig(c GlobalConfig) GlobalConfig { 91 | nc := c 92 | if nc.Hostname == "" { 93 | nc.Hostname = defaultHostname() 94 | } 95 | if nc.Step < 1 { 96 | nc.Step = defaultStep 97 | } 98 | if nc.Tags != "" { 99 | tagsOk := true 100 | tagsSlice := strings.Split(nc.Tags, ",") 101 | for _, tag := range tagsSlice { 102 | kv := strings.Split(tag, "=") 103 | if len(kv) != 2 || kv[0] == "name" { // name是保留tag 104 | tagsOk = false 105 | break 106 | } 107 | } 108 | if !tagsOk { 109 | nc.Tags = defaultTags 110 | } 111 | } 112 | if nc.Push.Enabled && nc.Push.Api == "" { 113 | nc.Push = defaultPush 114 | } 115 | if len(nc.Bases) < 1 { 116 | nc.Bases = defaultBases 117 | } 118 | 119 | return nc 120 | } 121 | 122 | func parseConfig(cfg string) (GlobalConfig, error) { 123 | var c GlobalConfig 124 | 125 | if cfg == "" { 126 | return c, fmt.Errorf("config file not found") 127 | } 128 | 129 | configContent, err := readFileString(cfg) 130 | if err != nil { 131 | return c, fmt.Errorf("read config file %s error: %v", cfg, err.Error()) 132 | } 133 | 134 | err = json.Unmarshal([]byte(configContent), &c) 135 | if err != nil { 136 | return c, fmt.Errorf("parse config file %s error: %v", cfg, err.Error()) 137 | } 138 | return c, nil 139 | } 140 | 141 | func defaultHostname() string { 142 | hostname, _ := os.Hostname() 143 | return hostname 144 | } 145 | 146 | func isFileExist(fn string) bool { 147 | _, err := os.Stat(fn) 148 | return err == nil || os.IsExist(err) 149 | } 150 | 151 | func readFileString(fn string) (string, error) { 152 | b, err := ioutil.ReadFile(fn) 153 | if err != nil { 154 | return "", err 155 | } 156 | return strings.TrimSpace(string(b)), nil 157 | } 158 | -------------------------------------------------------------------------------- /doc/API.md: -------------------------------------------------------------------------------- 1 | API 2 | ==== 3 | 4 | gopfercounter提供了几种类型的统计器,分比为Gauge、Meter、Histogram。统计器的含义,参见[java-metrics](http://metrics.dropwizard.io/3.1.0/getting-started/)。 5 | 6 | 7 | Gauge 8 | ---- 9 | 10 | A gauge metric is an instantaneous reading of a particular value 11 | 12 | ##### 设置 13 | + 接口: Gauge(name string, value int64) 14 | + 参数: value - 记录的数值 15 | + 例子: 16 | 17 | ```go 18 | Gauge("queueSize", int64(13)) 19 | ``` 20 | 21 | ##### 设置 22 | + 接口: SetGaugeValue(name string, value float64) 23 | + Alias: GaugeFloat64(name string, value float64) 24 | + 参数: value - 记录的数值 25 | + 例子: 26 | 27 | ```go 28 | GaugeFloat64("requestRate", float64(13.14)) 29 | SetGaugeValue("requestRate", float64(13.14)) 30 | ``` 31 | 32 | ##### 获取 33 | + 接口: GetGaugeValue(name string) float64 34 | + 例子: 35 | 36 | ```go 37 | reqRate := GetGaugeValue("requestRate") 38 | ``` 39 | 40 | Meter 41 | ---- 42 | 43 | A meter metric which measures mean throughput and one-, five-, and fifteen-minute exponentially-weighted moving average throughputs. 44 | 45 | 关于EWMA, 点击[这里](http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) 46 | 47 | ##### 设置 48 | + 接口: SetMeterCount(name string, value int64) 49 | + Alias: Meter(name string, value int64) 50 | + 参数: value - 该事件发生的次数 51 | + 例子: 52 | 53 | ```go 54 | // 页面访问次数统计,每来一次访问,pv加1 55 | SetMeterCount("pageView", int64(1)) 56 | ``` 57 | 58 | ##### 获取累计的值 59 | + 接口: GetMeterCount(name string) int64 60 | + 例子: 61 | 62 | ```go 63 | // 获取pv次数的总和 64 | pvSum := GetMeterCount("pageView") 65 | ``` 66 | 67 | ##### 获取一个上报周期内的变化率 68 | + 接口: GetMeterRateStep(name string) float64 69 | + 例子: 70 | 71 | ```go 72 | // pv发生次数的时间平均,单位CPS。计时范围为,本接口两次调用的时间差,即一个上报周期。 73 | pvRateStep := GetMeterRateStep("pageView") 74 | ``` 75 | 76 | ##### 获取累计的平均值 77 | + 接口: GetMeterRateMean(name string) float64 78 | + 例子: 79 | 80 | ```go 81 | // pv发生次数的时间平均,单位CPS。计时范围为,goperfcounter完成初始,至当前时刻。 82 | pvRateMean := GetMeterRateMean("pageView") 83 | ``` 84 | 85 | ##### 获取1min的滑动平均 86 | + 接口: GetMeterRate1(name string) float64 87 | + 例子: 88 | 89 | ```go 90 | // pv发生次数的1min滑动平均值,单位CPS 91 | pvRate1Min := GetMeterRate1("pageView") 92 | ``` 93 | 94 | ##### 获取5min的滑动平均 95 | + 接口: GetMeterRate5(name string) float64 96 | + 例子: 97 | 98 | ```go 99 | // pv发生次数的5min滑动平均值,单位CPS 100 | pvRate5Min := GetMeterRate5("pageView") 101 | ``` 102 | 103 | ##### 获取15min的滑动平均 104 | + 接口: GetMeterRate15(name string) float64 105 | + 例子: 106 | 107 | ```go 108 | // pv发生次数的15min滑动平均值,单位CPS 109 | pvRate15Min := GetMeterRate15("pageView") 110 | ``` 111 | 112 | Histogram 113 | ---- 114 | 115 | A histogram measures the [statistical distribution](http://www.johndcook.com/standard_deviation.html) of values in a stream of data. In addition to minimum, maximum, mean, etc., it also measures median, 75th, 90th, 95th, 98th, and 99th percentiles 116 | 117 | ##### 设置 118 | + 接口: SetHistogramCount(name string, count int64) 119 | + Alias: Histogram(name string, count int64) 120 | + 参数: count - 该记录当前采样点的取值 121 | + 例子: 122 | 123 | ```go 124 | // 设置当前同时处理请求的并发度 125 | SetHistogramCount("processNum", int64(325)) 126 | ``` 127 | 128 | ##### 获取最大值 129 | + 接口: GetHistogramMax(name string) int64 130 | + 例子: 131 | 132 | ```go 133 | max := GetHistogramMax("processNum") 134 | ``` 135 | 136 | ##### 获取最小值 137 | + 接口: GetHistogramMin(name string) int64 138 | + 例子: 139 | 140 | ```go 141 | min := GetHistogramMin("processNum") 142 | ``` 143 | 144 | ##### 获取平均值 145 | + 接口: GetHistogramMean(name string) float64 146 | + 例子: 147 | 148 | ```go 149 | mean := GetHistogramMean("processNum") 150 | ``` 151 | 152 | ##### 获取75thPecentile 153 | + 接口: GetHistogram75th(name string) float64 154 | + 例子: 155 | 156 | ```go 157 | // 获取所有采样数据中,处于75%的并发度 158 | pNum75th := GetHistogram75th("processNum") 159 | ``` 160 | 161 | ##### 获取95thPecentile 162 | + 接口: GetHistogram95th(name string) float64 163 | + 例子: 164 | 165 | ```go 166 | // 获取所有采样数据中,处于95%的并发度 167 | pNum95th := GetHistogram95th("processNum") 168 | ``` 169 | 170 | ##### 获取99thPecentile 171 | + 接口: GetHistogram99th(name string) float64 172 | + 例子: 173 | 174 | ```go 175 | // 获取所有采样数据中,处于99%的并发度 176 | pNum99th := GetHistogram99th("processNum") 177 | ``` 178 | 179 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | package goperfcounter 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | _ "net/http/pprof" 9 | "strings" 10 | ) 11 | 12 | func startHttp(addr string, debug bool) { 13 | configCommonRoutes() 14 | configProcRoutes() 15 | if len(addr) >= 9 { //x.x.x.x:x 16 | s := &http.Server{ 17 | Addr: addr, 18 | MaxHeaderBytes: 1 << 30, 19 | } 20 | go func() { 21 | if debug { 22 | log.Println("[perfcounter] http server start, listening on", addr) 23 | } 24 | s.ListenAndServe() 25 | if debug { 26 | log.Println("[perfcounter] http server stop,", addr) 27 | } 28 | }() 29 | } 30 | } 31 | 32 | // routers 33 | func configProcRoutes() { 34 | http.HandleFunc("/pfc/proc/metrics/json", func(w http.ResponseWriter, r *http.Request) { 35 | if !isLocalReq(r.RemoteAddr) { 36 | RenderJson(w, "no privilege") 37 | return 38 | } 39 | RenderJson(w, rawMetrics()) 40 | }) 41 | http.HandleFunc("/pfc/proc/metrics/falcon", func(w http.ResponseWriter, r *http.Request) { 42 | if !isLocalReq(r.RemoteAddr) { 43 | RenderJson(w, "no privilege") 44 | return 45 | } 46 | RenderJson(w, falconMetrics()) 47 | }) 48 | // url=/pfc/proc/metric/{json,falcon} 49 | http.HandleFunc("/pfc/proc/metrics/", func(w http.ResponseWriter, r *http.Request) { 50 | if !isLocalReq(r.RemoteAddr) { 51 | RenderJson(w, "no privilege") 52 | return 53 | } 54 | urlParam := r.URL.Path[len("/pfc/proc/metrics/"):] 55 | args := strings.Split(urlParam, "/") 56 | argsLen := len(args) 57 | if argsLen != 2 { 58 | RenderJson(w, "") 59 | return 60 | } 61 | 62 | types := []string{} 63 | typeslice := strings.Split(args[0], ",") 64 | for _, t := range typeslice { 65 | nt := strings.TrimSpace(t) 66 | if nt != "" { 67 | types = append(types, nt) 68 | } 69 | } 70 | 71 | if args[1] == "json" { 72 | RenderJson(w, rawMetric(types)) 73 | return 74 | } 75 | if args[1] == "falcon" { 76 | RenderJson(w, falconMetric(types)) 77 | return 78 | } 79 | }) 80 | 81 | http.HandleFunc("/pfc/proc/metrics/size", func(w http.ResponseWriter, r *http.Request) { 82 | if !isLocalReq(r.RemoteAddr) { 83 | RenderJson(w, "no privilege") 84 | return 85 | } 86 | RenderJson(w, rawSizes()) 87 | }) 88 | 89 | } 90 | 91 | func configCommonRoutes() { 92 | http.HandleFunc("/pfc/health", func(w http.ResponseWriter, r *http.Request) { 93 | if !isLocalReq(r.RemoteAddr) { 94 | RenderJson(w, "no privilege") 95 | return 96 | } 97 | w.Write([]byte("ok")) 98 | }) 99 | 100 | http.HandleFunc("/pfc/version", func(w http.ResponseWriter, r *http.Request) { 101 | if !isLocalReq(r.RemoteAddr) { 102 | RenderJson(w, "no privilege") 103 | return 104 | } 105 | w.Write([]byte(fmt.Sprintf("%s\n", VERSION))) 106 | }) 107 | 108 | http.HandleFunc("/pfc/config", func(w http.ResponseWriter, r *http.Request) { 109 | if !isLocalReq(r.RemoteAddr) { 110 | RenderJson(w, "no privilege") 111 | return 112 | } 113 | RenderJson(w, config()) 114 | }) 115 | 116 | http.HandleFunc("/pfc/config/reload", func(w http.ResponseWriter, r *http.Request) { 117 | if !isLocalReq(r.RemoteAddr) { 118 | RenderJson(w, "no privilege") 119 | return 120 | } 121 | loadConfig() 122 | RenderJson(w, "ok") 123 | }) 124 | } 125 | 126 | func isLocalReq(raddr string) bool { 127 | if strings.HasPrefix(raddr, "127.0.0.1") { 128 | return true 129 | } 130 | return false 131 | } 132 | 133 | // render 134 | func RenderJson(w http.ResponseWriter, data interface{}) { 135 | renderJson(w, Dto{Msg: "success", Data: data}) 136 | } 137 | 138 | func RenderString(w http.ResponseWriter, msg string) { 139 | renderJson(w, map[string]string{"msg": msg}) 140 | } 141 | 142 | func renderJson(w http.ResponseWriter, v interface{}) { 143 | bs, err := json.Marshal(v) 144 | if err != nil { 145 | http.Error(w, err.Error(), http.StatusInternalServerError) 146 | return 147 | } 148 | w.Header().Set("Content-Type", "application/json; charset=UTF-8") 149 | w.Write(bs) 150 | } 151 | 152 | // common http return 153 | type Dto struct { 154 | Msg string `json:"msg"` 155 | Data interface{} `json:"data"` 156 | } 157 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GoPerfcounter 2 | ========== 3 | 4 | goperfcounter用于golang应用的业务监控。goperfcounter需要和开源监控系统[Open-Falcon](http://book.open-falcon.com/zh/index.html)一起使用。 5 | 6 | 概述 7 | ----- 8 | 使用goperfcounter进行golang应用的监控,大体如下: 9 | 10 | 1. 用户在其golang应用代码中,调用goperfcounter提供的统计函数;统计函数被调用时,perfcounter会生成统计记录、并保存在内存中 11 | 2. goperfcounter会自动的、定期的将这些统计记录push给Open-Falcon的收集器([agent](https://github.com/open-falcon/agent)或[transfer](https://github.com/open-falcon/transfer)) 12 | 3. 用户在Open-Falcon中,查看统计数据的绘图曲线、设置实时报警 13 | 14 | 另外,goperfcounter提供了golang应用的基础监控,包括runtime指标、debug指标等。默认情况下,基础监控是关闭的,用户可以通过[配置文件](#配置)来开启此功能。 15 | 16 | 安装 17 | ----- 18 | 19 | 在golang项目中使用goperfcounter时,需要进行安装,操作如下 20 | 21 | ```bash 22 | go get github.com/niean/goperfcounter 23 | 24 | ``` 25 | 26 | 使用 27 | ----- 28 | 29 | 用户需要引入goperfcounter包,需要在代码片段中调用goperfcounter的[API](#API)。比如,用户想要统计函数的出错次数,可以调用`Meter`方法。 30 | 31 | ```go 32 | package xxx 33 | 34 | import ( 35 | pfc "github.com/niean/goperfcounter" 36 | ) 37 | 38 | func foo() { 39 | if err := bar(); err != nil { 40 | pfc.Meter("bar.called.error", int64(1)) 41 | } 42 | } 43 | 44 | func bar() error { 45 | // do sth ... 46 | return nil 47 | } 48 | 49 | ``` 50 | 51 | 这个调用主要会产生2个Open-Falcon统计指标,如下。其中,`timestamp `和`value`是监控数据的取值;`endpoint`默认为服务器`Hostname()`,可以通过配置文件设置;`step`默认为60s,可以通过配置文件设置;`tags`中包含一个`name=bar.called.error`的标签(`bar.called.error`为用户自定义的统计器名称),其他`tags`标签可以通过配置文件设置;`counterType `和`metric`由goperfcounter决定。 52 | 53 | ```python 54 | { 55 | "counterType": "GAUGE", 56 | "endpoint": "git", 57 | "metric": "rate", 58 | "step": 20, 59 | "tags": "module=perfcounter,name=bar.called.error", 60 | "timestamp": 1451397266, 61 | "value": 13.14 62 | }, 63 | { 64 | "counterType": "GAUGE", 65 | "endpoint": "git", 66 | "metric": "sum", 67 | "step": 20, 68 | "tags": "module=perfcounter,name=bar.called.error", 69 | "timestamp": 1451397266, 70 | "value": 1023 71 | } 72 | 73 | ``` 74 | 75 | 76 | 配置 77 | ---- 78 | 默认情况下,goperfcounter不需要进行配置。如果用户需要定制goperfcounter的行为,可以通过配置文件来进行。配置文件需要满足以下的条件: 79 | 80 | + 配置文件必须和golang二进制文件应用文件,在同一目录 81 | + 配置文件命名,必须为```perfcounter.json``` 82 | 83 | 配置文件的内容,如下 84 | 85 | ```go 86 | { 87 | "debug": false, // 是否开启调制,默认为false 88 | "hostname": "", // 机器名(也即endpoint名称),默认为本机名称 89 | "tags": "", // tags标签,默认为空。一个tag形如"key=val",多个tag用逗号分隔;name为保留字段,因此不允许设置形如"name=xxx"的tag。eg. "cop=xiaomi,module=perfcounter" 90 | "step": 60, // 上报周期,单位s,默认为60s 91 | "bases":[], // gvm基础信息采集,可选值为"debug"、"runtime",默认不采集 92 | "push": { // push数据到Open-Falcon 93 | "enabled":true, // 是否开启自动push,默认开启 94 | "api": "" // Open-Falcon接收器地址,默认为本地agent,即"http:// 127.0.0.1:1988/v1/push" 95 | }, 96 | "http": { // http服务,为了安全考虑,当前只允许本地访问 97 | "enabled": false, // 是否开启http服务,默认不开启 98 | "listen": "" // http服务监听地址,默认为空。eg. "0.0.0.0:2015"表示在2015端口开启http监听 99 | } 100 | } 101 | 102 | ``` 103 | 104 | 105 | 106 | API 107 | ---- 108 | 109 | 几个常用接口,如下。 110 | 111 | |接口名称|例子|使用场景| 112 | |:----|:----|:---| 113 | |Meter|`// 统计页面访问次数,每来一次请求,pv加1`
`Meter("pageView", int64(1)) `|Meter用于累加计数。输出累加求和、变化率| 114 | |Gauge|`// 统计队列长度`
`Gauge("queueSize", int64(len(myQueueList))) `
`GaugeFloat64("queueSize", float64(len(myQueueList)))`|Gauge用于记录瞬时值。支持int64、float64类型| 115 | |Histogram|`// 统计线程并发度`
`Histogram("processNum", int64(326)) `| Histogram用于计算统计分布。输出最大值、最小值、平均值、75th、95th、99th等| 116 | 117 | 更详细的API介绍,请移步到[这里](https://github.com/niean/goperfcounter/blob/master/doc/API.md)。 118 | 119 | 120 | 121 | 数据上报 122 | ---- 123 | 124 | goperfcounter会将各种统计器的统计结果,定时发送到Open-Falcon。每种统计器,会被转换成不同的Open-Falcon指标项,转换关系如下。每条数据,至少包含一个```name=XXX```的tag,```XXX```是用户定义的统计器名称。 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 |
统计器类型输出指标的名称输出指标的含义
Gaugevalue最后一次的记录值(float64)
Metersum事件发生的总次数(即所有计数的累加和)
rate一个Open-Falcon上报周期(默认60s)内,事件发生的频率,单位CPS
Histogrammax采样数据的最大值
min采样数据的最小值
mean采样数据的平均值
75th所有采样数据中,处于75%处的数值
95th所有采样数据中,处于95%处的数值
99th所有采样数据中,处于99%处的数值
172 | 173 | 174 | Bench 175 | ---- 176 | 177 | 请移步到[这里](https://github.com/niean/goperfcounter/blob/master/doc/BENCH.md) 178 | 179 | 180 | TODO 181 | ---- 182 | 183 | + 支持本地缓存统计数据及UI展示 184 | -------------------------------------------------------------------------------- /falcon.go: -------------------------------------------------------------------------------- 1 | package goperfcounter 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/niean/go-metrics-lite" 10 | bhttp "github.com/niean/gotools/http/httpclient/beego" 11 | ) 12 | 13 | const ( 14 | GAUGE = "GAUGE" 15 | ) 16 | 17 | func pushToFalcon() { 18 | cfg := config() 19 | step := cfg.Step 20 | api := cfg.Push.Api 21 | debug := cfg.Debug 22 | 23 | // align push start ts 24 | alignPushStartTs(step) 25 | 26 | for _ = range time.Tick(time.Duration(step) * time.Second) { 27 | selfMeter("pfc.push.cnt", 1) // statistics 28 | 29 | fms := falconMetrics() 30 | start := time.Now() 31 | err := push(fms, api, debug) 32 | selfGauge("pfc.push.ms", int64(time.Since(start)/time.Millisecond)) // statistics 33 | 34 | if err != nil { 35 | if debug { 36 | log.Printf("[perfcounter] send to %s error: %v", api, err) 37 | } 38 | selfGauge("pfc.push.size", int64(0)) // statistics 39 | } else { 40 | selfGauge("pfc.push.size", int64(len(fms))) // statistics 41 | } 42 | } 43 | } 44 | 45 | func falconMetric(types []string) []*MetricValue { 46 | fd := []*MetricValue{} 47 | for _, ty := range types { 48 | if r, ok := values[ty]; ok && r != nil { 49 | data := _falconMetric(r) 50 | fd = append(fd, data...) 51 | } 52 | } 53 | return fd 54 | } 55 | 56 | func falconMetrics() []*MetricValue { 57 | data := make([]*MetricValue, 0) 58 | for _, r := range values { 59 | nd := _falconMetric(r) 60 | data = append(data, nd...) 61 | } 62 | return data 63 | } 64 | 65 | // internal 66 | func _falconMetric(r metrics.Registry) []*MetricValue { 67 | cfg := config() 68 | endpoint := cfg.Hostname 69 | step := cfg.Step 70 | tags := cfg.Tags 71 | ts := time.Now().Unix() 72 | 73 | data := make([]*MetricValue, 0) 74 | r.Each(func(name string, i interface{}) { 75 | switch metric := i.(type) { 76 | case metrics.Gauge: 77 | m := gaugeMetricValue(metric, name, endpoint, tags, step, ts) 78 | data = append(data, m...) 79 | case metrics.GaugeFloat64: 80 | m := gaugeFloat64MetricValue(metric, name, endpoint, tags, step, ts) 81 | data = append(data, m...) 82 | case metrics.Counter: 83 | m := counterMetricValue(metric, name, endpoint, tags, step, ts) 84 | data = append(data, m...) 85 | case metrics.Meter: 86 | m := metric.Snapshot() 87 | ms := meterMetricValue(m, name, endpoint, tags, step, ts) 88 | data = append(data, ms...) 89 | case metrics.Histogram: 90 | h := metric.Snapshot() 91 | ms := histogramMetricValue(h, name, endpoint, tags, step, ts) 92 | data = append(data, ms...) 93 | } 94 | }) 95 | 96 | return data 97 | } 98 | 99 | func gaugeMetricValue(metric metrics.Gauge, metricName, endpoint, oldtags string, step, ts int64) []*MetricValue { 100 | tags := getTags(metricName, oldtags) 101 | c := newMetricValue(endpoint, "value", metric.Value(), step, GAUGE, tags, ts) 102 | return []*MetricValue{c} 103 | } 104 | 105 | func gaugeFloat64MetricValue(metric metrics.GaugeFloat64, metricName, endpoint, oldtags string, step, ts int64) []*MetricValue { 106 | tags := getTags(metricName, oldtags) 107 | c := newMetricValue(endpoint, "value", metric.Value(), step, GAUGE, tags, ts) 108 | return []*MetricValue{c} 109 | } 110 | 111 | func counterMetricValue(metric metrics.Counter, metricName, endpoint, oldtags string, step, ts int64) []*MetricValue { 112 | tags := getTags(metricName, oldtags) 113 | c1 := newMetricValue(endpoint, "count", metric.Count(), step, GAUGE, tags, ts) 114 | return []*MetricValue{c1} 115 | } 116 | 117 | func meterMetricValue(metric metrics.Meter, metricName, endpoint, oldtags string, step, ts int64) []*MetricValue { 118 | data := make([]*MetricValue, 0) 119 | tags := getTags(metricName, oldtags) 120 | 121 | c1 := newMetricValue(endpoint, "rate", metric.RateStep(), step, GAUGE, tags, ts) 122 | c2 := newMetricValue(endpoint, "sum", metric.Count(), step, GAUGE, tags, ts) 123 | data = append(data, c1, c2) 124 | 125 | return data 126 | } 127 | 128 | func histogramMetricValue(metric metrics.Histogram, metricName, endpoint, oldtags string, step, ts int64) []*MetricValue { 129 | data := make([]*MetricValue, 0) 130 | tags := getTags(metricName, oldtags) 131 | 132 | values := make(map[string]interface{}) 133 | ps := metric.Percentiles([]float64{0.75, 0.95, 0.99}) 134 | values["min"] = metric.Min() 135 | values["max"] = metric.Max() 136 | values["mean"] = metric.Mean() 137 | values["75th"] = ps[0] 138 | values["95th"] = ps[1] 139 | values["99th"] = ps[2] 140 | for key, val := range values { 141 | c := newMetricValue(endpoint, key, val, step, GAUGE, tags, ts) 142 | data = append(data, c) 143 | } 144 | 145 | return data 146 | } 147 | 148 | func newMetricValue(endpoint, metric string, value interface{}, step int64, t, tags string, ts int64) *MetricValue { 149 | return &MetricValue{ 150 | Endpoint: endpoint, 151 | Metric: metric, 152 | Value: value, 153 | Step: step, 154 | Type: t, 155 | Tags: tags, 156 | Timestamp: ts, 157 | } 158 | } 159 | 160 | func getTags(name string, tags string) string { 161 | if tags == "" { 162 | return fmt.Sprintf("name=%s", name) 163 | } 164 | return fmt.Sprintf("%s,name=%s", tags, name) 165 | } 166 | 167 | // 168 | func push(data []*MetricValue, url string, debug bool) error { 169 | dlen := len(data) 170 | pkg := 200 //send pkg items once 171 | sent := 0 172 | for { 173 | if sent >= dlen { 174 | break 175 | } 176 | 177 | end := sent + pkg 178 | if end > dlen { 179 | end = dlen 180 | } 181 | 182 | pkgData := data[sent:end] 183 | jr, err := json.Marshal(pkgData) 184 | if err != nil { 185 | return err 186 | } 187 | 188 | response, err := bhttp.Post(url).Body(jr).String() 189 | if err != nil { 190 | return err 191 | } 192 | sent = end 193 | 194 | if debug { 195 | log.Printf("[perfcounter] push result: %v, data: %v\n", response, pkgData) 196 | } 197 | } 198 | return nil 199 | } 200 | 201 | // 202 | func alignPushStartTs(stepSec int64) { 203 | nw := time.Duration(time.Now().UnixNano()) 204 | step := time.Duration(stepSec) * time.Second 205 | sleepNano := step - nw%step 206 | if sleepNano > 0 { 207 | time.Sleep(sleepNano) 208 | } 209 | } 210 | 211 | // 212 | type MetricValue struct { 213 | Endpoint string `json:"endpoint"` 214 | Metric string `json:"metric"` 215 | Value interface{} `json:"value"` 216 | Step int64 `json:"step"` 217 | Type string `json:"counterType"` 218 | Tags string `json:"tags"` 219 | Timestamp int64 `json:"timestamp"` 220 | } 221 | 222 | func (this *MetricValue) String() string { 223 | return fmt.Sprintf( 224 | "", 225 | this.Endpoint, 226 | this.Metric, 227 | this.Tags, 228 | this.Type, 229 | this.Step, 230 | this.Timestamp, 231 | this.Value, 232 | ) 233 | } 234 | -------------------------------------------------------------------------------- /perfcounter.go: -------------------------------------------------------------------------------- 1 | package goperfcounter 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/niean/go-metrics-lite" 7 | ) 8 | 9 | func init() { 10 | // init cfg 11 | err := loadConfig() 12 | if err != nil { 13 | setDefaultConfig() 14 | } 15 | cfg := config() 16 | 17 | // init http 18 | if cfg.Http.Enabled { 19 | go startHttp(cfg.Http.Listen, cfg.Debug) 20 | } 21 | 22 | // base collector cron 23 | if len(cfg.Bases) > 0 { 24 | go collectBase(cfg.Bases) 25 | } 26 | 27 | // push cron 28 | if cfg.Push.Enabled { 29 | go pushToFalcon() 30 | } 31 | } 32 | 33 | // gauge 34 | func Gauge(name string, value int64) { 35 | SetGaugeValue(name, float64(value)) 36 | } 37 | func GaugeFloat64(name string, value float64) { 38 | SetGaugeValue(name, value) 39 | } 40 | 41 | func SetGaugeValue(name string, value float64) { 42 | rr := gpGaugeFloat64.Get(name) 43 | if rr != nil { 44 | if r, ok := rr.(metrics.GaugeFloat64); ok { 45 | r.Update(value) 46 | } 47 | return 48 | } 49 | 50 | r := metrics.NewGaugeFloat64() 51 | r.Update(value) 52 | if err := gpGaugeFloat64.Register(name, r); isDuplicateMetricError(err) { 53 | r := gpGaugeFloat64.Get(name).(metrics.GaugeFloat64) 54 | r.Update(value) 55 | } 56 | } 57 | 58 | func GetGaugeValue(name string) float64 { 59 | rr := gpGaugeFloat64.Get(name) 60 | if rr != nil { 61 | if r, ok := rr.(metrics.GaugeFloat64); ok { 62 | return r.Value() 63 | } 64 | } 65 | return 0.0 66 | } 67 | 68 | // meter 69 | func Meter(name string, count int64) { 70 | SetMeterCount(name, count) 71 | } 72 | 73 | func SetMeterCount(name string, count int64) { 74 | rr := gpMeter.Get(name) 75 | if rr != nil { 76 | if r, ok := rr.(metrics.Meter); ok { 77 | r.Mark(count) 78 | } 79 | return 80 | } 81 | 82 | r := metrics.NewMeter() 83 | r.Mark(count) 84 | if err := gpMeter.Register(name, r); isDuplicateMetricError(err) { 85 | r := gpMeter.Get(name).(metrics.Meter) 86 | r.Mark(count) 87 | } 88 | } 89 | 90 | func GetMeterCount(name string) int64 { 91 | rr := gpMeter.Get(name) 92 | if rr != nil { 93 | if r, ok := rr.(metrics.Meter); ok { 94 | return r.Count() 95 | } 96 | } 97 | return 0 98 | } 99 | 100 | func GetMeterRateStep(name string) float64 { 101 | rr := gpMeter.Get(name) 102 | if rr != nil { 103 | if r, ok := rr.(metrics.Meter); ok { 104 | return r.RateStep() 105 | } 106 | } 107 | return 0.0 108 | } 109 | 110 | func GetMeterRateMean(name string) float64 { 111 | rr := gpMeter.Get(name) 112 | if rr != nil { 113 | if r, ok := rr.(metrics.Meter); ok { 114 | return r.RateMean() 115 | } 116 | } 117 | return 0.0 118 | } 119 | 120 | func GetMeterRate1(name string) float64 { 121 | rr := gpMeter.Get(name) 122 | if rr != nil { 123 | if r, ok := rr.(metrics.Meter); ok { 124 | return r.Rate1() 125 | } 126 | } 127 | return 0.0 128 | } 129 | 130 | func GetMeterRate5(name string) float64 { 131 | rr := gpMeter.Get(name) 132 | if rr != nil { 133 | if r, ok := rr.(metrics.Meter); ok { 134 | return r.Rate5() 135 | } 136 | } 137 | return 0.0 138 | } 139 | 140 | func GetMeterRate15(name string) float64 { 141 | rr := gpMeter.Get(name) 142 | if rr != nil { 143 | if r, ok := rr.(metrics.Meter); ok { 144 | return r.Rate15() 145 | } 146 | } 147 | return 0.0 148 | } 149 | 150 | // histogram 151 | func Histogram(name string, count int64) { 152 | SetHistogramCount(name, count) 153 | } 154 | 155 | func SetHistogramCount(name string, count int64) { 156 | rr := gpHistogram.Get(name) 157 | if rr != nil { 158 | if r, ok := rr.(metrics.Histogram); ok { 159 | r.Update(count) 160 | } 161 | return 162 | } 163 | 164 | s := metrics.NewExpDecaySample(1028, 0.015) 165 | r := metrics.NewHistogram(s) 166 | r.Update(count) 167 | if err := gpHistogram.Register(name, r); isDuplicateMetricError(err) { 168 | r := gpHistogram.Get(name).(metrics.Histogram) 169 | r.Update(count) 170 | } 171 | } 172 | 173 | func GetHistogramCount(name string) int64 { 174 | rr := gpHistogram.Get(name) 175 | if rr != nil { 176 | if r, ok := rr.(metrics.Histogram); ok { 177 | return r.Count() 178 | } 179 | } 180 | return 0 181 | } 182 | func GetHistogramMax(name string) int64 { 183 | rr := gpHistogram.Get(name) 184 | if rr != nil { 185 | if r, ok := rr.(metrics.Histogram); ok { 186 | return r.Max() 187 | } 188 | } 189 | return 0 190 | } 191 | func GetHistogramMin(name string) int64 { 192 | rr := gpHistogram.Get(name) 193 | if rr != nil { 194 | if r, ok := rr.(metrics.Histogram); ok { 195 | return r.Min() 196 | } 197 | } 198 | return 0 199 | } 200 | func GetHistogramSum(name string) int64 { 201 | rr := gpHistogram.Get(name) 202 | if rr != nil { 203 | if r, ok := rr.(metrics.Histogram); ok { 204 | return r.Sum() 205 | } 206 | } 207 | return 0 208 | } 209 | func GetHistogramMean(name string) float64 { 210 | rr := gpHistogram.Get(name) 211 | if rr != nil { 212 | if r, ok := rr.(metrics.Histogram); ok { 213 | return r.Mean() 214 | } 215 | } 216 | return 0.0 217 | } 218 | func GetHistogramStdDev(name string) float64 { 219 | rr := gpHistogram.Get(name) 220 | if rr != nil { 221 | if r, ok := rr.(metrics.Histogram); ok { 222 | return r.StdDev() 223 | } 224 | } 225 | return 0.0 226 | } 227 | func GetHistogram50th(name string) float64 { 228 | rr := gpHistogram.Get(name) 229 | if rr != nil { 230 | if r, ok := rr.(metrics.Histogram); ok { 231 | return r.Percentile(0.5) 232 | } 233 | } 234 | return 0.0 235 | } 236 | func GetHistogram75th(name string) float64 { 237 | rr := gpHistogram.Get(name) 238 | if rr != nil { 239 | if r, ok := rr.(metrics.Histogram); ok { 240 | return r.Percentile(0.75) 241 | } 242 | } 243 | return 0.0 244 | } 245 | func GetHistogram95th(name string) float64 { 246 | rr := gpHistogram.Get(name) 247 | if rr != nil { 248 | if r, ok := rr.(metrics.Histogram); ok { 249 | return r.Percentile(0.95) 250 | } 251 | } 252 | return 0.0 253 | } 254 | func GetHistogram99th(name string) float64 { 255 | rr := gpHistogram.Get(name) 256 | if rr != nil { 257 | if r, ok := rr.(metrics.Histogram); ok { 258 | return r.Percentile(0.99) 259 | } 260 | } 261 | return 0.0 262 | } 263 | func GetHistogram999th(name string) float64 { 264 | rr := gpHistogram.Get(name) 265 | if rr != nil { 266 | if r, ok := rr.(metrics.Histogram); ok { 267 | return r.Percentile(0.999) 268 | } 269 | } 270 | return 0.0 271 | } 272 | 273 | // senior 274 | func Counter(name string, count int64) { 275 | SetCounterCount(name, count) 276 | } 277 | func SetCounterCount(name string, count int64) { 278 | rr := gpCounter.Get(name) 279 | if rr != nil { 280 | if r, ok := rr.(metrics.Counter); ok { 281 | r.Inc(count) 282 | } 283 | return 284 | } 285 | 286 | r := metrics.NewCounter() 287 | r.Inc(count) 288 | if err := gpCounter.Register(name, r); isDuplicateMetricError(err) { 289 | r := gpCounter.Get(name).(metrics.Counter) 290 | r.Inc(count) 291 | } 292 | } 293 | 294 | func GetCounterCount(name string) int64 { 295 | rr := gpCounter.Get(name) 296 | if rr != nil { 297 | if r, ok := rr.(metrics.Counter); ok { 298 | return r.Count() 299 | } 300 | } 301 | return 0 302 | } 303 | 304 | // self 305 | func selfGauge(name string, value int64) { 306 | rr := gpSelf.Get(name) 307 | if rr != nil { 308 | if r, ok := rr.(metrics.Gauge); ok { 309 | r.Update(value) 310 | } 311 | return 312 | } 313 | 314 | r := metrics.NewGauge() 315 | r.Update(value) 316 | if err := gpSelf.Register(name, r); isDuplicateMetricError(err) { 317 | r := gpSelf.Get(name).(metrics.Gauge) 318 | r.Update(value) 319 | } 320 | } 321 | 322 | func selfMeter(name string, value int64) { 323 | rr := gpSelf.Get(name) 324 | if rr != nil { 325 | if r, ok := rr.(metrics.Meter); ok { 326 | r.Mark(value) 327 | } 328 | return 329 | } 330 | 331 | r := metrics.NewMeter() 332 | r.Mark(value) 333 | if err := gpSelf.Register(name, r); isDuplicateMetricError(err) { 334 | r := gpSelf.Get(name).(metrics.Meter) 335 | r.Mark(value) 336 | } 337 | } 338 | 339 | // internal 340 | func isDuplicateMetricError(err error) bool { 341 | if err == nil { 342 | return false 343 | } 344 | return strings.Index(err.Error(), "duplicate metric:") == 0 345 | } 346 | -------------------------------------------------------------------------------- /base/runtime.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "runtime" 5 | "time" 6 | 7 | "github.com/niean/go-metrics-lite" 8 | ) 9 | 10 | var ( 11 | memStats runtime.MemStats 12 | runtimeMetrics struct { 13 | MemStats struct { 14 | Alloc metrics.Gauge 15 | BuckHashSys metrics.Gauge 16 | DebugGC metrics.Gauge 17 | EnableGC metrics.Gauge 18 | Frees metrics.Gauge 19 | HeapAlloc metrics.Gauge 20 | HeapIdle metrics.Gauge 21 | HeapInuse metrics.Gauge 22 | HeapObjects metrics.Gauge 23 | HeapReleased metrics.Gauge 24 | HeapSys metrics.Gauge 25 | LastGC metrics.Gauge 26 | Lookups metrics.Gauge 27 | Mallocs metrics.Gauge 28 | MCacheInuse metrics.Gauge 29 | MCacheSys metrics.Gauge 30 | MSpanInuse metrics.Gauge 31 | MSpanSys metrics.Gauge 32 | NextGC metrics.Gauge 33 | NumGC metrics.Gauge 34 | GCCPUFraction metrics.GaugeFloat64 35 | PauseNs metrics.Histogram 36 | PauseTotalNs metrics.Gauge 37 | StackInuse metrics.Gauge 38 | StackSys metrics.Gauge 39 | Sys metrics.Gauge 40 | TotalAlloc metrics.Gauge 41 | } 42 | NumCgoCall metrics.Gauge 43 | NumGoroutine metrics.Gauge 44 | ReadMemStats metrics.Histogram 45 | } 46 | frees uint64 47 | lookups uint64 48 | mallocs uint64 49 | numGC uint32 50 | numCgoCalls int64 51 | ) 52 | 53 | func RegisterAndCaptureRuntimeMemStats(r metrics.Registry, d time.Duration) { 54 | registerRuntimeMemStats(r) 55 | go captureRuntimeMemStats(r, d) 56 | } 57 | 58 | // Capture new values for the Go runtime statistics exported in 59 | // runtime.MemStats. This is designed to be called as a goroutine. 60 | func captureRuntimeMemStats(r metrics.Registry, d time.Duration) { 61 | for _ = range time.Tick(d) { 62 | captureRuntimeMemStatsOnce(r) 63 | } 64 | } 65 | 66 | // Capture new values for the Go runtime statistics exported in 67 | // runtime.MemStats. This is designed to be called in a background 68 | // goroutine. Giving a registry which has not been given to 69 | // RegisterRuntimeMemStats will panic. 70 | // 71 | // Be very careful with this because runtime.ReadMemStats calls the C 72 | // functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld() 73 | // and that last one does what it says on the tin. 74 | func captureRuntimeMemStatsOnce(r metrics.Registry) { 75 | t := time.Now() 76 | runtime.ReadMemStats(&memStats) // This takes 50-200us. 77 | runtimeMetrics.ReadMemStats.Update(int64(time.Since(t))) 78 | 79 | runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc)) 80 | runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys)) 81 | if memStats.DebugGC { 82 | runtimeMetrics.MemStats.DebugGC.Update(1) 83 | } else { 84 | runtimeMetrics.MemStats.DebugGC.Update(0) 85 | } 86 | if memStats.EnableGC { 87 | runtimeMetrics.MemStats.EnableGC.Update(1) 88 | } else { 89 | runtimeMetrics.MemStats.EnableGC.Update(0) 90 | } 91 | 92 | runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees)) 93 | runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc)) 94 | runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle)) 95 | runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse)) 96 | runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects)) 97 | runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased)) 98 | runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys)) 99 | runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC)) 100 | runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups)) 101 | runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs)) 102 | runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse)) 103 | runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys)) 104 | runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse)) 105 | runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys)) 106 | runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC)) 107 | runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC)) 108 | runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats)) 109 | 110 | // 111 | i := numGC % uint32(len(memStats.PauseNs)) 112 | ii := memStats.NumGC % uint32(len(memStats.PauseNs)) 113 | if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) { 114 | for i = 0; i < uint32(len(memStats.PauseNs)); i++ { 115 | runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) 116 | } 117 | } else { 118 | if i > ii { 119 | for ; i < uint32(len(memStats.PauseNs)); i++ { 120 | runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) 121 | } 122 | i = 0 123 | } 124 | for ; i < ii; i++ { 125 | runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) 126 | } 127 | } 128 | frees = memStats.Frees 129 | lookups = memStats.Lookups 130 | mallocs = memStats.Mallocs 131 | numGC = memStats.NumGC 132 | 133 | runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs)) 134 | runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse)) 135 | runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys)) 136 | runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys)) 137 | runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc)) 138 | 139 | currentNumCgoCalls := numCgoCall() 140 | runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls) 141 | numCgoCalls = currentNumCgoCalls 142 | 143 | runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine())) 144 | } 145 | 146 | // Register runtimeMetrics for the Go runtime statistics exported in runtime and 147 | // specifically runtime.MemStats. The runtimeMetrics are named by their 148 | // fully-qualified Go symbols, i.e. runtime.MemStats.Alloc. 149 | func registerRuntimeMemStats(r metrics.Registry) { 150 | runtimeMetrics.MemStats.Alloc = metrics.NewGauge() 151 | runtimeMetrics.MemStats.BuckHashSys = metrics.NewGauge() 152 | runtimeMetrics.MemStats.DebugGC = metrics.NewGauge() 153 | runtimeMetrics.MemStats.EnableGC = metrics.NewGauge() 154 | runtimeMetrics.MemStats.Frees = metrics.NewGauge() 155 | runtimeMetrics.MemStats.HeapAlloc = metrics.NewGauge() 156 | runtimeMetrics.MemStats.HeapIdle = metrics.NewGauge() 157 | runtimeMetrics.MemStats.HeapInuse = metrics.NewGauge() 158 | runtimeMetrics.MemStats.HeapObjects = metrics.NewGauge() 159 | runtimeMetrics.MemStats.HeapReleased = metrics.NewGauge() 160 | runtimeMetrics.MemStats.HeapSys = metrics.NewGauge() 161 | runtimeMetrics.MemStats.LastGC = metrics.NewGauge() 162 | runtimeMetrics.MemStats.Lookups = metrics.NewGauge() 163 | runtimeMetrics.MemStats.Mallocs = metrics.NewGauge() 164 | runtimeMetrics.MemStats.MCacheInuse = metrics.NewGauge() 165 | runtimeMetrics.MemStats.MCacheSys = metrics.NewGauge() 166 | runtimeMetrics.MemStats.MSpanInuse = metrics.NewGauge() 167 | runtimeMetrics.MemStats.MSpanSys = metrics.NewGauge() 168 | runtimeMetrics.MemStats.NextGC = metrics.NewGauge() 169 | runtimeMetrics.MemStats.NumGC = metrics.NewGauge() 170 | runtimeMetrics.MemStats.GCCPUFraction = metrics.NewGaugeFloat64() 171 | runtimeMetrics.MemStats.PauseNs = metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015)) 172 | runtimeMetrics.MemStats.PauseTotalNs = metrics.NewGauge() 173 | runtimeMetrics.MemStats.StackInuse = metrics.NewGauge() 174 | runtimeMetrics.MemStats.StackSys = metrics.NewGauge() 175 | runtimeMetrics.MemStats.Sys = metrics.NewGauge() 176 | runtimeMetrics.MemStats.TotalAlloc = metrics.NewGauge() 177 | runtimeMetrics.NumCgoCall = metrics.NewGauge() 178 | runtimeMetrics.NumGoroutine = metrics.NewGauge() 179 | runtimeMetrics.ReadMemStats = metrics.NewHistogram(metrics.NewExpDecaySample(1028, 0.015)) 180 | 181 | r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc) 182 | r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys) 183 | r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC) 184 | r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC) 185 | r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees) 186 | r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc) 187 | r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle) 188 | r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse) 189 | r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects) 190 | r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased) 191 | r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys) 192 | r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC) 193 | r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups) 194 | r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs) 195 | r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse) 196 | r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys) 197 | r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse) 198 | r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys) 199 | r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC) 200 | r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC) 201 | r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction) 202 | r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs) 203 | r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs) 204 | r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse) 205 | r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys) 206 | r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys) 207 | r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc) 208 | r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall) 209 | r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine) 210 | r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats) 211 | } 212 | --------------------------------------------------------------------------------