feat(serializers.nowmetric): Add option for JSONv2 format (#13722)
This commit is contained in:
parent
8bc6822e28
commit
93bf2becce
|
|
@ -1,16 +1,22 @@
|
||||||
# ServiceNow Metrics serializer
|
# ServiceNow Metrics serializer
|
||||||
|
|
||||||
The ServiceNow Metrics serializer outputs metrics in the [ServiceNow Operational Intelligence format][ServiceNow-format].
|
The ServiceNow Metrics serializer outputs metrics in the
|
||||||
|
[ServiceNow Operational Intelligence format][ServiceNow-format] or optionally
|
||||||
|
with the [ServiceNow JSONv2 format][ServiceNow-jsonv2]
|
||||||
|
|
||||||
It can be used to write to a file using the file output, or for sending metrics to a MID Server with Enable REST endpoint activated using the standard telegraf HTTP output.
|
It can be used to write to a file using the file output, or for sending metrics
|
||||||
If you're using the HTTP output, this serializer knows how to batch the metrics so you don't end up with an HTTP POST per metric.
|
to a MID Server with Enable REST endpoint activated using the standard telegraf
|
||||||
|
HTTP output. If you are using the HTTP output, this serializer knows how to
|
||||||
|
batch the metrics so you do not end up with an HTTP POST per metric.
|
||||||
|
|
||||||
[ServiceNow-format]: https://docs.servicenow.com/bundle/london-it-operations-management/page/product/event-management/reference/mid-POST-metrics.html
|
[ServiceNow-format]: https://docs.servicenow.com/bundle/london-it-operations-management/page/product/event-management/reference/mid-POST-metrics.html
|
||||||
|
[ServiceNow-jsonv2]: https://docs.servicenow.com/bundle/tokyo-application-development/page/integrate/inbound-other-web-services/concept/c_JSONv2WebService.html
|
||||||
|
|
||||||
An example event looks like:
|
An example Operational Intelligence format event looks like:
|
||||||
|
|
||||||
```javascript
|
```json
|
||||||
[{
|
[
|
||||||
|
{
|
||||||
"metric_type": "Disk C: % Free Space",
|
"metric_type": "Disk C: % Free Space",
|
||||||
"resource": "C:\\",
|
"resource": "C:\\",
|
||||||
"node": "lnux100",
|
"node": "lnux100",
|
||||||
|
|
@ -19,8 +25,29 @@ An example event looks like:
|
||||||
"ci2metric_id": {
|
"ci2metric_id": {
|
||||||
"node": "lnux100"
|
"node": "lnux100"
|
||||||
},
|
},
|
||||||
"source": “Telegraf”
|
"source": "Telegraf"
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
An example of the JSONv2 format even looks like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"metric_type": "Disk C: % Free Space",
|
||||||
|
"resource": "C:\\",
|
||||||
|
"node": "lnux100",
|
||||||
|
"value": 50,
|
||||||
|
"timestamp": 1473183012000,
|
||||||
|
"ci2metric_id": {
|
||||||
|
"node": "lnux100"
|
||||||
|
},
|
||||||
|
"source": "Telegraf"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using with the HTTP output
|
## Using with the HTTP output
|
||||||
|
|
@ -55,6 +82,13 @@ To send this data to a ServiceNow MID Server with Web Server extension activated
|
||||||
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_OUTPUT.md
|
||||||
data_format = "nowmetric"
|
data_format = "nowmetric"
|
||||||
|
|
||||||
|
## Format Type
|
||||||
|
## By default, the serializer returns an array of metrics matching the
|
||||||
|
## Now Metric Operational Intelligence format or with the option set to 'oi'.
|
||||||
|
## Optionally, if set to 'jsonv2' the output format will involve the newer
|
||||||
|
## JSON object based format.
|
||||||
|
# nowmetric_format = "oi"
|
||||||
|
|
||||||
## Additional HTTP headers
|
## Additional HTTP headers
|
||||||
[outputs.http.headers]
|
[outputs.http.headers]
|
||||||
# # Should be set manually to "application/json" for json data_format
|
# # Should be set manually to "application/json" for json data_format
|
||||||
|
|
@ -66,6 +100,23 @@ Starting with the [London release](https://docs.servicenow.com/bundle/london-it-
|
||||||
),
|
),
|
||||||
you also need to explicitly create event rule to allow binding of metric events to host CIs.
|
you also need to explicitly create event rule to allow binding of metric events to host CIs.
|
||||||
|
|
||||||
|
## Metric Format
|
||||||
|
|
||||||
|
The following describes the two options of the `nowmetric_format` option:
|
||||||
|
|
||||||
|
The Operational Intelligence format is used along with the
|
||||||
|
`/api/mid/sa/metrics` API endpoint. The payload is requires a JSON array full
|
||||||
|
of metrics. This is the default settings and used when set to `oi`. See the
|
||||||
|
[ServiceNow KB0853084][KB0853084] for more details on this format.
|
||||||
|
|
||||||
|
Another option is the use of the [JSONv2 web service][jsonv2]. This service
|
||||||
|
requires a different format that is [JSON object based][jsonv2_format]. This
|
||||||
|
option is used when set to `jsonv2`.
|
||||||
|
|
||||||
|
[KB0853084]: https://support.servicenow.com/kb?id=kb_article_view&sysparm_article=KB0853084
|
||||||
|
[jsonv2]: https://docs.servicenow.com/bundle/tokyo-application-development/page/integrate/inbound-other-web-services/concept/c_JSONv2WebService.html
|
||||||
|
[jsonv2_format]: https://docs.servicenow.com/bundle/tokyo-application-development/page/integrate/inbound-other-web-services/concept/c_JSONObjectFormat.html
|
||||||
|
|
||||||
## Using with the File output
|
## Using with the File output
|
||||||
|
|
||||||
You can use the file output to output the payload in a file.
|
You can use the file output to output the payload in a file.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package nowmetric
|
package nowmetric
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -10,20 +9,9 @@ import (
|
||||||
"github.com/influxdata/telegraf/plugins/serializers"
|
"github.com/influxdata/telegraf/plugins/serializers"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Serializer struct{}
|
type Serializer struct {
|
||||||
|
Format string `toml:"nowmetric_format"`
|
||||||
/*
|
|
||||||
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 {
|
type OIMetric struct {
|
||||||
Metric string `json:"metric_type"`
|
Metric string `json:"metric_type"`
|
||||||
|
|
@ -36,30 +24,47 @@ type OIMetric struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type OIMetrics []OIMetric
|
type OIMetrics []OIMetric
|
||||||
|
type OIMetricsObj struct {
|
||||||
|
Records []OIMetric `json:"records"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Serializer) Init() error {
|
||||||
|
switch s.Format {
|
||||||
|
case "":
|
||||||
|
s.Format = "oi"
|
||||||
|
case "oi", "jsonv2":
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid format %q", s.Format)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Serializer) Serialize(metric telegraf.Metric) (out []byte, err error) {
|
func (s *Serializer) Serialize(metric telegraf.Metric) (out []byte, err error) {
|
||||||
serialized, err := s.createObject(metric)
|
m := s.createObject(metric)
|
||||||
if err != nil {
|
|
||||||
return []byte{}, err
|
if s.Format == "jsonv2" {
|
||||||
|
obj := OIMetricsObj{Records: m}
|
||||||
|
return json.Marshal(obj)
|
||||||
}
|
}
|
||||||
return serialized, nil
|
return json.Marshal(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Serializer) SerializeBatch(metrics []telegraf.Metric) (out []byte, err error) {
|
func (s *Serializer) SerializeBatch(metrics []telegraf.Metric) (out []byte, err error) {
|
||||||
objects := make([]byte, 0)
|
objects := make([]OIMetric, 0)
|
||||||
for _, metric := range metrics {
|
for _, metric := range metrics {
|
||||||
m, err := s.createObject(metric)
|
objects = append(objects, s.createObject(metric)...)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("dropping invalid metric: %s", metric.Name())
|
|
||||||
} else if m != nil {
|
|
||||||
objects = append(objects, m...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
replaced := bytes.Replace(objects, []byte("]["), []byte(","), -1)
|
|
||||||
return replaced, nil
|
if s.Format == "jsonv2" {
|
||||||
|
obj := OIMetricsObj{Records: objects}
|
||||||
|
return json.Marshal(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(objects)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Serializer) createObject(metric telegraf.Metric) ([]byte, error) {
|
func (s *Serializer) createObject(metric telegraf.Metric) OIMetrics {
|
||||||
/* ServiceNow Operational Intelligence supports an array of JSON objects.
|
/* ServiceNow Operational Intelligence supports an array of JSON objects.
|
||||||
** Following elements accepted in the request body:
|
** Following elements accepted in the request body:
|
||||||
** metric_type: The name of the metric
|
** metric_type: The name of the metric
|
||||||
|
|
@ -117,9 +122,7 @@ func (s *Serializer) createObject(metric telegraf.Metric) ([]byte, error) {
|
||||||
allmetrics = append(allmetrics, oimetric)
|
allmetrics = append(allmetrics, oimetric)
|
||||||
}
|
}
|
||||||
|
|
||||||
metricsJSON, err := json.Marshal(allmetrics)
|
return allmetrics
|
||||||
|
|
||||||
return metricsJSON, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyValue(v interface{}) bool {
|
func verifyValue(v interface{}) bool {
|
||||||
|
|
|
||||||
|
|
@ -199,3 +199,52 @@ func TestSerializeBatch(t *testing.T) {
|
||||||
buf,
|
buf,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSerializeJSONv2Format(t *testing.T) {
|
||||||
|
m := metric.New(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
)
|
||||||
|
s := &Serializer{Format: "jsonv2"}
|
||||||
|
buf, err := s.Serialize(m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(
|
||||||
|
t,
|
||||||
|
[]byte(`{"records":[{"metric_type":"value","resource":"","node":"","value":42,"timestamp":0,"ci2metric_id":null,"source":"Telegraf"}]}`),
|
||||||
|
buf,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeJSONv2FormatBatch(t *testing.T) {
|
||||||
|
m := metric.New(
|
||||||
|
"cpu",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"value": 42.0,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
)
|
||||||
|
s := &Serializer{Format: "jsonv2"}
|
||||||
|
metrics := []telegraf.Metric{m, m}
|
||||||
|
buf, err := s.SerializeBatch(metrics)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(
|
||||||
|
t,
|
||||||
|
[]byte(
|
||||||
|
`{"records":[`+
|
||||||
|
`{"metric_type":"value","resource":"","node":"","value":42,"timestamp":0,"ci2metric_id":null,"source":"Telegraf"},`+
|
||||||
|
`{"metric_type":"value","resource":"","node":"","value":42,"timestamp":0,"ci2metric_id":null,"source":"Telegraf"}`+
|
||||||
|
`]}`,
|
||||||
|
),
|
||||||
|
buf,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSerializeInvalidFormat(t *testing.T) {
|
||||||
|
s := &Serializer{Format: "foo"}
|
||||||
|
require.Error(t, s.Init())
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue