diff options
Diffstat (limited to '')
-rw-r--r-- | src/go/collectors/go.d.plugin/pkg/stm/stm.go | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/pkg/stm/stm.go b/src/go/collectors/go.d.plugin/pkg/stm/stm.go new file mode 100644 index 000000000..7d07ba9a4 --- /dev/null +++ b/src/go/collectors/go.d.plugin/pkg/stm/stm.go @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package stm + +import ( + "fmt" + "log" + "reflect" + "strconv" + "strings" +) + +const ( + fieldTagName = "stm" + structKey = "STMKey" +) + +type ( + Value interface { + WriteTo(rv map[string]int64, key string, mul, div int) + } +) + +// ToMap converts struct to a map[string]int64 based on 'stm' tags +func ToMap(s ...interface{}) map[string]int64 { + rv := map[string]int64{} + for _, v := range s { + value := reflect.Indirect(reflect.ValueOf(v)) + toMap(value, rv, "", 1, 1) + } + return rv +} + +func toMap(value reflect.Value, rv map[string]int64, key string, mul, div int) { + if !value.IsValid() { + log.Panicf("value is not valid key=%s", key) + } + if value.CanInterface() { + val, ok := value.Interface().(Value) + if ok { + val.WriteTo(rv, key, mul, div) + return + } + } + switch value.Kind() { + case reflect.Ptr: + convertPtr(value, rv, key, mul, div) + case reflect.Struct: + convertStruct(value, rv, key) + case reflect.Array, reflect.Slice: + convertArraySlice(value, rv, key, mul, div) + case reflect.Map: + convertMap(value, rv, key, mul, div) + case reflect.Bool: + convertBool(value, rv, key) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + convertInteger(value, rv, key, mul, div) + case reflect.Float32, reflect.Float64: + convertFloat(value, rv, key, mul, div) + case reflect.Interface: + convertInterface(value, rv, key, mul, div) + default: + log.Panicf("unsupported data type: %v", value.Kind()) + } +} + +func convertPtr(value reflect.Value, rv map[string]int64, key string, mul, div int) { + if !value.IsNil() { + toMap(value.Elem(), rv, key, mul, div) + } +} + +func convertStruct(value reflect.Value, rv map[string]int64, key string) { + t := value.Type() + k := value.FieldByName(structKey) + if k.Kind() == reflect.String { + key = joinPrefix(key, k.String()) + } + for i := 0; i < t.NumField(); i++ { + ft := t.Field(i) + tag, ok := ft.Tag.Lookup(fieldTagName) + if !ok || ft.Name == structKey { + continue + } + value := value.Field(i) + prefix, mul, div := parseTag(tag) + toMap(value, rv, joinPrefix(key, prefix), mul, div) + } +} + +func convertMap(value reflect.Value, rv map[string]int64, key string, mul, div int) { + if value.IsNil() { + log.Panicf("value is nil key=%s", key) + } + for _, k := range value.MapKeys() { + toMap(value.MapIndex(k), rv, joinPrefix(key, k.String()), mul, div) + } +} + +func convertArraySlice(value reflect.Value, rv map[string]int64, key string, mul, div int) { + for i := 0; i < value.Len(); i++ { + toMap(value.Index(i), rv, key, mul, div) + } +} + +func convertBool(value reflect.Value, rv map[string]int64, key string) { + if _, ok := rv[key]; ok { + log.Panic("duplicate key: ", key) + } + if value.Bool() { + rv[key] = 1 + } else { + rv[key] = 0 + } +} + +func convertInteger(value reflect.Value, rv map[string]int64, key string, mul, div int) { + if _, ok := rv[key]; ok { + log.Panic("duplicate key: ", key) + } + intVal := value.Int() + rv[key] = intVal * int64(mul) / int64(div) +} + +func convertFloat(value reflect.Value, rv map[string]int64, key string, mul, div int) { + if _, ok := rv[key]; ok { + log.Panic("duplicate key: ", key) + } + floatVal := value.Float() + rv[key] = int64(floatVal * float64(mul) / float64(div)) +} + +func convertInterface(value reflect.Value, rv map[string]int64, key string, mul, div int) { + fv := reflect.ValueOf(value.Interface()) + toMap(fv, rv, key, mul, div) +} + +func joinPrefix(prefix, key string) string { + if prefix == "" { + return key + } + if key == "" { + return prefix + } + return prefix + "_" + key +} + +func parseTag(tag string) (prefix string, mul int, div int) { + tokens := strings.Split(tag, ",") + mul = 1 + div = 1 + var err error + switch len(tokens) { + case 3: + div, err = strconv.Atoi(tokens[2]) + if err != nil { + log.Panic(err) + } + fallthrough + case 2: + mul, err = strconv.Atoi(tokens[1]) + if err != nil { + log.Panic(err) + } + fallthrough + case 1: + prefix = tokens[0] + default: + log.Panic(fmt.Errorf("invalid tag format: %s", tag)) + } + return +} |