func trackingFeatures()

in confgenerator/feature_tracking.go [153:361]


func trackingFeatures(c reflect.Value, md metadata, feature Feature) ([]Feature, error) {
	if customFeatures, ok := c.Interface().(CustomFeatures); ok {
		cfs, err := customFeatures.ExtractFeatures()
		if err != nil {
			return nil, err
		}
		var features []Feature
		for _, cf := range cfs {
			features = append(features, Feature{
				Module: feature.Module,
				Kind:   feature.Kind,
				Type:   feature.Type,
				Key:    append(feature.Key, cf.Key...),
				Value:  cf.Value,
			})
		}
		return features, nil
	}

	if md.isExcluded {
		return nil, nil
	}
	t := c.Type()

	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	if c.IsZero() {
		return nil, nil
	}

	v := reflect.Indirect(c)
	if v.Kind() == reflect.Invalid {
		return nil, nil
	}

	var features []Feature

	switch kind := t.Kind(); {
	case kind == reflect.Struct:
		// If struct has tracking it must have a value
		if md.hasTracking && !md.hasOverride {
			return nil, ErrTrackingOverrideStruct
		}

		if md.yamlTag != "" {
			feature.Key = append(feature.Key, md.yamlTag)
		}

		if md.hasTracking {
			// If struct is inline there is no associated name for key generation
			// By default inline structs of a tracked field are also tracked
			if md.isInline {
				return nil, ErrTrackingInlineStruct
			} else {
				// For structs that are in a Component. An extra metric is added with
				// the value being the override value from the yaml tag
				ftr := feature
				ftr.Value = md.overrideValue
				features = append(features, ftr)
			}
		}

		// Iterate over all available fields and read the tag value
		for i := 0; i < t.NumField(); i++ {
			// Get the field, returns https://golang.org/pkg/reflect/#StructField
			field := t.Field(i)
			if !field.IsExported() {
				continue
			}
			// Type field name is part of the ConfigComponent definition.
			// All user visible component inlines that component, this field can help
			// us assert that a certain component is enabled.
			// Capture special metrics for enabled receiver or processor
			if field.Name == "Type" {
				f := feature
				f.Key = append(f.Key, "enabled")
				f.Value = "true"
				features = append(features, f)
				continue
			}

			f := Feature{
				Module: feature.Module,
				Kind:   feature.Kind,
				Type:   feature.Type,
				Key:    append([]string{}, feature.Key...),
				Value:  feature.Value,
			}

			tf, err := trackingFeatures(v.Field(i), getMetadata(field), f)
			if err != nil {
				return nil, err
			}
			features = append(features, tf...)
		}
	case kind == reflect.Map:

		// Create map length metric
		features = append(features, Feature{
			Module: feature.Module,
			Kind:   feature.Kind,
			Type:   feature.Type,
			Key:    append(feature.Key, md.yamlTag, "__length"),
			Value:  fmt.Sprintf("%d", v.Len()),
		})

		keys := make([]string, 0)
		for _, k := range v.MapKeys() {
			keys = append(keys, k.String())
		}
		sort.Strings(keys)

		for i, key := range keys {
			f := Feature{
				Module: feature.Module,
				Kind:   feature.Kind,
				Type:   feature.Type,
				Key:    append(feature.Key, md.yamlTag),
			}
			vAtKey := v.MapIndex(reflect.ValueOf(key))
			t := vAtKey.Type()
			fs := make([]Feature, 0)

			k := fmt.Sprintf("[%d]", i)
			if md.keepKeys {
				features = append(features, Feature{
					Module: feature.Module,
					Kind:   feature.Kind,
					Type:   feature.Type,
					Key:    append(feature.Key, md.yamlTag, k, "__key"),
					Value:  key,
				})
			}

			mdCopy := md.deepCopy()

			var err error
			if t.Kind() == reflect.Struct {
				f.Key = append(f.Key, k)
				mdCopy.yamlTag = ""
				fs, err = trackingFeatures(vAtKey, mdCopy, f)
			} else {
				mdCopy.yamlTag = k
				fs, err = trackingFeatures(vAtKey, mdCopy, f)
			}

			if err != nil {
				return nil, err
			}
			features = append(features, fs...)
		}

	case kind == reflect.Slice || kind == reflect.Array:

		// Create array length metric
		features = append(features, Feature{
			Module: feature.Module,
			Kind:   feature.Kind,
			Type:   feature.Type,
			Key:    append(feature.Key, md.yamlTag, "__length"),
			Value:  fmt.Sprintf("%d", v.Len()),
		})

		for i := 0; i < v.Len(); i++ {
			f := Feature{
				Module: feature.Module,
				Kind:   feature.Kind,
				Type:   feature.Type,
				Key:    append(feature.Key, md.yamlTag),
			}

			v := v.Index(i)
			t := v.Type()
			fs := make([]Feature, 0)
			m2 := md.deepCopy()

			var err error
			if t.Kind() == reflect.Struct {
				f.Key = append(f.Key, fmt.Sprintf("[%d]", i))
				m2.yamlTag = ""
				fs, err = trackingFeatures(v, m2, f)
			} else {
				m2.yamlTag = fmt.Sprintf("[%d]", i)
				fs, err = trackingFeatures(v, m2, f)
			}

			if err != nil {
				return nil, err
			}
			features = append(features, fs...)
		}

	default:
		if skipField(v, md) {
			return nil, nil
		}
		feature.Key = append(feature.Key, md.yamlTag)
		if md.hasOverride {
			feature.Value = md.overrideValue
		} else {
			feature.Value = fmt.Sprintf("%v", v.Interface())
		}
		features = append(features, feature)
	}

	return features, nil
}