197 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
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)
 | 
						|
	}
 | 
						|
}
 |