Skip to content

Commit

Permalink
Add resource support to dogstatsd exporter (#13)
Browse files Browse the repository at this point in the history
* Add resource support to dogstatsd

* Mod tidy
  • Loading branch information
jmacd authored Apr 16, 2020
1 parent 4ef49a2 commit da45c10
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 32 deletions.
17 changes: 13 additions & 4 deletions exporters/metric/dogstatsd/dogstatsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ import (
"github.com/open-telemetry/opentelemetry-go-contrib/exporters/metric/dogstatsd/internal/statsd"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/api/metric"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/array"
"go.opentelemetry.io/otel/sdk/metric/aggregator/lastvalue"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"

export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/metric/batcher/ungrouped"
"go.opentelemetry.io/otel/sdk/metric/controller/push"
"go.opentelemetry.io/otel/sdk/resource"
)

type (
Expand All @@ -44,7 +44,7 @@ type (
Exporter struct {
*statsd.Exporter

labelEncoder *statsd.LabelEncoder
labelEncoder *LabelEncoder
}
)

Expand All @@ -54,8 +54,17 @@ var (

// NewRawExporter returns a new Dogstatsd-syntax exporter for use in a pipeline.
func NewRawExporter(config Config) (*Exporter, error) {
// TODO: Remove the resource value set from the Config here when
// https://github.com/open-telemetry/opentelemetry-go/pull/640
// and 641 are released. The resources will be received on
// the first call to Export().
res := config.Resource
if res == nil {
res = resource.New()
}

exp := &Exporter{
labelEncoder: statsd.NewLabelEncoder(),
labelEncoder: NewLabelEncoder(res),
}

var err error
Expand Down
6 changes: 3 additions & 3 deletions exporters/metric/dogstatsd/dogstatsd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ import (
"testing"

"github.com/open-telemetry/opentelemetry-go-contrib/exporters/metric/dogstatsd"
"github.com/open-telemetry/opentelemetry-go-contrib/exporters/metric/dogstatsd/internal/statsd"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporters/metric/test"
"go.opentelemetry.io/otel/sdk/metric/aggregator/sum"
"go.opentelemetry.io/otel/sdk/resource"
)

// TestDogstatsLabels that labels are formatted in the correct style,
// whether or not the provided labels were encoded by a statsd label
// encoder.
func TestDogstatsLabels(t *testing.T) {
encoder := statsd.NewLabelEncoder()
encoder := dogstatsd.NewLabelEncoder(resource.New(key.String("R", "S")))
ctx := context.Background()
checkpointSet := test.NewCheckpointSet(encoder)

Expand All @@ -54,5 +54,5 @@ func TestDogstatsLabels(t *testing.T) {
err = exp.Export(ctx, checkpointSet)
require.Nil(t, err)

require.Equal(t, "test.name:123|c|#A:B\n", buf.String())
require.Equal(t, "test.name:123|c|#R:S,A:B\n", buf.String())
}
7 changes: 7 additions & 0 deletions exporters/metric/dogstatsd/internal/statsd/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"go.opentelemetry.io/otel/api/unit"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator"
"go.opentelemetry.io/otel/sdk/resource"
)

type (
Expand All @@ -50,6 +51,12 @@ type (
// MaxPacketSize this limits the packet size for packet-oriented transports.
MaxPacketSize int

// Resource WILL BE REMOVED AFTER RESOURCES ARE ADDED
// TO THE EXPORT APIs. TODO: Remove this when Export()
// passes the Resource. See:
// https://github.com/open-telemetry/opentelemetry-go/pull/640
Resource *resource.Resource

// TODO support Dial and Write timeouts
}

Expand Down
16 changes: 9 additions & 7 deletions exporters/metric/dogstatsd/internal/statsd/conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (

// withTagsAdapter tests a dogstatsd-style statsd exporter.
type withTagsAdapter struct {
*statsd.LabelEncoder
export.LabelEncoder
}

func (*withTagsAdapter) AppendName(rec export.Record, buf *bytes.Buffer) {
Expand All @@ -44,12 +44,14 @@ func (*withTagsAdapter) AppendName(rec export.Record, buf *bytes.Buffer) {

func (ta *withTagsAdapter) AppendTags(rec export.Record, buf *bytes.Buffer) {
encoded := rec.Labels().Encoded(ta.LabelEncoder)
_, _ = buf.WriteString("++")
_, _ = buf.WriteString(encoded)
}

func newWithTagsAdapter() *withTagsAdapter {
return &withTagsAdapter{
statsd.NewLabelEncoder(),
// Note: This uses non-statsd syntax. (No problem.)
export.NewDefaultLabelEncoder(),
}
}

Expand Down Expand Up @@ -93,10 +95,10 @@ func TestBasicFormat(t *testing.T) {

for _, ao := range []adapterOutput{{
adapter: newWithTagsAdapter(),
expected: `counter:%s|c|#A:B,C:D
observer:%s|g|#A:B,C:D
measure:%s|h|#A:B,C:D
timer:%s|ms|#A:B,C:D
expected: `counter:%s|c++A=B,C=D
observer:%s|g++A=B,C=D
measure:%s|h++A=B,C=D
timer:%s|ms++A=B,C=D
`}, {
adapter: newNoTagsAdapter(),
expected: `counter.B.D:%s|c
Expand Down Expand Up @@ -297,7 +299,7 @@ func TestPacketSplit(t *testing.T) {
offset += nkeys
iter := export.LabelSlice(labels).Iter()
encoded := adapter.LabelEncoder.Encode(iter)
expect := fmt.Sprint("counter:100|c", encoded, "\n")
expect := fmt.Sprint("counter:100|c++", encoded, "\n")
expected = append(expected, expect)
checkpointSet.AddCounter(&desc, 100, labels...)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package statsd
package dogstatsd

import (
"bytes"
"sort"
"sync"

"go.opentelemetry.io/otel/api/core"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/resource"
)

// LabelEncoder encodes metric labels in the dogstatsd syntax.
Expand All @@ -28,21 +31,28 @@ import (
//
// https://github.com/stripe/veneur/blob/master/sinks/datadog/datadog.go
type LabelEncoder struct {
pool sync.Pool
pool sync.Pool
resources []core.KeyValue
}

var _ export.LabelEncoder = &LabelEncoder{}
var leID = export.NewLabelEncoderID()

// NewLabelEncoder returns a new encoder for dogstatsd-syntax metric
// labels.
func NewLabelEncoder() *LabelEncoder {
func NewLabelEncoder(resource *resource.Resource) *LabelEncoder {
attrs := resource.Attributes()
sort.Slice(attrs[:], func(i, j int) bool {
return attrs[i].Key < attrs[j].Key
})

return &LabelEncoder{
pool: sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
},
},
resources: attrs,
}
}

Expand All @@ -52,19 +62,26 @@ func (e *LabelEncoder) Encode(iter export.LabelIterator) string {
defer e.pool.Put(buf)
buf.Reset()

delimiter := "|#"

for _, kv := range e.resources {
e.encodeOne(buf, kv)
}
for iter.Next() {
kv := iter.Label()
_, _ = buf.WriteString(delimiter)
_, _ = buf.WriteString(string(kv.Key))
_, _ = buf.WriteRune(':')
_, _ = buf.WriteString(kv.Value.Emit())
delimiter = ","
e.encodeOne(buf, iter.Label())
}
return buf.String()
}

func (e *LabelEncoder) encodeOne(buf *bytes.Buffer, kv core.KeyValue) {
if buf.Len() == 0 {
_, _ = buf.WriteString("|#")
} else {
_, _ = buf.WriteRune(',')
}
_, _ = buf.WriteString(string(kv.Key))
_, _ = buf.WriteRune(':')
_, _ = buf.WriteString(kv.Value.Emit())
}

func (*LabelEncoder) ID() int64 {
return leID
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,46 @@
// See the License for the specific language governing permissions and
// limitations under the License.

package statsd_test
package dogstatsd_test

import (
"testing"

"github.com/open-telemetry/opentelemetry-go-contrib/exporters/metric/dogstatsd/internal/statsd"
"github.com/open-telemetry/opentelemetry-go-contrib/exporters/metric/dogstatsd"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key"
export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/resource"
)

var testLabels = []core.KeyValue{
key.New("A").String("B"),
key.New("C").String("D"),
key.New("E").Float64(1.5),
key.String("A", "B"),
key.String("C", "D"),
key.Float64("E", 1.5),
}

var testResources = []core.KeyValue{
key.String("R1", "V1"),
key.String("R2", "V2"),
}

func TestLabelSyntax(t *testing.T) {
encoder := statsd.NewLabelEncoder()
encoder := dogstatsd.NewLabelEncoder(resource.New())

require.Equal(t, `|#A:B,C:D,E:1.5`, encoder.Encode(export.LabelSlice(testLabels).Iter()))

kvs := []core.KeyValue{
key.New("A").String("B"),
key.String("A", "B"),
}
require.Equal(t, `|#A:B`, encoder.Encode(export.LabelSlice(kvs).Iter()))

require.Equal(t, "", encoder.Encode(export.LabelSlice(nil).Iter()))
}

func TestLabelResources(t *testing.T) {
encoder := dogstatsd.NewLabelEncoder(resource.New(testResources...))

require.Equal(t, `|#R1:V1,R2:V2,A:B,C:D,E:1.5`, encoder.Encode(export.LabelSlice(testLabels).Iter()))
}

0 comments on commit da45c10

Please sign in to comment.