feat(processors.converter): Add support for base64 encoded IEEE floats (#16214)

This commit is contained in:
Veprim Krasnici 2025-02-04 20:13:36 +00:00 committed by GitHub
parent ef098557ba
commit b58c6a4416
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 101 additions and 19 deletions

View File

@ -1,12 +1,12 @@
# Converter Processor Plugin # Converter Processor Plugin
The converter processor is used to change the type of tag or field values. In The converter processor is used to change the type of tag or field values. In
addition to changing field types it can convert between fields and tags. addition to changing field types it can convert between fields and tags.
Values that cannot be converted are dropped. Values that cannot be converted are dropped.
**Note:** When converting tags to fields, take care to ensure the series is **Note:** When converting tags to fields, take care to ensure the series is
still uniquely identifiable. Fields with the same series key (measurement + still uniquely identifiable. Fields with the same series key (measurement +
tags) will overwrite one another. tags) will overwrite one another.
**Note on large strings being converted to numeric types:** When converting a **Note on large strings being converted to numeric types:** When converting a
@ -67,6 +67,11 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
boolean = [] boolean = []
float = [] float = []
## Optional field to use for converting base64 encoding of IEEE 754 Float32 values
## i.e. data_json_content_state_openconfig-platform-psu:output-power":"RKeAAA=="
## into a float32 value 1340
# base64_ieee_float32 = []
## Optional field to use as metric timestamp ## Optional field to use as metric timestamp
# timestamp = [] # timestamp = []

View File

@ -3,9 +3,12 @@ package converter
import ( import (
_ "embed" _ "embed"
"encoding/base64"
"errors" "errors"
"fmt"
"math" "math"
"math/big" "math/big"
"strconv"
"strings" "strings"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
@ -18,15 +21,16 @@ import (
var sampleConfig string var sampleConfig string
type Conversion struct { type Conversion struct {
Measurement []string `toml:"measurement"` Measurement []string `toml:"measurement"`
Tag []string `toml:"tag"` Tag []string `toml:"tag"`
String []string `toml:"string"` String []string `toml:"string"`
Integer []string `toml:"integer"` Integer []string `toml:"integer"`
Unsigned []string `toml:"unsigned"` Unsigned []string `toml:"unsigned"`
Boolean []string `toml:"boolean"` Boolean []string `toml:"boolean"`
Float []string `toml:"float"` Float []string `toml:"float"`
Timestamp []string `toml:"timestamp"` Timestamp []string `toml:"timestamp"`
TimestampFormat string `toml:"timestamp_format"` TimestampFormat string `toml:"timestamp_format"`
Base64IEEEFloat32 []string `toml:"base64_ieee_float32"`
} }
type Converter struct { type Converter struct {
@ -39,14 +43,15 @@ type Converter struct {
} }
type ConversionFilter struct { type ConversionFilter struct {
Measurement filter.Filter Measurement filter.Filter
Tag filter.Filter Tag filter.Filter
String filter.Filter String filter.Filter
Integer filter.Filter Integer filter.Filter
Unsigned filter.Filter Unsigned filter.Filter
Boolean filter.Filter Boolean filter.Filter
Float filter.Filter Float filter.Filter
Timestamp filter.Filter Timestamp filter.Filter
Base64IEEEFloat32 filter.Filter
} }
func (*Converter) SampleConfig() string { func (*Converter) SampleConfig() string {
@ -132,6 +137,11 @@ func compileFilter(conv *Conversion) (*ConversionFilter, error) {
return nil, err return nil, err
} }
cf.Base64IEEEFloat32, err = filter.Compile(conv.Base64IEEEFloat32)
if err != nil {
return nil, err
}
return cf, nil return cf, nil
} }
@ -249,6 +259,14 @@ func (p *Converter) convertFields(metric telegraf.Metric) {
metric.SetTime(time) metric.SetTime(time)
metric.RemoveField(key) metric.RemoveField(key)
} }
case p.fieldConversions.Base64IEEEFloat32 != nil && p.fieldConversions.Base64IEEEFloat32.Match(key):
if v, err := base64ToFloat32(value.(string)); err != nil {
p.Log.Errorf("Converting to base64_ieee_float32 [%T] failed: %v", value, err)
metric.RemoveField(key)
} else {
metric.AddField(key, v)
}
} }
} }
} }
@ -345,6 +363,32 @@ func toFloat(v interface{}) (float64, error) {
return internal.ToFloat64(v) return internal.ToFloat64(v)
} }
func base64ToFloat32(encoded string) (float32, error) {
// Decode the Base64 string to bytes
decodedBytes, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return 0, err
}
// Check if the byte length matches a float32 (4 bytes)
if len(decodedBytes) != 4 {
return 0, errors.New("decoded byte length is not 4 bytes")
}
// Convert the bytes to a string representation as per IEEE 754 of the bits
bitsStrRepresentation := fmt.Sprintf("%08b%08b%08b%08b", decodedBytes[0], decodedBytes[1], decodedBytes[2], decodedBytes[3])
// Convert the bits to a uint32
bits, err := strconv.ParseUint(bitsStrRepresentation, 2, 32)
if err != nil {
return 0, err
}
// Convert the uint32 (bits) to a float32 based on IEEE 754 binary representation
return math.Float32frombits(uint32(bits)), nil
}
func init() { func init() {
processors.Add("converter", func() telegraf.Processor { processors.Add("converter", func() telegraf.Processor {
return &Converter{} return &Converter{}

View File

@ -770,6 +770,34 @@ func TestMeasurement(t *testing.T) {
), ),
}, },
}, },
{
name: "float32 from ieee754 float32 encoded as base64",
converter: &Converter{
Fields: &Conversion{
Base64IEEEFloat32: []string{"a", "b"},
},
},
input: testutil.MustMetric(
"cpu",
map[string]string{},
map[string]interface{}{
"a": "QlAAAA==",
"b": "QlgAAA==",
},
time.Unix(0, 0),
),
expected: []telegraf.Metric{
testutil.MustMetric(
"cpu",
map[string]string{},
map[string]interface{}{
"a": float32(52),
"b": float32(54),
},
time.Unix(0, 0),
),
},
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View File

@ -35,6 +35,11 @@
boolean = [] boolean = []
float = [] float = []
## Optional field to use for converting base64 encoding of IEEE 754 Float32 values
## i.e. data_json_content_state_openconfig-platform-psu:output-power":"RKeAAA=="
## into a float32 value 1340
# base64_ieee_float32 = []
## Optional field to use as metric timestamp ## Optional field to use as metric timestamp
# timestamp = [] # timestamp = []