forked from influxdata/telegraf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathentry.go
133 lines (121 loc) · 3.44 KB
/
entry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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 = e.convertToFloat64
case "float32":
e.converter = e.convertToFloat32
case "uint64":
e.converter = e.convertToUint64
case "uint32":
e.converter = e.convertToUint32
case "uint16":
e.converter = e.convertToUint16
case "uint8":
e.converter = e.convertToUint8
case "int64":
e.converter = e.convertToInt64
case "int32":
e.converter = e.convertToInt32
case "int16":
e.converter = e.convertToInt16
case "int8":
e.converter = e.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)
}