Skip to content

Commit

Permalink
Add support in jolokia get response for single value (elastic#11075)
Browse files Browse the repository at this point in the history
* Add support in jolokia get response for single value

* Use reflect.TypeOf to check attribute and value type

* Add changelog

* Replace reflect by type switching

* Fix indentation for comments
  • Loading branch information
kaiyan-sheng committed Mar 6, 2019
1 parent de272bf commit 1d966f2
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 72 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.next.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
- Migrate docker autodiscover to ECS. {issue}10757[10757] {pull}10862[10862]
- Fix issue in kubernetes module preventing usage percentages to be properly calculated. {pull}10946[10946]
- Fix for not reusable http client leading to connection leaks in Jolokia module {pull}11014[11014]
- Fix parsing error using GET in Jolokia module. {pull}11075[11075] {issue}11071[11071]

*Packetbeat*

Expand Down
35 changes: 18 additions & 17 deletions metricbeat/module/jolokia/jmx/_meta/data.json
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
{
"@timestamp": "2017-10-12T08:05:34.853Z",
"beat": {
"hostname": "host.example.com",
"name": "host.example.com"
"event": {
"dataset": "jolokia.testnamespace",
"duration": 115000,
"module": "jolokia"
},
"jolokia": {
"testnamespace": {
"memory": {
"heap_usage": {
"committed": 112721920,
"init": 64673792,
"max": 921174016,
"used": 81023984
"committed": 514850816,
"init": 536870912,
"max": 7635730432,
"used": 133286280
},
"non_heap_usage": {
"committed": 24576000,
"init": 24576000,
"max": 224395264,
"used": 17684240
"committed": 32702464,
"init": 2555904,
"max": -1,
"used": 31349800
}
},
"uptime": 580487
"uptime": 56281839
}
},
"metricset": {
"host": "jolokia:8778",
"module": "jolokia",
"name": "jmx",
"namespace": "testnamespace",
"rtt": 115
"name": "jmx"
},
"service": {
"address": "127.0.0.1:8080",
"type": "jolokia"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"request":{
"mbean":"java.lang:type=Runtime",
"attribute":"Uptime",
"type":"read"
},
"value":88622,
"timestamp":1551739190,
"status":200
}
94 changes: 67 additions & 27 deletions metricbeat/module/jolokia/jmx/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ const (

type Entry struct {
Request struct {
Mbean string `json:"mbean"`
Mbean string `json:"mbean"`
Attribute interface{} `json:"attribute"`
}
Value map[string]interface{}
Value interface{}
}

// Map responseBody to common.MapStr
Expand Down Expand Up @@ -90,7 +91,22 @@ type Entry struct {
// "timestamp": 1519409583
// "status": 200,
// }
// }
// ]
//
// A response with single value
//
// [
// {
// "request": {
// "mbean":"java.lang:type=Runtime",
// "attribute":"Uptime",
// "type":"read"
// },
// "value":88622,
// "timestamp":1551739190,
// "status":200
// }
// ]
type eventKey struct {
mbean, event string
}
Expand All @@ -103,32 +119,24 @@ func eventMapping(entries []Entry, mapping AttributeMapping) ([]common.MapStr, e
var errs multierror.Errors

for _, v := range entries {
hasWildcard := strings.Contains(v.Request.Mbean, "*")
for attribute, value := range v.Value {
if !hasWildcard {
err := parseResponseEntry(v.Request.Mbean, v.Request.Mbean, attribute, value, mbeanEvents, mapping)
if err != nil {
errs = append(errs, err)
}
continue
}

// If there was a wildcard, we are going to have an additional
// nesting level in response values, and attribute here is going
// to be actually the matching mbean name
values, ok := value.(map[string]interface{})
if !ok {
errs = append(errs, errors.Errorf("expected map of values for %s", v.Request.Mbean))
continue
}
if v.Value == nil || v.Request.Attribute == nil {
continue
}

responseMbean := attribute
for attribute, value := range values {
err := parseResponseEntry(v.Request.Mbean, responseMbean, attribute, value, mbeanEvents, mapping)
switch attribute := v.Request.Attribute.(type) {
case string:
switch entryValues := v.Value.(type) {
case float64:
err := parseResponseEntry(v.Request.Mbean, v.Request.Mbean, attribute, entryValues, mbeanEvents, mapping)
if err != nil {
errs = append(errs, err)
}
case map[string]interface{}:
constructEvents(entryValues, v, mbeanEvents, mapping, errs)
}
case []interface{}:
entryValues := v.Value.(map[string]interface{})
constructEvents(entryValues, v, mbeanEvents, mapping, errs)
}
}

Expand All @@ -140,6 +148,36 @@ func eventMapping(entries []Entry, mapping AttributeMapping) ([]common.MapStr, e
return events, errs.Err()
}

func constructEvents(entryValues map[string]interface{}, v Entry, mbeanEvents map[eventKey]common.MapStr, mapping AttributeMapping, errs multierror.Errors) {
hasWildcard := strings.Contains(v.Request.Mbean, "*")
for attribute, value := range entryValues {
if !hasWildcard {
err := parseResponseEntry(v.Request.Mbean, v.Request.Mbean, attribute, value, mbeanEvents, mapping)
if err != nil {
errs = append(errs, err)
}
continue
}

// If there was a wildcard, we are going to have an additional
// nesting level in response values, and attribute here is going
// to be actually the matching mbean name
values, ok := value.(map[string]interface{})
if !ok {
errs = append(errs, errors.Errorf("expected map of values for %s", v.Request.Mbean))
continue
}

responseMbean := attribute
for attribute, value := range values {
err := parseResponseEntry(v.Request.Mbean, responseMbean, attribute, value, mbeanEvents, mapping)
if err != nil {
errs = append(errs, err)
}
}
}
}

func selectEvent(events map[eventKey]common.MapStr, key eventKey) common.MapStr {
event, found := events[key]
if !found {
Expand Down Expand Up @@ -177,13 +215,15 @@ func parseResponseEntry(

// In case the attributeValue is a map the keys are dedotted
data := attributeValue
c, ok := data.(map[string]interface{})
if ok {
switch aValue := attributeValue.(type) {
case map[string]interface{}:
newData := map[string]interface{}{}
for k, v := range c {
for k, v := range aValue {
newData[common.DeDot(k)] = v
}
data = newData
case float64:
data = aValue
}
_, err := event.Put(field.Field, data)
return err
Expand Down
Loading

0 comments on commit 1d966f2

Please sign in to comment.