From 298a1d4396e297c412c8e90d8ad0201aa7c0265b Mon Sep 17 00:00:00 2001 From: Sven Rebhan <36194019+srebhan@users.noreply.github.com> Date: Mon, 12 Feb 2024 15:43:01 +0100 Subject: [PATCH] chore(processors.converter): Cleanup code (#14744) --- internal/type_conversions.go | 31 ++ plugins/processors/converter/converter.go | 445 +++++++--------------- 2 files changed, 176 insertions(+), 300 deletions(-) diff --git a/internal/type_conversions.go b/internal/type_conversions.go index 204649ed2..d58895a24 100644 --- a/internal/type_conversions.go +++ b/internal/type_conversions.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "strconv" + "strings" ) var ErrOutOfRange = strconv.ErrRange @@ -103,6 +104,9 @@ func ToFloat32(value interface{}) (float32, error) { func ToUint64(value interface{}) (uint64, error) { switch v := value.(type) { case string: + if strings.HasPrefix(v, "0x") { + return strconv.ParseUint(strings.TrimPrefix(v, "0x"), 16, 64) + } return strconv.ParseUint(v, 10, 64) case []byte: return strconv.ParseUint(string(v), 10, 64) @@ -167,6 +171,10 @@ func ToUint64(value interface{}) (uint64, error) { func ToUint32(value interface{}) (uint32, error) { switch v := value.(type) { case string: + if strings.HasPrefix(v, "0x") { + x, err := strconv.ParseUint(strings.TrimPrefix(v, "0x"), 16, 32) + return uint32(x), err + } x, err := strconv.ParseUint(v, 10, 32) return uint32(x), err case []byte: @@ -237,6 +245,10 @@ func ToUint32(value interface{}) (uint32, error) { func ToUint16(value interface{}) (uint16, error) { switch v := value.(type) { case string: + if strings.HasPrefix(v, "0x") { + x, err := strconv.ParseUint(strings.TrimPrefix(v, "0x"), 16, 16) + return uint16(x), err + } x, err := strconv.ParseUint(v, 10, 32) return uint16(x), err case []byte: @@ -310,6 +322,10 @@ func ToUint16(value interface{}) (uint16, error) { func ToUint8(value interface{}) (uint8, error) { switch v := value.(type) { case string: + if strings.HasPrefix(v, "0x") { + x, err := strconv.ParseUint(strings.TrimPrefix(v, "0x"), 16, 8) + return uint8(x), err + } x, err := strconv.ParseUint(v, 10, 32) return uint8(x), err case []byte: @@ -386,6 +402,9 @@ func ToUint8(value interface{}) (uint8, error) { func ToInt64(value interface{}) (int64, error) { switch v := value.(type) { case string: + if strings.HasPrefix(v, "0x") { + return strconv.ParseInt(strings.TrimPrefix(v, "0x"), 16, 64) + } return strconv.ParseInt(v, 10, 64) case []byte: return strconv.ParseInt(string(v), 10, 64) @@ -441,6 +460,10 @@ func ToInt64(value interface{}) (int64, error) { func ToInt32(value interface{}) (int32, error) { switch v := value.(type) { case string: + if strings.HasPrefix(v, "0x") { + x, err := strconv.ParseInt(strings.TrimPrefix(v, "0x"), 16, 32) + return int32(x), err + } x, err := strconv.ParseInt(v, 10, 32) return int32(x), err case []byte: @@ -508,6 +531,10 @@ func ToInt32(value interface{}) (int32, error) { func ToInt16(value interface{}) (int16, error) { switch v := value.(type) { case string: + if strings.HasPrefix(v, "0x") { + x, err := strconv.ParseInt(strings.TrimPrefix(v, "0x"), 16, 16) + return int16(x), err + } x, err := strconv.ParseInt(v, 10, 32) return int16(x), err case []byte: @@ -578,6 +605,10 @@ func ToInt16(value interface{}) (int16, error) { func ToInt8(value interface{}) (int8, error) { switch v := value.(type) { case string: + if strings.HasPrefix(v, "0x") { + x, err := strconv.ParseInt(strings.TrimPrefix(v, "0x"), 16, 8) + return int8(x), err + } x, err := strconv.ParseInt(v, 10, 32) return int8(x), err case []byte: diff --git a/plugins/processors/converter/converter.go b/plugins/processors/converter/converter.go index 5576d9620..8ca1c021f 100644 --- a/plugins/processors/converter/converter.go +++ b/plugins/processors/converter/converter.go @@ -6,7 +6,6 @@ import ( "errors" "math" "math/big" - "strconv" "strings" "github.com/influxdata/telegraf" @@ -143,80 +142,46 @@ func (p *Converter) convertTags(metric telegraf.Metric) { } for key, value := range metric.Tags() { - if p.tagConversions.Measurement != nil && p.tagConversions.Measurement.Match(key) { - metric.RemoveTag(key) + switch { + case p.tagConversions.Measurement != nil && p.tagConversions.Measurement.Match(key): metric.SetName(value) - continue - } - - if p.tagConversions.String != nil && p.tagConversions.String.Match(key) { - metric.RemoveTag(key) + case p.tagConversions.String != nil && p.tagConversions.String.Match(key): metric.AddField(key, value) + case p.tagConversions.Integer != nil && p.tagConversions.Integer.Match(key): + if v, err := toInteger(value); err != nil { + p.Log.Errorf("Converting to integer [%T] failed: %v", value, err) + } else { + metric.AddField(key, v) + } + case p.tagConversions.Unsigned != nil && p.tagConversions.Unsigned.Match(key): + if v, err := toUnsigned(value); err != nil { + p.Log.Errorf("Converting to unsigned [%T] failed: %v", value, err) + } else { + metric.AddField(key, v) + } + case p.tagConversions.Boolean != nil && p.tagConversions.Boolean.Match(key): + if v, err := internal.ToBool(value); err != nil { + p.Log.Errorf("Converting to boolean [%T] failed: %v", value, err) + } else { + metric.AddField(key, v) + } + case p.tagConversions.Float != nil && p.tagConversions.Float.Match(key): + if v, err := toFloat(value); err != nil { + p.Log.Errorf("Converting to float [%T] failed: %v", value, err) + } else { + metric.AddField(key, v) + } + case p.tagConversions.Timestamp != nil && p.tagConversions.Timestamp.Match(key): + if time, err := internal.ParseTimestamp(p.Tags.TimestampFormat, value, nil); err != nil { + p.Log.Errorf("Converting to timestamp [%T] failed: %v", value, err) + continue + } else { + metric.SetTime(time) + } + default: continue } - - if p.tagConversions.Integer != nil && p.tagConversions.Integer.Match(key) { - v, ok := toInteger(value) - if !ok { - metric.RemoveTag(key) - p.Log.Errorf("error converting to integer [%T]: %v", value, value) - continue - } - - metric.RemoveTag(key) - metric.AddField(key, v) - } - - if p.tagConversions.Unsigned != nil && p.tagConversions.Unsigned.Match(key) { - v, ok := toUnsigned(value) - if !ok { - metric.RemoveTag(key) - p.Log.Errorf("error converting to unsigned [%T]: %v", value, value) - continue - } - - metric.RemoveTag(key) - metric.AddField(key, v) - continue - } - - if p.tagConversions.Boolean != nil && p.tagConversions.Boolean.Match(key) { - v, ok := toBool(value) - if !ok { - metric.RemoveTag(key) - p.Log.Errorf("error converting to boolean [%T]: %v", value, value) - continue - } - - metric.RemoveTag(key) - metric.AddField(key, v) - continue - } - - if p.tagConversions.Float != nil && p.tagConversions.Float.Match(key) { - v, ok := toFloat(value) - if !ok { - metric.RemoveTag(key) - p.Log.Errorf("error converting to float [%T]: %v", value, value) - continue - } - - metric.RemoveTag(key) - metric.AddField(key, v) - continue - } - - if p.tagConversions.Timestamp != nil && p.tagConversions.Timestamp.Match(key) { - time, err := internal.ParseTimestamp(p.Tags.TimestampFormat, value, nil) - if err != nil { - p.Log.Errorf("error converting to timestamp [%T]: %v", value, value) - continue - } - - metric.RemoveTag(key) - metric.SetTime(time) - continue - } + metric.RemoveTag(key) } } @@ -227,277 +192,157 @@ func (p *Converter) convertFields(metric telegraf.Metric) { } for key, value := range metric.Fields() { - if p.fieldConversions.Measurement != nil && p.fieldConversions.Measurement.Match(key) { - v, ok := toString(value) - if !ok { + switch { + case p.fieldConversions.Measurement != nil && p.fieldConversions.Measurement.Match(key): + if v, err := internal.ToString(value); err != nil { + p.Log.Errorf("Converting to measurement [%T] failed: %v", value, err) + } else { + metric.SetName(v) + } + metric.RemoveField(key) + case p.fieldConversions.Tag != nil && p.fieldConversions.Tag.Match(key): + if v, err := internal.ToString(value); err != nil { + p.Log.Errorf("Converting to tag [%T] failed: %v", value, err) + } else { + metric.AddTag(key, v) + } + metric.RemoveField(key) + case p.fieldConversions.Float != nil && p.fieldConversions.Float.Match(key): + if v, err := toFloat(value); err != nil { + p.Log.Errorf("Converting to float [%T] failed: %v", value, err) metric.RemoveField(key) - p.Log.Errorf("error converting to measurement [%T]: %v", value, value) - continue + } else { + metric.AddField(key, v) } - - metric.RemoveField(key) - metric.SetName(v) - continue - } - - if p.fieldConversions.Tag != nil && p.fieldConversions.Tag.Match(key) { - v, ok := toString(value) - if !ok { + case p.fieldConversions.Integer != nil && p.fieldConversions.Integer.Match(key): + if v, err := toInteger(value); err != nil { + p.Log.Errorf("Converting to integer [%T] failed: %v", value, err) metric.RemoveField(key) - p.Log.Errorf("error converting to tag [%T]: %v", value, value) - continue + } else { + metric.AddField(key, v) } - - metric.RemoveField(key) - metric.AddTag(key, v) - continue - } - - if p.fieldConversions.Float != nil && p.fieldConversions.Float.Match(key) { - v, ok := toFloat(value) - if !ok { + case p.fieldConversions.Unsigned != nil && p.fieldConversions.Unsigned.Match(key): + if v, err := toUnsigned(value); err != nil { + p.Log.Errorf("Converting to unsigned [%T] failed: %v", value, err) metric.RemoveField(key) - p.Log.Errorf("error converting to float [%T]: %v", value, value) - continue + } else { + metric.AddField(key, v) } - - metric.RemoveField(key) - metric.AddField(key, v) - continue - } - - if p.fieldConversions.Integer != nil && p.fieldConversions.Integer.Match(key) { - v, ok := toInteger(value) - if !ok { + case p.fieldConversions.Boolean != nil && p.fieldConversions.Boolean.Match(key): + if v, err := internal.ToBool(value); err != nil { + p.Log.Errorf("Converting to bool [%T] failed: %v", value, err) metric.RemoveField(key) - p.Log.Errorf("error converting to integer [%T]: %v", value, value) - continue + } else { + metric.AddField(key, v) } - - metric.RemoveField(key) - metric.AddField(key, v) - continue - } - - if p.fieldConversions.Unsigned != nil && p.fieldConversions.Unsigned.Match(key) { - v, ok := toUnsigned(value) - if !ok { + case p.fieldConversions.String != nil && p.fieldConversions.String.Match(key): + if v, err := internal.ToString(value); err != nil { + p.Log.Errorf("Converting to string [%T] failed: %v", value, err) metric.RemoveField(key) - p.Log.Errorf("error converting to unsigned [%T]: %v", value, value) - continue + } else { + metric.AddField(key, v) } - - metric.RemoveField(key) - metric.AddField(key, v) - continue - } - - if p.fieldConversions.Boolean != nil && p.fieldConversions.Boolean.Match(key) { - v, ok := toBool(value) - if !ok { + case p.fieldConversions.Timestamp != nil && p.fieldConversions.Timestamp.Match(key): + if time, err := internal.ParseTimestamp(p.Fields.TimestampFormat, value, nil); err != nil { + p.Log.Errorf("Converting to timestamp [%T] failed: %v", value, err) + } else { + metric.SetTime(time) metric.RemoveField(key) - p.Log.Errorf("error converting to bool [%T]: %v", value, value) - continue } - - metric.RemoveField(key) - metric.AddField(key, v) - continue - } - - if p.fieldConversions.String != nil && p.fieldConversions.String.Match(key) { - v, ok := toString(value) - if !ok { - metric.RemoveField(key) - p.Log.Errorf("Error converting to string [%T]: %v", value, value) - continue - } - - metric.RemoveField(key) - metric.AddField(key, v) - continue - } - - if p.fieldConversions.Timestamp != nil && p.fieldConversions.Timestamp.Match(key) { - time, err := internal.ParseTimestamp(p.Fields.TimestampFormat, value, nil) - if err != nil { - p.Log.Errorf("error converting to timestamp [%T]: %v", value, value) - continue - } - - metric.RemoveField(key) - metric.SetTime(time) - continue } } } -func toBool(v interface{}) (val bool, ok bool) { +func toInteger(v interface{}) (int64, error) { switch value := v.(type) { - case int64: - return value != 0, true - case uint64: - return value != 0, true - case float64: - return value != 0, true - case bool: - return value, true - case string: - result, err := strconv.ParseBool(value) - return result, err == nil - } - return false, false -} - -func toInteger(v interface{}) (int64, bool) { - switch value := v.(type) { - case int64: - return value, true - case uint64: - if value <= uint64(math.MaxInt64) { - return int64(value), true + case float32: + if value < float32(math.MinInt64) { + return math.MinInt64, nil } - return math.MaxInt64, true + if value > float32(math.MaxInt64) { + return math.MaxInt64, nil + } + return int64(math.Round(float64(value))), nil case float64: if value < float64(math.MinInt64) { - return math.MinInt64, true - } else if value > float64(math.MaxInt64) { - return math.MaxInt64, true - } else { - return int64(math.Round(value)), true + return math.MinInt64, nil } - case bool: - if value { - return 1, true + if value > float64(math.MaxInt64) { + return math.MaxInt64, nil + } + return int64(math.Round(value)), nil + default: + if v, err := internal.ToInt64(value); err == nil { + return v, nil } - return 0, true - case string: - result, err := strconv.ParseInt(value, 0, 64) + v, err := internal.ToFloat64(value) if err != nil { - var result float64 - var err error - - if isHexadecimal(value) { - result, err = parseHexadecimal(value) - } else { - result, err = strconv.ParseFloat(value, 64) - } - - if err != nil { - return 0, false - } - - return toInteger(result) + return 0, err } - return result, true + + if v < float64(math.MinInt64) { + return math.MinInt64, nil + } + if v > float64(math.MaxInt64) { + return math.MaxInt64, nil + } + return int64(math.Round(v)), nil } - return 0, false } -func toUnsigned(v interface{}) (uint64, bool) { +func toUnsigned(v interface{}) (uint64, error) { switch value := v.(type) { - case uint64: - return value, true - case int64: + case float32: if value < 0 { - return 0, true + return 0, nil } - return uint64(value), true + if value > float32(math.MaxUint64) { + return math.MaxUint64, nil + } + return uint64(math.Round(float64(value))), nil case float64: - if value < 0.0 { - return 0, true - } else if value > float64(math.MaxUint64) { - return math.MaxUint64, true - } else { - return uint64(math.Round(value)), true + if value < 0 { + return 0, nil } - case bool: - if value { - return 1, true + if value > float64(math.MaxUint64) { + return math.MaxUint64, nil + } + return uint64(math.Round(value)), nil + default: + if v, err := internal.ToUint64(value); err == nil { + return v, nil } - return 0, true - case string: - result, err := strconv.ParseUint(value, 0, 64) + v, err := internal.ToFloat64(value) if err != nil { - var result float64 - var err error - - if isHexadecimal(value) { - result, err = parseHexadecimal(value) - } else { - result, err = strconv.ParseFloat(value, 64) - } - - if err != nil { - return 0, false - } - - return toUnsigned(result) - } - return result, true - } - return 0, false -} - -func toFloat(v interface{}) (float64, bool) { - switch value := v.(type) { - case int64: - return float64(value), true - case uint64: - return float64(value), true - case float64: - return value, true - case bool: - if value { - return 1.0, true - } - return 0.0, true - case string: - if isHexadecimal(value) { - result, err := parseHexadecimal(value) - return result, err == nil + return 0, err } - result, err := strconv.ParseFloat(value, 64) - return result, err == nil + if v < 0 { + return 0, nil + } + if v > float64(math.MaxUint64) { + return math.MaxUint64, nil + } + return uint64(math.Round(v)), nil } - return 0.0, false } -func toString(v interface{}) (string, bool) { - switch value := v.(type) { - case int64: - return strconv.FormatInt(value, 10), true - case uint64: - return strconv.FormatUint(value, 10), true - case float64: - return strconv.FormatFloat(value, 'f', -1, 64), true - case bool: - return strconv.FormatBool(value), true - case string: - return value, true +func toFloat(v interface{}) (float64, error) { + if v, ok := v.(string); ok && strings.HasPrefix(v, "0x") { + var i big.Int + if _, success := i.SetString(v, 0); !success { + return 0, errors.New("unable to parse string to big int") + } + + var f big.Float + f.SetInt(&i) + result, _ := f.Float64() + + return result, nil } - return "", false -} - -func parseHexadecimal(value string) (float64, error) { - i := new(big.Int) - - _, success := i.SetString(value, 0) - if !success { - return 0, errors.New("unable to parse string to big int") - } - - f := new(big.Float).SetInt(i) - result, _ := f.Float64() - - return result, nil -} - -func isHexadecimal(value string) bool { - return len(value) >= 3 && strings.ToLower(value)[1] == 'x' + return internal.ToFloat64(v) } func init() {