195 lines
4.9 KiB
Go
195 lines
4.9 KiB
Go
|
package newrelic_platform_go
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
NEWRELIC_API_URL = "https://platform-api.newrelic.com/platform/v1/metrics"
|
||
|
)
|
||
|
|
||
|
type INewrelicPlugin interface {
|
||
|
GetMetricaKey(metrica IMetrica) string
|
||
|
Harvest() error
|
||
|
Run()
|
||
|
AddComponent(component IComponent)
|
||
|
}
|
||
|
type NewrelicPlugin struct {
|
||
|
Agent *Agent `json:"agent"`
|
||
|
Components []ComponentData `json:"components"`
|
||
|
|
||
|
ComponentModels []IComponent `json:"-"`
|
||
|
LastPollTime time.Time `json:"-"`
|
||
|
Verbose bool `json:"-"`
|
||
|
LicenseKey string `json:"-"`
|
||
|
PollIntervalInSecond int `json:"-"`
|
||
|
}
|
||
|
|
||
|
func NewNewrelicPlugin(version string, licenseKey string, pollInterval int) *NewrelicPlugin {
|
||
|
plugin := &NewrelicPlugin{
|
||
|
LicenseKey: licenseKey,
|
||
|
PollIntervalInSecond: pollInterval,
|
||
|
}
|
||
|
|
||
|
plugin.Agent = NewAgent(version)
|
||
|
plugin.Agent.CollectEnvironmentInfo()
|
||
|
|
||
|
plugin.ComponentModels = []IComponent{}
|
||
|
return plugin
|
||
|
}
|
||
|
|
||
|
func (plugin *NewrelicPlugin) Harvest() error {
|
||
|
startTime := time.Now()
|
||
|
var duration int
|
||
|
if plugin.LastPollTime.IsZero() {
|
||
|
duration = plugin.PollIntervalInSecond
|
||
|
} else {
|
||
|
duration = int(startTime.Sub(plugin.LastPollTime).Seconds())
|
||
|
}
|
||
|
|
||
|
plugin.Components = make([]ComponentData, 0, len(plugin.ComponentModels))
|
||
|
for i := 0; i < len(plugin.ComponentModels); i++ {
|
||
|
plugin.ComponentModels[i].SetDuration(duration)
|
||
|
plugin.Components = append(plugin.Components, plugin.ComponentModels[i].Harvest(plugin))
|
||
|
}
|
||
|
|
||
|
if httpCode, err := plugin.SendMetricas(); err != nil {
|
||
|
log.Printf("Can not send metricas to newrelic: %#v\n", err)
|
||
|
return err
|
||
|
} else {
|
||
|
|
||
|
if plugin.Verbose {
|
||
|
log.Printf("Got HTTP response code:%d", httpCode)
|
||
|
}
|
||
|
|
||
|
if err, isFatal := plugin.CheckResponse(httpCode); isFatal {
|
||
|
log.Printf("Got fatal error:%v\n", err)
|
||
|
return err
|
||
|
} else {
|
||
|
if err != nil {
|
||
|
log.Printf("WARNING: %v", err)
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (plugin *NewrelicPlugin) GetMetricaKey(metrica IMetrica) string {
|
||
|
var keyBuffer bytes.Buffer
|
||
|
|
||
|
keyBuffer.WriteString("Component/")
|
||
|
keyBuffer.WriteString(metrica.GetName())
|
||
|
keyBuffer.WriteString("[")
|
||
|
keyBuffer.WriteString(metrica.GetUnits())
|
||
|
keyBuffer.WriteString("]")
|
||
|
|
||
|
return keyBuffer.String()
|
||
|
}
|
||
|
|
||
|
func (plugin *NewrelicPlugin) SendMetricas() (int, error) {
|
||
|
client := &http.Client{}
|
||
|
var metricasJson []byte
|
||
|
var encodingError error
|
||
|
|
||
|
if plugin.Verbose {
|
||
|
metricasJson, encodingError = json.MarshalIndent(plugin, "", " ")
|
||
|
} else {
|
||
|
metricasJson, encodingError = json.Marshal(plugin)
|
||
|
}
|
||
|
|
||
|
if encodingError != nil {
|
||
|
return 0, encodingError
|
||
|
}
|
||
|
|
||
|
jsonAsString := string(metricasJson)
|
||
|
if plugin.Verbose {
|
||
|
log.Printf("Send data:%s \n", jsonAsString)
|
||
|
}
|
||
|
|
||
|
if httpRequest, err := http.NewRequest("POST", NEWRELIC_API_URL, strings.NewReader(jsonAsString)); err != nil {
|
||
|
return 0, err
|
||
|
} else {
|
||
|
httpRequest.Header.Set("X-License-Key", plugin.LicenseKey)
|
||
|
httpRequest.Header.Set("Content-Type", "application/json")
|
||
|
httpRequest.Header.Set("Accept", "application/json")
|
||
|
|
||
|
if httpResponse, err := client.Do(httpRequest); err != nil {
|
||
|
return 0, err
|
||
|
} else {
|
||
|
defer httpResponse.Body.Close()
|
||
|
return httpResponse.StatusCode, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// we will never get there
|
||
|
return 0, nil
|
||
|
}
|
||
|
|
||
|
func (plugin *NewrelicPlugin) ClearSentData() {
|
||
|
for _, component := range plugin.ComponentModels {
|
||
|
component.ClearSentData()
|
||
|
}
|
||
|
plugin.Components = nil
|
||
|
plugin.LastPollTime = time.Now()
|
||
|
}
|
||
|
|
||
|
func (plugin *NewrelicPlugin) CheckResponse(httpResponseCode int) (error, bool) {
|
||
|
isFatal := false
|
||
|
var err error
|
||
|
switch httpResponseCode {
|
||
|
case http.StatusOK:
|
||
|
{
|
||
|
plugin.ClearSentData()
|
||
|
}
|
||
|
case http.StatusForbidden:
|
||
|
{
|
||
|
err = fmt.Errorf("Authentication error (no license key header, or invalid license key).\n")
|
||
|
isFatal = true
|
||
|
}
|
||
|
case http.StatusBadRequest:
|
||
|
{
|
||
|
err = fmt.Errorf("The request or headers are in the wrong format or the URL is incorrect.\n")
|
||
|
isFatal = true
|
||
|
}
|
||
|
case http.StatusNotFound:
|
||
|
{
|
||
|
err = fmt.Errorf("Invalid URL\n")
|
||
|
isFatal = true
|
||
|
}
|
||
|
case http.StatusRequestEntityTooLarge:
|
||
|
{
|
||
|
err = fmt.Errorf("Too many metrics were sent in one request, or too many components (instances) were specified in one request, or other single-request limits were reached.\n")
|
||
|
//discard metrics
|
||
|
plugin.ClearSentData()
|
||
|
}
|
||
|
case http.StatusInternalServerError, http.StatusBadGateway, http.StatusServiceUnavailable, http.StatusGatewayTimeout:
|
||
|
{
|
||
|
err = fmt.Errorf("Got %v response code.Metricas will be aggregated", httpResponseCode)
|
||
|
}
|
||
|
}
|
||
|
return err, isFatal
|
||
|
}
|
||
|
|
||
|
func (plugin *NewrelicPlugin) Run() {
|
||
|
plugin.Harvest()
|
||
|
tickerChannel := time.Tick(time.Duration(plugin.PollIntervalInSecond) * time.Second)
|
||
|
for ts := range tickerChannel {
|
||
|
plugin.Harvest()
|
||
|
|
||
|
if plugin.Verbose {
|
||
|
log.Printf("Harvest ended at:%v\n", ts)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (plugin *NewrelicPlugin) AddComponent(component IComponent) {
|
||
|
plugin.ComponentModels = append(plugin.ComponentModels, component)
|
||
|
}
|