1
0
Fork 0
telegraf/plugins/serializers/binary/entry.go

134 lines
3.4 KiB
Go
Raw Permalink Normal View History

package binary
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"strings"
"time"
)
type converterFunc func(value interface{}, order binary.ByteOrder) ([]byte, error)
type Entry struct {
ReadFrom string `toml:"read_from"` // field, tag, time, name
Name string `toml:"name"` // name of entry
DataFormat string `toml:"data_format"` // int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, string
StringTerminator string `toml:"string_terminator"` // for string metrics: null, 0x00, 00, ....
StringLength uint64 `toml:"string_length"` // for string only, target size
TimeFormat string `toml:"time_format"` // for time metrics: unix, unix_ms, unix_us, unix_ns
converter converterFunc
termination byte
}
func (e *Entry) fillDefaults() error {
// Normalize
e.ReadFrom = strings.ToLower(e.ReadFrom)
// Check input constraints
switch e.ReadFrom {
case "":
e.ReadFrom = "field"
fallthrough
case "field", "tag":
if e.Name == "" {
return errors.New("missing name")
}
case "time":
switch e.TimeFormat {
case "":
e.TimeFormat = "unix"
case "unix", "unix_ms", "unix_us", "unix_ns":
default:
return errors.New("invalid time format")
}
case "name":
if e.DataFormat == "" {
e.DataFormat = "string"
} else if e.DataFormat != "string" {
return errors.New("name data format has to be string")
}
default:
return fmt.Errorf("unknown assignment %q", e.ReadFrom)
}
// Check data format
switch e.DataFormat {
case "":
return errors.New("missing data format")
case "float64":
e.converter = convertToFloat64
case "float32":
e.converter = convertToFloat32
case "uint64":
e.converter = convertToUint64
case "uint32":
e.converter = convertToUint32
case "uint16":
e.converter = convertToUint16
case "uint8":
e.converter = convertToUint8
case "int64":
e.converter = convertToInt64
case "int32":
e.converter = convertToInt32
case "int16":
e.converter = convertToInt16
case "int8":
e.converter = convertToInt8
case "string":
switch e.StringTerminator {
case "", "null":
e.termination = 0x00
default:
e.StringTerminator = strings.TrimPrefix(e.StringTerminator, "0x")
termination, err := hex.DecodeString(e.StringTerminator)
if err != nil {
return fmt.Errorf("decoding terminator failed for %q: %w", e.Name, err)
}
if len(termination) != 1 {
return fmt.Errorf("terminator must be a single byte, got %q", e.StringTerminator)
}
e.termination = termination[0]
}
if e.StringLength < 1 {
return errors.New("string length must be at least 1")
}
e.converter = e.convertToString
default:
return fmt.Errorf("invalid data format %q for field %q", e.ReadFrom, e.DataFormat)
}
return nil
}
func (e *Entry) serializeValue(value interface{}, order binary.ByteOrder) ([]byte, error) {
// Handle normal fields, tags, etc
if e.ReadFrom != "time" {
return e.converter(value, order)
}
// We need to serialize the time, make sure we actually do get a time and
// convert it to the correct timestamp (with scale) first.
t, ok := value.(time.Time)
if !ok {
return nil, fmt.Errorf("time expected but got %T", value)
}
var timestamp int64
switch e.TimeFormat {
case "unix":
timestamp = t.Unix()
case "unix_ms":
timestamp = t.UnixMilli()
case "unix_us":
timestamp = t.UnixMicro()
case "unix_ns":
timestamp = t.UnixNano()
}
return e.converter(timestamp, order)
}