279 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			279 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Go
		
	
	
	
| package jolokia2
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| type point struct {
 | |
| 	Tags   map[string]string
 | |
| 	Fields map[string]interface{}
 | |
| }
 | |
| 
 | |
| type pointBuilder struct {
 | |
| 	metric           Metric
 | |
| 	objectAttributes []string
 | |
| 	objectPath       string
 | |
| 	substitutions    []string
 | |
| }
 | |
| 
 | |
| func newPointBuilder(metric Metric, attributes []string, path string) *pointBuilder {
 | |
| 	return &pointBuilder{
 | |
| 		metric:           metric,
 | |
| 		objectAttributes: attributes,
 | |
| 		objectPath:       path,
 | |
| 		substitutions:    makeSubstitutionList(metric.Mbean),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Build generates a point for a given mbean name/pattern and value object.
 | |
| func (pb *pointBuilder) Build(mbean string, value interface{}) []point {
 | |
| 	hasPattern := strings.Contains(mbean, "*")
 | |
| 	if !hasPattern {
 | |
| 		value = map[string]interface{}{mbean: value}
 | |
| 	}
 | |
| 
 | |
| 	valueMap, ok := value.(map[string]interface{})
 | |
| 	if !ok { // FIXME: log it and move on.
 | |
| 		panic(fmt.Sprintf("There should be a map here for %s!\n", mbean))
 | |
| 	}
 | |
| 
 | |
| 	points := make([]point, 0)
 | |
| 	for mbean, value := range valueMap {
 | |
| 
 | |
| 		points = append(points, point{
 | |
| 			Tags:   pb.extractTags(mbean),
 | |
| 			Fields: pb.extractFields(mbean, value),
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return compactPoints(points)
 | |
| }
 | |
| 
 | |
| // extractTags generates the map of tags for a given mbean name/pattern.
 | |
| func (pb *pointBuilder) extractTags(mbean string) map[string]string {
 | |
| 	propertyMap := makePropertyMap(mbean)
 | |
| 	tagMap := make(map[string]string)
 | |
| 
 | |
| 	for key, value := range propertyMap {
 | |
| 		if pb.includeTag(key) {
 | |
| 			tagName := pb.formatTagName(key)
 | |
| 			tagMap[tagName] = value
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return tagMap
 | |
| }
 | |
| 
 | |
| func (pb *pointBuilder) includeTag(tagName string) bool {
 | |
| 	for _, t := range pb.metric.TagKeys {
 | |
| 		if tagName == t {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func (pb *pointBuilder) formatTagName(tagName string) string {
 | |
| 	if tagName == "" {
 | |
| 		return ""
 | |
| 	}
 | |
| 
 | |
| 	if tagPrefix := pb.metric.TagPrefix; tagPrefix != "" {
 | |
| 		return tagPrefix + tagName
 | |
| 	}
 | |
| 
 | |
| 	return tagName
 | |
| }
 | |
| 
 | |
| // extractFields generates the map of fields for a given mbean name
 | |
| // and value object.
 | |
| func (pb *pointBuilder) extractFields(mbean string, value interface{}) map[string]interface{} {
 | |
| 	fieldMap := make(map[string]interface{})
 | |
| 	valueMap, ok := value.(map[string]interface{})
 | |
| 
 | |
| 	if ok {
 | |
| 		// complex value
 | |
| 		if len(pb.objectAttributes) == 0 {
 | |
| 			// if there were no attributes requested,
 | |
| 			// then the keys are attributes
 | |
| 			pb.fillFields("", valueMap, fieldMap)
 | |
| 
 | |
| 		} else if len(pb.objectAttributes) == 1 {
 | |
| 			// if there was a single attribute requested,
 | |
| 			// then the keys are the attribute's properties
 | |
| 			fieldName := pb.formatFieldName(pb.objectAttributes[0], pb.objectPath)
 | |
| 			pb.fillFields(fieldName, valueMap, fieldMap)
 | |
| 
 | |
| 		} else {
 | |
| 			// if there were multiple attributes requested,
 | |
| 			// then the keys are the attribute names
 | |
| 			for _, attribute := range pb.objectAttributes {
 | |
| 				fieldName := pb.formatFieldName(attribute, pb.objectPath)
 | |
| 				pb.fillFields(fieldName, valueMap[attribute], fieldMap)
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		// scalar value
 | |
| 		var fieldName string
 | |
| 		if len(pb.objectAttributes) == 0 {
 | |
| 			fieldName = pb.formatFieldName(defaultFieldName, pb.objectPath)
 | |
| 		} else {
 | |
| 			fieldName = pb.formatFieldName(pb.objectAttributes[0], pb.objectPath)
 | |
| 		}
 | |
| 
 | |
| 		pb.fillFields(fieldName, value, fieldMap)
 | |
| 	}
 | |
| 
 | |
| 	if len(pb.substitutions) > 1 {
 | |
| 		pb.applySubstitutions(mbean, fieldMap)
 | |
| 	}
 | |
| 
 | |
| 	return fieldMap
 | |
| }
 | |
| 
 | |
| // formatFieldName generates a field name from the supplied attribute and
 | |
| // path. The return value has the configured FieldPrefix and FieldSuffix
 | |
| // instructions applied.
 | |
| func (pb *pointBuilder) formatFieldName(attribute, path string) string {
 | |
| 	fieldName := attribute
 | |
| 	fieldPrefix := pb.metric.FieldPrefix
 | |
| 	fieldSeparator := pb.metric.FieldSeparator
 | |
| 
 | |
| 	if fieldPrefix != "" {
 | |
| 		fieldName = fieldPrefix + fieldName
 | |
| 	}
 | |
| 
 | |
| 	if path != "" {
 | |
| 		fieldName = fieldName + fieldSeparator + strings.Replace(path, "/", fieldSeparator, -1)
 | |
| 	}
 | |
| 
 | |
| 	return fieldName
 | |
| }
 | |
| 
 | |
| // fillFields recurses into the supplied value object, generating a named field
 | |
| // for every value it discovers.
 | |
| func (pb *pointBuilder) fillFields(name string, value interface{}, fieldMap map[string]interface{}) {
 | |
| 	if valueMap, ok := value.(map[string]interface{}); ok {
 | |
| 		// keep going until we get to something that is not a map
 | |
| 		for key, innerValue := range valueMap {
 | |
| 			if _, ok := innerValue.([]interface{}); ok {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			var innerName string
 | |
| 			if name == "" {
 | |
| 				innerName = pb.metric.FieldPrefix + key
 | |
| 			} else {
 | |
| 				innerName = name + pb.metric.FieldSeparator + key
 | |
| 			}
 | |
| 
 | |
| 			pb.fillFields(innerName, innerValue, fieldMap)
 | |
| 		}
 | |
| 
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if _, ok := value.([]interface{}); ok {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if pb.metric.FieldName != "" {
 | |
| 		name = pb.metric.FieldName
 | |
| 		if prefix := pb.metric.FieldPrefix; prefix != "" {
 | |
| 			name = prefix + name
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if name == "" {
 | |
| 		name = defaultFieldName
 | |
| 	}
 | |
| 
 | |
| 	fieldMap[name] = value
 | |
| }
 | |
| 
 | |
| // applySubstitutions updates all the keys in the supplied map
 | |
| // of fields to account for $1-style substitution instructions.
 | |
| func (pb *pointBuilder) applySubstitutions(mbean string, fieldMap map[string]interface{}) {
 | |
| 	properties := makePropertyMap(mbean)
 | |
| 
 | |
| 	for i, subKey := range pb.substitutions[1:] {
 | |
| 
 | |
| 		symbol := fmt.Sprintf("$%d", i+1)
 | |
| 		substitution := properties[subKey]
 | |
| 
 | |
| 		for fieldName, fieldValue := range fieldMap {
 | |
| 			newFieldName := strings.Replace(fieldName, symbol, substitution, -1)
 | |
| 			if fieldName != newFieldName {
 | |
| 				fieldMap[newFieldName] = fieldValue
 | |
| 				delete(fieldMap, fieldName)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // makePropertyMap returns a the mbean property-key list as
 | |
| // a dictionary. foo:x=y becomes map[string]string { "x": "y" }
 | |
| func makePropertyMap(mbean string) map[string]string {
 | |
| 	props := make(map[string]string)
 | |
| 	object := strings.SplitN(mbean, ":", 2)
 | |
| 	domain := object[0]
 | |
| 
 | |
| 	if domain != "" && len(object) == 2 {
 | |
| 		list := object[1]
 | |
| 
 | |
| 		for _, keyProperty := range strings.Split(list, ",") {
 | |
| 			pair := strings.SplitN(keyProperty, "=", 2)
 | |
| 
 | |
| 			if len(pair) != 2 {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			if key := pair[0]; key != "" {
 | |
| 				props[key] = pair[1]
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return props
 | |
| }
 | |
| 
 | |
| // makeSubstitutionList returns an array of values to
 | |
| // use as substitutions when renaming fields
 | |
| // with the $1..$N syntax. The first item in the list
 | |
| // is always the mbean domain.
 | |
| func makeSubstitutionList(mbean string) []string {
 | |
| 	subs := make([]string, 0)
 | |
| 
 | |
| 	object := strings.SplitN(mbean, ":", 2)
 | |
| 	domain := object[0]
 | |
| 
 | |
| 	if domain != "" && len(object) == 2 {
 | |
| 		subs = append(subs, domain)
 | |
| 		list := object[1]
 | |
| 
 | |
| 		for _, keyProperty := range strings.Split(list, ",") {
 | |
| 			pair := strings.SplitN(keyProperty, "=", 2)
 | |
| 
 | |
| 			if len(pair) != 2 {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			key := pair[0]
 | |
| 			if key == "" {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			property := pair[1]
 | |
| 			if !strings.Contains(property, "*") {
 | |
| 				continue
 | |
| 			}
 | |
| 
 | |
| 			subs = append(subs, key)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return subs
 | |
| }
 |