-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Metricbeat/HTTP: Support array in http/json metricset #6480
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,7 @@ type MetricSet struct { | |
body string | ||
requestEnabled bool | ||
responseEnabled bool | ||
jsonIsArray bool | ||
deDotEnabled bool | ||
} | ||
|
||
|
@@ -63,12 +64,14 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { | |
Body string `config:"body"` | ||
RequestEnabled bool `config:"request.enabled"` | ||
ResponseEnabled bool `config:"response.enabled"` | ||
JSONIsArray bool `config:"json.is_array"` | ||
DeDotEnabled bool `config:"dedot.enabled"` | ||
}{ | ||
Method: "GET", | ||
Body: "", | ||
RequestEnabled: false, | ||
ResponseEnabled: false, | ||
JSONIsArray: false, | ||
DeDotEnabled: false, | ||
} | ||
|
||
|
@@ -91,37 +94,18 @@ func New(base mb.BaseMetricSet) (mb.MetricSet, error) { | |
http: http, | ||
requestEnabled: config.RequestEnabled, | ||
responseEnabled: config.ResponseEnabled, | ||
jsonIsArray: config.JSONIsArray, | ||
deDotEnabled: config.DeDotEnabled, | ||
}, nil | ||
} | ||
|
||
// Fetch methods implements the data gathering and data conversion to the right format | ||
// It returns the event which is then forward to the output. In case of an error, a | ||
// descriptive error must be returned. | ||
func (m *MetricSet) Fetch() (common.MapStr, error) { | ||
response, err := m.http.FetchResponse() | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer response.Body.Close() | ||
|
||
var jsonBody map[string]interface{} | ||
var event map[string]interface{} | ||
|
||
body, err := ioutil.ReadAll(response.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = json.Unmarshal(body, &jsonBody) | ||
if err != nil { | ||
return nil, err | ||
} | ||
func (m *MetricSet) processBody(response *http.Response, jsonBody interface{}) common.MapStr { | ||
var event common.MapStr | ||
|
||
if m.deDotEnabled { | ||
event = common.DeDotJSON(jsonBody).(map[string]interface{}) | ||
event = common.DeDotJSON(jsonBody).(common.MapStr) | ||
} else { | ||
event = jsonBody | ||
event = jsonBody.(common.MapStr) | ||
} | ||
|
||
if m.requestEnabled { | ||
|
@@ -148,7 +132,49 @@ func (m *MetricSet) Fetch() (common.MapStr, error) { | |
// Set dynamic namespace | ||
event["_namespace"] = m.namespace | ||
|
||
return event, nil | ||
return event | ||
} | ||
|
||
// Fetch methods implements the data gathering and data conversion to the right format | ||
// It returns the event which is then forward to the output. In case of an error, a | ||
// descriptive error must be returned. | ||
func (m *MetricSet) Fetch() ([]common.MapStr, error) { | ||
response, err := m.http.FetchResponse() | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer response.Body.Close() | ||
|
||
var jsonBody common.MapStr | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As |
||
var jsonBodyArr []common.MapStr | ||
var events []common.MapStr | ||
|
||
body, err := ioutil.ReadAll(response.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if m.jsonIsArray { | ||
err = json.Unmarshal(body, &jsonBodyArr) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, obj := range jsonBodyArr { | ||
event := m.processBody(response, obj) | ||
events = append(events, event) | ||
} | ||
} else { | ||
err = json.Unmarshal(body, &jsonBody) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
event := m.processBody(response, jsonBody) | ||
events = append(events, event) | ||
} | ||
|
||
return events, nil | ||
} | ||
|
||
func (m *MetricSet) getHeaders(header http.Header) map[string]string { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,10 +12,10 @@ import ( | |
mbtest "github.com/elastic/beats/metricbeat/mb/testing" | ||
) | ||
|
||
func TestFetch(t *testing.T) { | ||
func TestFetchObject(t *testing.T) { | ||
compose.EnsureUp(t, "http") | ||
|
||
f := mbtest.NewEventFetcher(t, getConfig()) | ||
f := mbtest.NewEventsFetcher(t, getConfig("object")) | ||
event, err := f.Fetch() | ||
if !assert.NoError(t, err) { | ||
t.FailNow() | ||
|
@@ -24,23 +24,47 @@ func TestFetch(t *testing.T) { | |
t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), event) | ||
} | ||
|
||
func TestFetchArray(t *testing.T) { | ||
compose.EnsureUp(t, "http") | ||
|
||
f := mbtest.NewEventsFetcher(t, getConfig("array")) | ||
event, err := f.Fetch() | ||
if !assert.NoError(t, err) { | ||
t.FailNow() | ||
} | ||
|
||
t.Logf("%s/%s event: %+v", f.Module().Name(), f.Name(), event) | ||
} | ||
func TestData(t *testing.T) { | ||
compose.EnsureUp(t, "http") | ||
|
||
f := mbtest.NewEventFetcher(t, getConfig()) | ||
err := mbtest.WriteEvent(f, t) | ||
f := mbtest.NewEventsFetcher(t, getConfig("object")) | ||
err := mbtest.WriteEvents(f, t) | ||
if err != nil { | ||
t.Fatal("write", err) | ||
} | ||
|
||
} | ||
|
||
func getConfig() map[string]interface{} { | ||
func getConfig(jsonType string) map[string]interface{} { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you could make this a bool called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If confirming to JSON standards, yes. A string, number, object, array, true, false and null could be valid JSON. I would say to leave it this way than adding a boolean between array or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting point about the other types. I wonder when a request for that will pop up :-) Lets go with your version. |
||
var path string | ||
var responseIsArray bool | ||
switch jsonType { | ||
case "object": | ||
path = "/jsonobj" | ||
responseIsArray = false | ||
case "array": | ||
path = "/jsonarr" | ||
responseIsArray = true | ||
} | ||
|
||
return map[string]interface{}{ | ||
"module": "http", | ||
"metricsets": []string{"json"}, | ||
"hosts": []string{getEnvHost() + ":" + getEnvPort()}, | ||
"path": "/", | ||
"namespace": "testnamespace", | ||
"module": "http", | ||
"metricsets": []string{"json"}, | ||
"hosts": []string{getEnvHost() + ":" + getEnvPort()}, | ||
"path": path, | ||
"namespace": "testnamespace", | ||
"json.is_array": responseIsArray, | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should do some type conversion checks on both lines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't understand what you mean by 'both lines'. Elaborate please? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I meant on line 106 and 108 as both to a type converstion to
common.MapStr
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did assertion because
common.DeDotJSON
accepts and emitsinterface{}
. Type conversion would fail.If types are mismatched, line 160 and 170 should error out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more miscommunication on my end. I meant type assertions. I was looking for:
If for whatever reason the assertion does not work, we would have a panic otherwise. At the same time I see it should not happen here.