Add line protocol uint64 support (#3946)

This commit is contained in:
Matt 2018-03-28 19:43:25 -04:00 committed by Daniel Nelson
parent ef112e6ee7
commit a320f91516
12 changed files with 8046 additions and 6754 deletions

View File

@ -4,6 +4,7 @@ BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
COMMIT := $(shell git rev-parse --short HEAD) COMMIT := $(shell git rev-parse --short HEAD)
GOFILES ?= $(shell git ls-files '*.go') GOFILES ?= $(shell git ls-files '*.go')
GOFMT ?= $(shell gofmt -l $(filter-out plugins/parsers/influx/machine.go, $(GOFILES))) GOFMT ?= $(shell gofmt -l $(filter-out plugins/parsers/influx/machine.go, $(GOFILES)))
BUILDFLAGS ?=
ifdef GOBIN ifdef GOBIN
PATH := $(GOBIN):$(PATH) PATH := $(GOBIN):$(PATH)
@ -35,7 +36,7 @@ deps:
gdm restore gdm restore
telegraf: telegraf:
go build -i -o $(TELEGRAF) -ldflags "$(LDFLAGS)" ./cmd/telegraf/telegraf.go go build -i -o $(TELEGRAF) -ldflags "$(LDFLAGS)" $(BUILDFLAGS) ./cmd/telegraf/telegraf.go
go-install: go-install:
go install -ldflags "-w -s $(LDFLAGS)" ./cmd/telegraf go install -ldflags "-w -s $(LDFLAGS)" ./cmd/telegraf
@ -61,6 +62,9 @@ fmtcheck:
fi fi
@echo '[INFO] done.' @echo '[INFO] done.'
uint64:
BUILDFLAGS="-tags uint64" $(MAKE) all
lint: lint:
golint ./... golint ./...
@ -99,4 +103,4 @@ docker-image:
plugins/parsers/influx/machine.go: plugins/parsers/influx/machine.go.rl plugins/parsers/influx/machine.go: plugins/parsers/influx/machine.go.rl
ragel -Z -G2 $^ -o $@ ragel -Z -G2 $^ -o $@
.PHONY: deps telegraf install test test-windows lint vet test-all package clean docker-image fmtcheck .PHONY: deps telegraf install test test-windows lint vet test-all package clean docker-image fmtcheck uint64

View File

@ -11,6 +11,16 @@ import (
const MaxInt = int(^uint(0) >> 1) const MaxInt = int(^uint(0) >> 1)
// enableUint64Support will enable uint64 support if set to true.
var enableUint64Support = false
// EnableUintSupport manually enables uint support for convertValue.
// This function will be removed in the future and only exists for unit tests during the
// transition.
func EnableUintSupport() {
enableUint64Support = true
}
type metric struct { type metric struct {
name string name string
tags []*telegraf.Tag tags []*telegraf.Tag
@ -265,11 +275,14 @@ func convertField(v interface{}) interface{} {
return int64(MaxInt) return int64(MaxInt)
} }
case uint64: case uint64:
if enableUint64Support == false {
if v <= uint64(MaxInt) { if v <= uint64(MaxInt) {
return int64(v) return int64(v)
} else { } else {
return int64(MaxInt) return int64(MaxInt)
} }
}
return uint64(v)
case []byte: case []byte:
return string(v) return string(v)
case int32: case int32:

7
metric/uint_support.go Normal file
View File

@ -0,0 +1,7 @@
// +build uint64
package metric
func init() {
EnableUintSupport()
}

View File

@ -63,6 +63,12 @@ func parseIntBytes(b []byte, base int, bitSize int) (i int64, err error) {
return strconv.ParseInt(s, base, bitSize) return strconv.ParseInt(s, base, bitSize)
} }
// parseUintBytes is a zero-alloc wrapper around strconv.ParseUint.
func parseUintBytes(b []byte, base int, bitSize int) (i uint64, err error) {
s := unsafeBytesToString(b)
return strconv.ParseUint(s, base, bitSize)
}
// parseFloatBytes is a zero-alloc wrapper around strconv.ParseFloat. // parseFloatBytes is a zero-alloc wrapper around strconv.ParseFloat.
func parseFloatBytes(b []byte, bitSize int) (float64, error) { func parseFloatBytes(b []byte, bitSize int) (float64, error) {
s := unsafeBytesToString(b) s := unsafeBytesToString(b)

View File

@ -54,6 +54,16 @@ func (h *MetricHandler) AddInt(key []byte, value []byte) {
h.builder.AddField(fk, fv) h.builder.AddField(fk, fv)
} }
func (h *MetricHandler) AddUint(key []byte, value []byte) {
fk := unescape(key)
fv, err := parseUintBytes(bytes.TrimSuffix(value, []byte("u")), 10, 64)
if err != nil {
log.Errorf("E! Received unparseable uint value: %q", value)
return
}
h.builder.AddField(fk, fv)
}
func (h *MetricHandler) AddFloat(key []byte, value []byte) { func (h *MetricHandler) AddFloat(key []byte, value []byte) {
fk := unescape(key) fk := unescape(key)
fv, err := parseFloatBytes(value, 64) fv, err := parseFloatBytes(value, 64)

File diff suppressed because it is too large Load Diff

View File

@ -89,6 +89,10 @@ action integer {
m.handler.AddInt(key, m.text()) m.handler.AddInt(key, m.text())
} }
action unsigned {
m.handler.AddUint(key, m.text())
}
action float { action float {
m.handler.AddFloat(key, m.text()) m.handler.AddFloat(key, m.text())
} }
@ -114,6 +118,9 @@ non_zero_digit =
integer = integer =
'-'? ( digit | ( non_zero_digit digit* ) ); '-'? ( digit | ( non_zero_digit digit* ) );
unsigned =
( digit | ( non_zero_digit digit* ) );
number = number =
( integer ( '.' digit* )? ) | ( '.' digit* ); ( integer ( '.' digit* )? ) | ( '.' digit* );
@ -135,6 +142,9 @@ fieldfloat =
fieldinteger = fieldinteger =
(integer 'i') >begin %integer; (integer 'i') >begin %integer;
fieldunsigned =
(unsigned 'u') >begin %unsigned;
false = false =
"false" | "FALSE" | "False" | "F" | "f"; "false" | "FALSE" | "False" | "F" | "f";
@ -153,7 +163,7 @@ fieldstring =
fieldstringquoted = fieldstringquoted =
'"' fieldstring '"'; '"' fieldstring '"';
fieldvalue = fieldinteger | fieldfloat | fieldstringquoted | fieldbool; fieldvalue = fieldinteger | fieldunsigned | fieldfloat | fieldstringquoted | fieldbool;
field = field =
fieldkey '=' fieldvalue; fieldkey '=' fieldvalue;

View File

@ -43,6 +43,18 @@ func (h *TestingHandler) AddInt(key []byte, value []byte) {
h.results = append(h.results, fieldkey, fieldvalue) h.results = append(h.results, fieldkey, fieldvalue)
} }
func (h *TestingHandler) AddUint(key []byte, value []byte) {
fieldkey := Result{
Name: FieldKey,
Value: key,
}
fieldvalue := Result{
Name: FieldUint,
Value: value,
}
h.results = append(h.results, fieldkey, fieldvalue)
}
func (h *TestingHandler) AddFloat(key []byte, value []byte) { func (h *TestingHandler) AddFloat(key []byte, value []byte) {
fieldkey := Result{ fieldkey := Result{
Name: FieldKey, Name: FieldKey,
@ -113,6 +125,9 @@ func (h *BenchmarkingHandler) AddTag(key []byte, value []byte) {
func (h *BenchmarkingHandler) AddInt(key []byte, value []byte) { func (h *BenchmarkingHandler) AddInt(key []byte, value []byte) {
} }
func (h *BenchmarkingHandler) AddUint(key []byte, value []byte) {
}
func (h *BenchmarkingHandler) AddFloat(key []byte, value []byte) { func (h *BenchmarkingHandler) AddFloat(key []byte, value []byte) {
} }

View File

@ -19,6 +19,7 @@ type Handler interface {
SetMeasurement(name []byte) SetMeasurement(name []byte)
AddTag(key []byte, value []byte) AddTag(key []byte, value []byte)
AddInt(key []byte, value []byte) AddInt(key []byte, value []byte)
AddUint(key []byte, value []byte)
AddFloat(key []byte, value []byte) AddFloat(key []byte, value []byte)
AddString(key []byte, value []byte) AddString(key []byte, value []byte)
AddBool(key []byte, value []byte) AddBool(key []byte, value []byte)

View File

@ -16,6 +16,12 @@ func Metric(v telegraf.Metric, err error) telegraf.Metric {
return v return v
} }
const (
Uint64Overflow uint64 = 9223372036854775808
Uint64Max uint64 = 18446744073709551615
Uint64Test uint64 = 42
)
var DefaultTime = func() time.Time { var DefaultTime = func() time.Time {
return time.Unix(42, 0) return time.Unix(42, 0)
} }
@ -256,6 +262,57 @@ var ptests = []struct {
}, },
err: nil, err: nil,
}, },
{
name: "field uint",
input: []byte("cpu value=42u"),
metrics: []telegraf.Metric{
Metric(
metric.New(
"cpu",
map[string]string{},
map[string]interface{}{
"value": Uint64Test,
},
time.Unix(42, 0),
),
),
},
err: nil,
},
{
name: "field uint int overflow",
input: []byte("cpu value=9223372036854775808u"),
metrics: []telegraf.Metric{
Metric(
metric.New(
"cpu",
map[string]string{},
map[string]interface{}{
"value": Uint64Overflow,
},
time.Unix(42, 0),
),
),
},
err: nil,
},
{
name: "field uint maximum",
input: []byte("cpu value=18446744073709551615u"),
metrics: []telegraf.Metric{
Metric(
metric.New(
"cpu",
map[string]string{},
map[string]interface{}{
"value": Uint64Max,
},
time.Unix(42, 0),
),
),
},
err: nil,
},
{ {
name: "field boolean", name: "field boolean",
input: []byte("cpu value=true"), input: []byte("cpu value=true"),

View File

@ -237,6 +237,8 @@ func (s *Serializer) writeMetric(w io.Writer, m telegraf.Metric) error {
func appendFieldValue(buf []byte, value interface{}) ([]byte, error) { func appendFieldValue(buf []byte, value interface{}) ([]byte, error) {
switch v := value.(type) { switch v := value.(type) {
case uint64:
return appendUintField(buf, v), nil
case int64: case int64:
return appendIntField(buf, v), nil return appendIntField(buf, v), nil
case float64: case float64:
@ -257,6 +259,10 @@ func appendFieldValue(buf []byte, value interface{}) ([]byte, error) {
return buf, ErrInvalidFieldType return buf, ErrInvalidFieldType
} }
func appendUintField(buf []byte, value uint64) []byte {
return append(strconv.AppendUint(buf, value, 10), 'u')
}
func appendIntField(buf []byte, value int64) []byte { func appendIntField(buf []byte, value int64) []byte {
return append(strconv.AppendInt(buf, value, 10), 'i') return append(strconv.AppendInt(buf, value, 10), 'i')
} }

View File

@ -11,12 +11,20 @@ import (
) )
func MustMetric(v telegraf.Metric, err error) telegraf.Metric { func MustMetric(v telegraf.Metric, err error) telegraf.Metric {
// Force uint support to be enabled for testing.
metric.EnableUintSupport()
if err != nil { if err != nil {
panic(err) panic(err)
} }
return v return v
} }
const (
Uint64Overflow uint64 = 9223372036854775808
Uint64Max uint64 = 18446744073709551615
Uint64Test uint64 = 42
)
var tests = []struct { var tests = []struct {
name string name string
maxBytes int maxBytes int
@ -128,6 +136,48 @@ var tests = []struct {
), ),
output: []byte("cpu value=42i 0\n"), output: []byte("cpu value=42i 0\n"),
}, },
{
name: "uint field",
input: MustMetric(
metric.New(
"cpu",
map[string]string{},
map[string]interface{}{
"value": Uint64Test,
},
time.Unix(0, 0),
),
),
output: []byte("cpu value=42u 0\n"),
},
{
name: "uint field int64 overflow",
input: MustMetric(
metric.New(
"cpu",
map[string]string{},
map[string]interface{}{
"value": Uint64Overflow,
},
time.Unix(0, 0),
),
),
output: []byte("cpu value=9223372036854775808u 0\n"),
},
{
name: "uint field uint64 max",
input: MustMetric(
metric.New(
"cpu",
map[string]string{},
map[string]interface{}{
"value": Uint64Max,
},
time.Unix(0, 0),
),
),
output: []byte("cpu value=18446744073709551615u 0\n"),
},
{ {
name: "bool field", name: "bool field",
input: MustMetric( input: MustMetric(