diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..6242fcc --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,14 @@ +# Golang CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-go/ for more details +version: 2 +jobs: + build: + docker: + - image: circleci/golang:1.10-stretch + + working_directory: /go/src/github.com/DCSO/fluxline + steps: + - checkout + - run: go get -v -t -d ./... + - run: go test -v ./... diff --git a/README.md b/README.md index 956dab9..c0af39c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# fluxline +# fluxline [![CircleCI](https://circleci.com/gh/DCSO/fluxline.svg?style=shield)](https://circleci.com/gh/DCSO/fluxline) [![Documentation](https://godoc.org/github.com/DCSO/fluxline?status.svg)](http://godoc.org/github.com/DCSO/fluxline) Encoder for Golang to prepare sets of metrics in [InfluxDB's Line Protocol](https://docs.influxdata.com/influxdb/v1.4/write_protocols/line_protocol_reference) format. As input, we use structs annotated with the `influx` tag, similar to how `encoding/json` works. diff --git a/encoder.go b/encoder.go index 82cdf26..2a55daa 100644 --- a/encoder.go +++ b/encoder.go @@ -28,16 +28,17 @@ return str } -func toInfluxRepr(tag string, val interface{}) (string, error) { +func toInfluxRepr(tag string, val interface{}, nostatictypes bool) (string, error) { switch v := val.(type) { case string: if len(v) > 64000 { return "", fmt.Errorf("%s: string too long (%d characters, max. 64K)", tag, len(v)) } return fmt.Sprintf("%q", v), nil - case int32, int64, int16, int8, int: - return fmt.Sprintf("%di", v), nil - case uint32, uint64, uint16, uint8, uint: + case int32, int64, int16, int8, int, uint32, uint64, uint16, uint8, uint: + if nostatictypes { + return fmt.Sprintf("%d", v), nil + } return fmt.Sprintf("%di", v), nil case float64, float32: return fmt.Sprintf("%g", v), nil @@ -51,7 +52,7 @@ } func recordFields(val interface{}, - fieldSet map[string]string) (map[string]string, error) { + fieldSet map[string]string, nostatictypes bool) (map[string]string, error) { t := reflect.TypeOf(val) v := reflect.ValueOf(val) @@ -61,7 +62,7 @@ if tag == "" { continue } - repr, err := toInfluxRepr(tag, v.Field(i).Interface()) + repr, err := toInfluxRepr(tag, v.Field(i).Interface(), nostatictypes) if err != nil { return nil, err } @@ -116,15 +117,30 @@ // Encode writes the line protocol representation for a given measurement // name, data struct and tag map to the io.Writer specified on encoder creation. -func (a *Encoder) Encode(prefix string, val interface{}, - tags map[string]string) error { +func (a *Encoder) encodeGeneric(prefix string, val interface{}, + tags map[string]string, nostatictypes bool) error { fieldSet := make(map[string]string) - fieldSet, err := recordFields(val, fieldSet) + fieldSet, err := recordFields(val, fieldSet, nostatictypes) if err != nil { return err } _, err = a.Writer.Write([]byte(a.formatLineProtocol(prefix, tags, fieldSet))) return err +} + +// Encode writes the line protocol representation for a given measurement +// name, data struct and tag map to the io.Writer specified on encoder creation. +func (a *Encoder) Encode(prefix string, val interface{}, + tags map[string]string) error { + return a.encodeGeneric(prefix, val, tags, false) +} + +// EncodeWithoutTypes writes the line protocol representation for a given measurement +// name, data struct and tag map to the io.Writer specified on encoder creation. +// In contrast to Encode(), this method never appends type suffixes to values. +func (a *Encoder) EncodeWithoutTypes(prefix string, val interface{}, + tags map[string]string) error { + return a.encodeGeneric(prefix, val, tags, true) } // EncodeMap writes the line protocol representation for a given measurement diff --git a/encoder_test.go b/encoder_test.go index d4824a4..a8ffe7c 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -102,6 +102,22 @@ } } +func TestEncoderNoTypes(t *testing.T) { + var b bytes.Buffer + + ile := NewEncoder(&b) + tags := make(map[string]string) + err := ile.EncodeWithoutTypes("mytool", testStruct, tags) + if err != nil { + t.Fatal(err) + } + out := b.String() + + if match, _ := regexp.Match(`^mytool,host=[^,]+ testval=1,testvalue=2,testvalue2=-3,testvalue3=\"foobar\\\"baz\",testvaluebool=false,testvalueflt32=3.1415927,testvalueflt64=1.29e-24,testvaluetime=`, []byte(out)); !match { + t.Fatalf("unexpected match content: %s", out) + } +} + func TestEncoderStringTooLongFail(t *testing.T) { var b bytes.Buffer