distribution/vendor/github.com/yvasiyarov/gorelic/runtime_metrics.go

197 lines
4.8 KiB
Go
Raw Normal View History

package gorelic
import (
"fmt"
"github.com/yvasiyarov/newrelic_platform_go"
"io/ioutil"
"os"
"runtime"
"strconv"
"strings"
"time"
)
const linuxSystemQueryInterval = 60
// Number of goroutines metrica
type noGoroutinesMetrica struct{}
func (metrica *noGoroutinesMetrica) GetName() string {
return "Runtime/General/NOGoroutines"
}
func (metrica *noGoroutinesMetrica) GetUnits() string {
return "goroutines"
}
func (metrica *noGoroutinesMetrica) GetValue() (float64, error) {
return float64(runtime.NumGoroutine()), nil
}
// Number of CGO calls metrica
type noCgoCallsMetrica struct {
lastValue int64
}
func (metrica *noCgoCallsMetrica) GetName() string {
return "Runtime/General/NOCgoCalls"
}
func (metrica *noCgoCallsMetrica) GetUnits() string {
return "calls"
}
func (metrica *noCgoCallsMetrica) GetValue() (float64, error) {
currentValue := runtime.NumCgoCall()
value := float64(currentValue - metrica.lastValue)
metrica.lastValue = currentValue
return value, nil
}
//OS specific metrics data source interface
type iSystemMetricaDataSource interface {
GetValue(key string) (float64, error)
}
// iSystemMetricaDataSource fabrica
func newSystemMetricaDataSource() iSystemMetricaDataSource {
var ds iSystemMetricaDataSource
switch runtime.GOOS {
default:
ds = &systemMetricaDataSource{}
case "linux":
ds = &linuxSystemMetricaDataSource{
systemData: make(map[string]string),
}
}
return ds
}
//Default implementation of iSystemMetricaDataSource. Just return an error
type systemMetricaDataSource struct{}
func (ds *systemMetricaDataSource) GetValue(key string) (float64, error) {
return 0, fmt.Errorf("this metrica was not implemented yet for %s", runtime.GOOS)
}
// Linux OS implementation of ISystemMetricaDataSource
type linuxSystemMetricaDataSource struct {
lastUpdate time.Time
systemData map[string]string
}
func (ds *linuxSystemMetricaDataSource) GetValue(key string) (float64, error) {
if err := ds.checkAndUpdateData(); err != nil {
return 0, err
} else if val, ok := ds.systemData[key]; !ok {
return 0, fmt.Errorf("system data with key %s was not found", key)
} else if key == "VmSize" || key == "VmPeak" || key == "VmHWM" || key == "VmRSS" {
valueParts := strings.Split(val, " ")
if len(valueParts) != 2 {
return 0, fmt.Errorf("invalid format for value %s", key)
}
valConverted, err := strconv.ParseFloat(valueParts[0], 64)
if err != nil {
return 0, err
}
switch valueParts[1] {
case "kB":
valConverted *= 1 << 10
case "mB":
valConverted *= 1 << 20
case "gB":
valConverted *= 1 << 30
}
return valConverted, nil
} else if valConverted, err := strconv.ParseFloat(val, 64); err != nil {
return valConverted, nil
} else {
return valConverted, nil
}
}
func (ds *linuxSystemMetricaDataSource) checkAndUpdateData() error {
startTime := time.Now()
if startTime.Sub(ds.lastUpdate) > time.Second*linuxSystemQueryInterval {
path := fmt.Sprintf("/proc/%d/status", os.Getpid())
rawStats, err := ioutil.ReadFile(path)
if err != nil {
return err
}
lines := strings.Split(string(rawStats), "\n")
for _, line := range lines {
parts := strings.Split(line, ":")
if len(parts) == 2 {
k := strings.TrimSpace(parts[0])
v := strings.TrimSpace(parts[1])
ds.systemData[k] = v
}
}
ds.lastUpdate = startTime
}
return nil
}
// OS specific metrica
type systemMetrica struct {
sourceKey string
newrelicName string
units string
dataSource iSystemMetricaDataSource
}
func (metrica *systemMetrica) GetName() string {
return metrica.newrelicName
}
func (metrica *systemMetrica) GetUnits() string {
return metrica.units
}
func (metrica *systemMetrica) GetValue() (float64, error) {
return metrica.dataSource.GetValue(metrica.sourceKey)
}
func addRuntimeMericsToComponent(component newrelic_platform_go.IComponent) {
component.AddMetrica(&noGoroutinesMetrica{})
component.AddMetrica(&noCgoCallsMetrica{})
ds := newSystemMetricaDataSource()
metrics := []*systemMetrica{
&systemMetrica{
sourceKey: "Threads",
units: "Threads",
newrelicName: "Runtime/System/Threads",
},
&systemMetrica{
sourceKey: "FDSize",
units: "fd",
newrelicName: "Runtime/System/FDSize",
},
// Peak virtual memory size
&systemMetrica{
sourceKey: "VmPeak",
units: "bytes",
newrelicName: "Runtime/System/Memory/VmPeakSize",
},
//Virtual memory size
&systemMetrica{
sourceKey: "VmSize",
units: "bytes",
newrelicName: "Runtime/System/Memory/VmCurrent",
},
//Peak resident set size
&systemMetrica{
sourceKey: "VmHWM",
units: "bytes",
newrelicName: "Runtime/System/Memory/RssPeak",
},
//Resident set size
&systemMetrica{
sourceKey: "VmRSS",
units: "bytes",
newrelicName: "Runtime/System/Memory/RssCurrent",
},
}
for _, m := range metrics {
m.dataSource = ds
component.AddMetrica(m)
}
}