diff --git a/plugins/httpjson/README.md b/plugins/httpjson/README.md new file mode 100644 index 000000000..53ee7d8fc --- /dev/null +++ b/plugins/httpjson/README.md @@ -0,0 +1,69 @@ +# HTTP JSON Plugin + +The httpjson plugin can collect data from remote URLs which respond with JSON. Then it flattens JSON and finds all numeric values, treating them as floats. + +For example, if you have a service called _mycollector_, which has HTTP endpoint for gathering stats http://my.service.com/_stats: + +``` +[[httpjson.services]] + name = "mycollector" + + servers = [ + "http://my.service.com/_stats" + ] + + # HTTP method to use (case-sensitive) + method = "GET" +``` + +The name is used as a prefix for the measurements. + +The `method` specifies HTTP method to use for requests. + +You can specify which keys from server response should be considered as tags: + +``` +[[httpjson.services]] + ... + + tagKeys = [ + "role", + "version" + ] +``` + +**NOTE**: tag values should be strings. + +You can also specify additional request parameters for the service: + +``` +[[httpjson.services]] + ... + + [httpjson.services.parameters] + event_type = "cpu_spike" + threshold = "0.75" + +``` + + +# Sample + +Let's say that we have a service named "mycollector", which responds with: +```json +{ + "a": 0.5, + "b": { + "c": "some text", + "d": 0.1, + "e": 5 + } +} +``` + +The collected metrics will be: +``` +httpjson_mycollector_a value=0.5 +httpjson_mycollector_b_d value=0.1 +httpjson_mycollector_b_e value=5 +``` diff --git a/plugins/httpjson/httpjson.go b/plugins/httpjson/httpjson.go index c9717d558..88a7cbe63 100644 --- a/plugins/httpjson/httpjson.go +++ b/plugins/httpjson/httpjson.go @@ -22,6 +22,7 @@ type Service struct { Name string Servers []string Method string + TagKeys []string Parameters map[string]string } @@ -61,6 +62,12 @@ var sampleConfig = ` # HTTP method to use (case-sensitive) method = "GET" + # List of tag names to extract from server response + # tagKeys = [ + # "my_tag_1", + # "my_tag_2" + # ] + # HTTP parameters (all values must be strings) [httpjson.services.parameters] event_type = "cpu_spike" @@ -126,7 +133,7 @@ func (h *HttpJson) gatherServer(acc plugins.Accumulator, service Service, server return err } - var jsonOut interface{} + var jsonOut map[string]interface{} if err = json.Unmarshal([]byte(resp), &jsonOut); err != nil { return errors.New("Error decoding JSON response") } @@ -135,6 +142,14 @@ func (h *HttpJson) gatherServer(acc plugins.Accumulator, service Service, server "server": serverURL, } + for _, tag := range service.TagKeys { + switch v := jsonOut[tag].(type) { + case string: + tags[tag] = v + } + delete(jsonOut, tag) + } + processResponse(acc, service.Name, tags, jsonOut) return nil } diff --git a/plugins/httpjson/httpjson_test.go b/plugins/httpjson/httpjson_test.go index 9e9b7d2b7..8f9bfe3ac 100644 --- a/plugins/httpjson/httpjson_test.go +++ b/plugins/httpjson/httpjson_test.go @@ -28,6 +28,13 @@ const validJSON = ` } }` +const validJSONTags = ` + { + "value": 15, + "role": "master", + "build": "123" + }` + const invalidJSON = "I don't think this is JSON" const empty = "" @@ -87,8 +94,8 @@ func genMockHttpJson(response string, statusCode int) *HttpJson { }, Service{ Servers: []string{ - "http://server1.example.com/metrics/", - "http://server2.example.com/metrics/", + "http://server3.example.com/metrics/", + "http://server4.example.com/metrics/", }, Name: "other_webapp", Method: "POST", @@ -96,6 +103,10 @@ func genMockHttpJson(response string, statusCode int) *HttpJson { "httpParam1": "12", "httpParam2": "the second parameter", }, + TagKeys: []string{ + "role", + "build", + }, }, }, } @@ -185,3 +196,28 @@ func TestHttpJsonEmptyResponse(t *testing.T) { assert.Equal(t, len(strings.Split(err.Error(), "\n")), 4) assert.Equal(t, 0, len(acc.Points)) } + +// Test that the proper values are ignored or collected +func TestHttpJson200Tags(t *testing.T) { + httpjson := genMockHttpJson(validJSONTags, 200) + + var acc testutil.Accumulator + err := httpjson.Gather(&acc) + require.NoError(t, err) + + assert.Equal(t, 4, len(acc.Points)) + + for _, service := range httpjson.Services { + if service.Name == "other_webapp" { + for _, srv := range service.Servers { + require.NoError(t, + acc.ValidateTaggedValue( + fmt.Sprintf("%s_value", service.Name), + 15.0, + map[string]string{"server": srv, "role": "master", "build": "123"}, + ), + ) + } + } + } +}