138 lines
3.3 KiB
Go
138 lines
3.3 KiB
Go
package nowmetric
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
)
|
|
|
|
type serializer struct {
|
|
TimestampUnits time.Duration
|
|
}
|
|
|
|
/*
|
|
Example for the JSON generated and pushed to the MID
|
|
{
|
|
"metric_type":"cpu_usage_system",
|
|
"resource":"",
|
|
"node":"ASGARD",
|
|
"value": 0.89,
|
|
"timestamp":1487365430,
|
|
"ci2metric_id":{"node":"ASGARD"},
|
|
"source":"Telegraf"
|
|
}
|
|
*/
|
|
|
|
type OIMetric struct {
|
|
Metric string `json:"metric_type"`
|
|
Resource string `json:"resource"`
|
|
Node string `json:"node"`
|
|
Value interface{} `json:"value"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
CiMapping map[string]string `json:"ci2metric_id"`
|
|
Source string `json:"source"`
|
|
}
|
|
|
|
type OIMetrics []OIMetric
|
|
|
|
func NewSerializer() (*serializer, error) {
|
|
s := &serializer{}
|
|
return s, nil
|
|
}
|
|
|
|
func (s *serializer) Serialize(metric telegraf.Metric) (out []byte, err error) {
|
|
serialized, err := s.createObject(metric)
|
|
if err != nil {
|
|
return []byte{}, nil
|
|
}
|
|
return serialized, err
|
|
}
|
|
|
|
func (s *serializer) SerializeBatch(metrics []telegraf.Metric) (out []byte, err error) {
|
|
objects := make([]byte, 0)
|
|
for _, metric := range metrics {
|
|
m, err := s.createObject(metric)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("D! [serializer.nowmetric] Dropping invalid metric: %s", metric.Name())
|
|
} else if m != nil {
|
|
objects = append(objects, m...)
|
|
}
|
|
}
|
|
replaced := bytes.Replace(objects, []byte("]["), []byte(","), -1)
|
|
return replaced, nil
|
|
}
|
|
|
|
func (s *serializer) createObject(metric telegraf.Metric) ([]byte, error) {
|
|
/* ServiceNow Operational Intelligence supports an array of JSON objects.
|
|
** Following elements accepted in the request body:
|
|
** metric_type: The name of the metric
|
|
** resource: Information about the resource for which metric data is being collected. In the example below, C:\ is the resource for which metric data is collected
|
|
** node: IP, FQDN, name of the CI, or host
|
|
** value: Value of the metric
|
|
** timestamp: Epoch timestamp of the metric in milliseconds
|
|
** ci2metric_id: List of key-value pairs to identify the CI.
|
|
** source: Data source monitoring the metric type
|
|
*/
|
|
var allmetrics OIMetrics
|
|
var oimetric OIMetric
|
|
|
|
oimetric.Source = "Telegraf"
|
|
|
|
// Process Tags to extract node & resource name info
|
|
for _, tag := range metric.TagList() {
|
|
if tag.Key == "" || tag.Value == "" {
|
|
continue
|
|
}
|
|
|
|
if tag.Key == "objectname" {
|
|
oimetric.Resource = tag.Value
|
|
}
|
|
|
|
if tag.Key == "host" {
|
|
oimetric.Node = tag.Value
|
|
}
|
|
}
|
|
|
|
// Format timestamp to UNIX epoch
|
|
oimetric.Timestamp = (metric.Time().UnixNano() / int64(time.Millisecond))
|
|
|
|
// Loop of fields value pair and build datapoint for each of them
|
|
for _, field := range metric.FieldList() {
|
|
if !verifyValue(field.Value) {
|
|
// Ignore String
|
|
continue
|
|
}
|
|
|
|
if field.Key == "" {
|
|
// Ignore Empty Key
|
|
continue
|
|
}
|
|
|
|
oimetric.Metric = field.Key
|
|
oimetric.Value = field.Value
|
|
|
|
if oimetric.Node != "" {
|
|
cimapping := map[string]string{}
|
|
cimapping["node"] = oimetric.Node
|
|
oimetric.CiMapping = cimapping
|
|
}
|
|
|
|
allmetrics = append(allmetrics, oimetric)
|
|
}
|
|
|
|
metricsJson, err := json.Marshal(allmetrics)
|
|
|
|
return metricsJson, err
|
|
}
|
|
|
|
func verifyValue(v interface{}) bool {
|
|
switch v.(type) {
|
|
case string:
|
|
return false
|
|
}
|
|
return true
|
|
}
|