github.com/waldiirawan/apm-agent-go/v2@v2.2.2/builtin_metrics.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package apm // import "github.com/waldiirawan/apm-agent-go/v2" 19 20 import ( 21 "context" 22 "runtime" 23 24 sysinfo "github.com/elastic/go-sysinfo" 25 "github.com/elastic/go-sysinfo/types" 26 ) 27 28 // builtinMetricsGatherer is an MetricsGatherer which gathers builtin metrics: 29 // - goroutines 30 // - memstats (allocations, usage, GC, etc.) 31 // - system and process CPU and memory usage 32 type builtinMetricsGatherer struct { 33 tracer *Tracer 34 lastSysMetrics sysMetrics 35 } 36 37 func newBuiltinMetricsGatherer(t *Tracer) *builtinMetricsGatherer { 38 g := &builtinMetricsGatherer{tracer: t} 39 if metrics, err := gatherSysMetrics(); err == nil { 40 g.lastSysMetrics = metrics 41 } 42 return g 43 } 44 45 // GatherMetrics gathers mem metrics into m. 46 func (g *builtinMetricsGatherer) GatherMetrics(ctx context.Context, m *Metrics) error { 47 m.Add("golang.goroutines", nil, float64(runtime.NumGoroutine())) 48 g.gatherSystemMetrics(m) 49 g.gatherMemStatsMetrics(m) 50 g.tracer.breakdownMetrics.gather(m) 51 return nil 52 } 53 54 func (g *builtinMetricsGatherer) gatherSystemMetrics(m *Metrics) { 55 metrics, err := gatherSysMetrics() 56 if err != nil { 57 return 58 } 59 systemCPU, processCPU := calculateCPUUsage(metrics.cpu, g.lastSysMetrics.cpu) 60 m.Add("system.cpu.total.norm.pct", nil, systemCPU) 61 m.Add("system.process.cpu.total.norm.pct", nil, processCPU) 62 m.Add("system.memory.total", nil, float64(metrics.mem.system.Total)) 63 m.Add("system.memory.actual.free", nil, float64(metrics.mem.system.Available)) 64 m.Add("system.process.memory.size", nil, float64(metrics.mem.process.Virtual)) 65 m.Add("system.process.memory.rss.bytes", nil, float64(metrics.mem.process.Resident)) 66 g.lastSysMetrics = metrics 67 } 68 69 func (g *builtinMetricsGatherer) gatherMemStatsMetrics(m *Metrics) { 70 var mem runtime.MemStats 71 runtime.ReadMemStats(&mem) 72 73 addUint64 := func(name string, v uint64) { 74 m.Add(name, nil, float64(v)) 75 } 76 add := func(name string, v float64) { 77 m.Add(name, nil, v) 78 } 79 80 addUint64("golang.heap.allocations.mallocs", mem.Mallocs) 81 addUint64("golang.heap.allocations.frees", mem.Frees) 82 addUint64("golang.heap.allocations.objects", mem.HeapObjects) 83 addUint64("golang.heap.allocations.total", mem.TotalAlloc) 84 addUint64("golang.heap.allocations.allocated", mem.HeapAlloc) 85 addUint64("golang.heap.allocations.idle", mem.HeapIdle) 86 addUint64("golang.heap.allocations.active", mem.HeapInuse) 87 addUint64("golang.heap.system.total", mem.Sys) 88 addUint64("golang.heap.system.obtained", mem.HeapSys) 89 addUint64("golang.heap.system.stack", mem.StackSys) 90 addUint64("golang.heap.system.released", mem.HeapReleased) 91 addUint64("golang.heap.gc.next_gc_limit", mem.NextGC) 92 addUint64("golang.heap.gc.total_count", uint64(mem.NumGC)) 93 addUint64("golang.heap.gc.total_pause.ns", mem.PauseTotalNs) 94 add("golang.heap.gc.cpu_fraction", mem.GCCPUFraction) 95 } 96 97 func calculateCPUUsage(current, last cpuMetrics) (systemUsage, processUsage float64) { 98 idleDelta := current.system.Idle + current.system.IOWait - last.system.Idle - last.system.IOWait 99 systemTotalDelta := current.system.Total() - last.system.Total() 100 if systemTotalDelta <= 0 { 101 return 0, 0 102 } 103 104 idlePercent := float64(idleDelta) / float64(systemTotalDelta) 105 systemUsage = 1 - idlePercent 106 107 processTotalDelta := current.process.Total() - last.process.Total() 108 processUsage = float64(processTotalDelta) / float64(systemTotalDelta) 109 110 return systemUsage, processUsage 111 } 112 113 type sysMetrics struct { 114 cpu cpuMetrics 115 mem memoryMetrics 116 } 117 118 type cpuMetrics struct { 119 process types.CPUTimes 120 system types.CPUTimes 121 } 122 123 type memoryMetrics struct { 124 process types.MemoryInfo 125 system *types.HostMemoryInfo 126 } 127 128 func gatherSysMetrics() (sysMetrics, error) { 129 proc, err := sysinfo.Self() 130 if err != nil { 131 return sysMetrics{}, err 132 } 133 host, err := sysinfo.Host() 134 if err != nil { 135 return sysMetrics{}, err 136 } 137 hostTimes, err := host.CPUTime() 138 if err != nil { 139 return sysMetrics{}, err 140 } 141 hostMemory, err := host.Memory() 142 if err != nil { 143 return sysMetrics{}, err 144 } 145 procTimes, err := proc.CPUTime() 146 if err != nil { 147 return sysMetrics{}, err 148 } 149 procMemory, err := proc.Memory() 150 if err != nil { 151 return sysMetrics{}, err 152 } 153 154 return sysMetrics{ 155 cpu: cpuMetrics{ 156 system: hostTimes, 157 process: procTimes, 158 }, 159 mem: memoryMetrics{ 160 system: hostMemory, 161 process: procMemory, 162 }, 163 }, nil 164 }