diff --git a/exporters/metric/dogstatsd/dogstatsd.go b/exporters/metric/dogstatsd/dogstatsd.go deleted file mode 100644 index da4966f27b8..00000000000 --- a/exporters/metric/dogstatsd/dogstatsd.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dogstatsd // import "go.opentelemetry.io/otel/exporters/metric/dogstatsd" - -import ( - "bytes" - "time" - - "go.opentelemetry.io/otel/api/global" - "go.opentelemetry.io/otel/exporters/metric/internal/statsd" - - 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/metric/selector/simple" -) - -type ( - Config = statsd.Config - - // Exporter implements a dogstatsd-format statsd exporter, - // which encodes label sets as independent fields in the - // output. - // - // TODO: find a link for this syntax. It's been copied out of - // code, not a specification: - // - // https://github.com/stripe/veneur/blob/master/sinks/datadog/datadog.go - Exporter struct { - *statsd.Exporter - - labelEncoder *statsd.LabelEncoder - } -) - -var ( - _ export.Exporter = &Exporter{} -) - -// NewRawExporter returns a new Dogstatsd-syntax exporter for use in a pipeline. -func NewRawExporter(config Config) (*Exporter, error) { - exp := &Exporter{ - labelEncoder: statsd.NewLabelEncoder(), - } - - var err error - exp.Exporter, err = statsd.NewExporter(config, exp) - return exp, err -} - -// InstallNewPipeline instantiates a NewExportPipeline and registers it globally. -// Typically called as: -// -// pipeline, err := dogstatsd.InstallNewPipeline(dogstatsd.Config{...}) -// if err != nil { -// ... -// } -// defer pipeline.Stop() -// ... Done -func InstallNewPipeline(config Config) (*push.Controller, error) { - controller, err := NewExportPipeline(config, time.Minute) - if err != nil { - return controller, err - } - global.SetMeterProvider(controller) - return controller, err -} - -// NewExportPipeline sets up a complete export pipeline with the recommended setup, -// chaining a NewRawExporter into the recommended selectors and batchers. -func NewExportPipeline(config Config, period time.Duration) (*push.Controller, error) { - selector := simple.NewWithExactMeasure() - exporter, err := NewRawExporter(config) - if err != nil { - return nil, err - } - - // The ungrouped batcher ensures that the export sees the full - // set of labels as dogstatsd tags. - batcher := ungrouped.New(selector, exporter.labelEncoder, false) - - pusher := push.New(batcher, exporter, period) - pusher.Start() - - return pusher, nil -} - -// AppendName is part of the stats-internal adapter interface. -func (*Exporter) AppendName(rec export.Record, buf *bytes.Buffer) { - _, _ = buf.WriteString(rec.Descriptor().Name()) -} - -// AppendTags is part of the stats-internal adapter interface. -func (e *Exporter) AppendTags(rec export.Record, buf *bytes.Buffer) { - encoded := rec.Labels().Encoded(e.labelEncoder) - _, _ = buf.WriteString(encoded) -} diff --git a/exporters/metric/dogstatsd/dogstatsd_test.go b/exporters/metric/dogstatsd/dogstatsd_test.go deleted file mode 100644 index d79dd1fd15d..00000000000 --- a/exporters/metric/dogstatsd/dogstatsd_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dogstatsd_test - -import ( - "bytes" - "context" - "testing" - - "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/dogstatsd" - "go.opentelemetry.io/otel/exporters/metric/internal/statsd" - "go.opentelemetry.io/otel/exporters/metric/test" - "go.opentelemetry.io/otel/sdk/metric/aggregator/sum" -) - -// 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() - ctx := context.Background() - checkpointSet := test.NewCheckpointSet(encoder) - - desc := metric.NewDescriptor("test.name", metric.CounterKind, core.Int64NumberKind) - cagg := sum.New() - _ = cagg.Update(ctx, core.NewInt64Number(123), &desc) - cagg.Checkpoint(ctx, &desc) - - checkpointSet.Add(&desc, cagg, key.New("A").String("B")) - - var buf bytes.Buffer - exp, err := dogstatsd.NewRawExporter(dogstatsd.Config{ - Writer: &buf, - }) - require.Nil(t, err) - - err = exp.Export(ctx, checkpointSet) - require.Nil(t, err) - - require.Equal(t, "test.name:123|c|#A:B\n", buf.String()) -} diff --git a/exporters/metric/dogstatsd/example_test.go b/exporters/metric/dogstatsd/example_test.go deleted file mode 100644 index dfea0f801f7..00000000000 --- a/exporters/metric/dogstatsd/example_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dogstatsd_test - -import ( - "context" - "fmt" - "io" - "log" - "sync" - "time" - - "go.opentelemetry.io/otel/api/key" - "go.opentelemetry.io/otel/api/metric" - "go.opentelemetry.io/otel/exporters/metric/dogstatsd" -) - -func ExampleNew() { - // Create a "server" - wg := &sync.WaitGroup{} - wg.Add(1) - - reader, writer := io.Pipe() - - go func() { - defer wg.Done() - - for { - var buf [4096]byte - n, err := reader.Read(buf[:]) - if err == io.EOF { - return - } else if err != nil { - log.Fatal("Read err: ", err) - } else if n >= len(buf) { - log.Fatal("Read small buffer: ", n) - } else { - fmt.Print(string(buf[0:n])) - } - } - }() - - // Create a meter - pusher, err := dogstatsd.NewExportPipeline(dogstatsd.Config{ - // The Writer field provides test support. - Writer: writer, - - // In real code, use the URL field: - // - // URL: fmt.Sprint("unix://", path), - }, time.Minute) - if err != nil { - log.Fatal("Could not initialize dogstatsd exporter:", err) - } - - ctx := context.Background() - - key := key.New("key") - - // pusher implements the metric.MeterProvider interface: - meter := pusher.Meter("example") - - // Create and update a single counter: - counter := metric.Must(meter).NewInt64Counter("a.counter", metric.WithKeys(key)) - labels := meter.Labels(key.String("value")) - - counter.Add(ctx, 100, labels) - - // Flush the exporter, close the pipe, and wait for the reader. - pusher.Stop() - writer.Close() - wg.Wait() - - // Output: - // a.counter:100|c|#key:value -} diff --git a/exporters/metric/internal/statsd/conn.go b/exporters/metric/internal/statsd/conn.go deleted file mode 100644 index e08d8d2b9ee..00000000000 --- a/exporters/metric/internal/statsd/conn.go +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package statsd - -// See https://github.com/b/statsd_spec for the best-available statsd -// syntax specification. See also -// https://github.com/statsd/statsd/edit/master/docs/metric_types.md - -import ( - "bytes" - "context" - "fmt" - "io" - "net" - "net/url" - "strconv" - - "go.opentelemetry.io/otel/api/core" - "go.opentelemetry.io/otel/api/unit" - export "go.opentelemetry.io/otel/sdk/export/metric" - "go.opentelemetry.io/otel/sdk/export/metric/aggregator" -) - -type ( - // Config supports common configuration that applies to statsd exporters. - Config struct { - // URL describes the destination for exporting statsd data. - // e.g., udp://host:port - // tcp://host:port - // unix:///socket/path - URL string - - // Writer is an alternate to providing a URL. When Writer is - // non-nil, URL will be ignored and the exporter will write to - // the configured Writer interface. - Writer io.Writer - - // MaxPacketSize this limits the packet size for packet-oriented transports. - MaxPacketSize int - - // TODO support Dial and Write timeouts - } - - // Exporter is common type meant to implement concrete statsd - // exporters. - Exporter struct { - adapter Adapter - config Config - conn net.Conn - writer io.Writer - buffer bytes.Buffer - } - - // Adapter supports statsd syntax variations, primarily plain - // statsd vs. dogstatsd. - Adapter interface { - AppendName(export.Record, *bytes.Buffer) - AppendTags(export.Record, *bytes.Buffer) - } -) - -const ( - formatCounter = "c" - formatHistogram = "h" - formatGauge = "g" - formatTiming = "ms" - - MaxPacketSize = 1 << 16 -) - -var ( - _ export.Exporter = &Exporter{} - - ErrInvalidScheme = fmt.Errorf("invalid statsd transport") -) - -// NewExporter returns a common implementation for exporters that Export -// statsd syntax. -func NewExporter(config Config, adapter Adapter) (*Exporter, error) { - if config.MaxPacketSize <= 0 { - config.MaxPacketSize = MaxPacketSize - } - var writer io.Writer - var conn net.Conn - var err error - if config.Writer != nil { - writer = config.Writer - } else { - conn, err = dial(config.URL) - if conn != nil { - writer = conn - } - } - // TODO: If err != nil, we return it _with_ a valid exporter; the - // exporter should attempt to re-dial if it's retryable. Add a - // Start() and Stop() API. - return &Exporter{ - adapter: adapter, - config: config, - conn: conn, - writer: writer, - }, err -} - -// dial connects to a statsd service using several common network -// types. Presently "udp" and "unix" datagram socket connections are -// supported. -func dial(endpoint string) (net.Conn, error) { - dest, err := url.Parse(endpoint) - if err != nil { - return nil, err - } - - // TODO: Support tcp destination, need configurable timeouts first. - - scheme := dest.Scheme - switch scheme { - case "udp", "udp4", "udp6": - udpAddr, err := net.ResolveUDPAddr(scheme, dest.Host) - locAddr := &net.UDPAddr{} - if err != nil { - return nil, err - } - conn, err := net.DialUDP(scheme, locAddr, udpAddr) - if err != nil { - return nil, err - } - return conn, err - case "unix", "unixgram": - scheme = "unixgram" - locAddr := &net.UnixAddr{} - - sockAddr, err := net.ResolveUnixAddr(scheme, dest.Path) - if err != nil { - return nil, err - } - conn, err := net.DialUnix(scheme, locAddr, sockAddr) - if err != nil { - return nil, err - } - return conn, err - } - return nil, ErrInvalidScheme -} - -// Export is common code for any statsd-based metric.Exporter implementation. -func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { - buf := &e.buffer - buf.Reset() - - var aggErr error - var sendErr error - - aggErr = checkpointSet.ForEach(func(rec export.Record) error { - before := buf.Len() - - if err := e.formatMetric(rec, buf); err != nil { - return err - } - - if buf.Len() < e.config.MaxPacketSize { - return nil - } - if before == 0 { - // A single metric >= packet size - if err := e.send(buf.Bytes()); err != nil && sendErr == nil { - sendErr = err - } - buf.Reset() - return nil - } - - // Send and copy the leftover - if err := e.send(buf.Bytes()[:before]); err != nil && sendErr == nil { - sendErr = err - } - - leftover := buf.Len() - before - - copy(buf.Bytes()[0:leftover], buf.Bytes()[before:]) - - buf.Truncate(leftover) - return nil - }) - if err := e.send(buf.Bytes()); err != nil && sendErr == nil { - sendErr = err - } - if sendErr != nil { - return sendErr - } - return aggErr -} - -// send writes a complete buffer to the writer as a blocking call. -func (e *Exporter) send(buf []byte) error { - for len(buf) != 0 { - n, err := e.writer.Write(buf) - if err != nil { - return err - } - buf = buf[n:] - } - return nil -} - -// formatMetric formats an individual export record. For some records -// this will emit a single statistic, for some it will emit more than -// one. -func (e *Exporter) formatMetric(rec export.Record, buf *bytes.Buffer) error { - desc := rec.Descriptor() - agg := rec.Aggregator() - - // TODO handle non-Points Distribution/MaxSumCount by - // formatting individual quantiles, the sum, and the count as - // single statistics. For the dogstatsd variation, assuming - // open-source systems like Veneur add support, figure out the - // proper encoding for "d"-type distribution data. - - if pts, ok := agg.(aggregator.Points); ok { - var format string - if desc.Unit() == unit.Milliseconds { - format = formatTiming - } else { - format = formatHistogram - } - points, err := pts.Points() - if err != nil { - return err - } - for _, pt := range points { - e.formatSingleStat(rec, pt, format, buf) - } - - } else if sum, ok := agg.(aggregator.Sum); ok { - sum, err := sum.Sum() - if err != nil { - return err - } - e.formatSingleStat(rec, sum, formatCounter, buf) - - } else if lv, ok := agg.(aggregator.LastValue); ok { - lv, _, err := lv.LastValue() - if err != nil { - return err - } - e.formatSingleStat(rec, lv, formatGauge, buf) - } - return nil -} - -// formatSingleStat encodes a single item of statsd data followed by a -// newline. -func (e *Exporter) formatSingleStat(rec export.Record, val core.Number, fmtStr string, buf *bytes.Buffer) { - e.adapter.AppendName(rec, buf) - _, _ = buf.WriteRune(':') - writeNumber(buf, val, rec.Descriptor().NumberKind()) - _, _ = buf.WriteRune('|') - _, _ = buf.WriteString(fmtStr) - e.adapter.AppendTags(rec, buf) - _, _ = buf.WriteRune('\n') -} - -func writeNumber(buf *bytes.Buffer, num core.Number, kind core.NumberKind) { - var tmp [128]byte - var conv []byte - switch kind { - case core.Int64NumberKind: - conv = strconv.AppendInt(tmp[:0], num.AsInt64(), 10) - case core.Float64NumberKind: - conv = strconv.AppendFloat(tmp[:0], num.AsFloat64(), 'g', -1, 64) - case core.Uint64NumberKind: - conv = strconv.AppendUint(tmp[:0], num.AsUint64(), 10) - - } - _, _ = buf.Write(conv) -} diff --git a/exporters/metric/internal/statsd/conn_test.go b/exporters/metric/internal/statsd/conn_test.go deleted file mode 100644 index b8b99e9ec5e..00000000000 --- a/exporters/metric/internal/statsd/conn_test.go +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package statsd_test - -import ( - "bytes" - "context" - "fmt" - "strconv" - "strings" - "testing" - - "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/api/unit" - "go.opentelemetry.io/otel/exporters/metric/internal/statsd" - "go.opentelemetry.io/otel/exporters/metric/test" - export "go.opentelemetry.io/otel/sdk/export/metric" -) - -// withTagsAdapter tests a dogstatsd-style statsd exporter. -type withTagsAdapter struct { - *statsd.LabelEncoder -} - -func (*withTagsAdapter) AppendName(rec export.Record, buf *bytes.Buffer) { - _, _ = buf.WriteString(rec.Descriptor().Name()) -} - -func (ta *withTagsAdapter) AppendTags(rec export.Record, buf *bytes.Buffer) { - encoded := rec.Labels().Encoded(ta.LabelEncoder) - _, _ = buf.WriteString(encoded) -} - -func newWithTagsAdapter() *withTagsAdapter { - return &withTagsAdapter{ - statsd.NewLabelEncoder(), - } -} - -// noTagsAdapter simulates a plain-statsd exporter that appends tag -// values to the metric name. -type noTagsAdapter struct { -} - -func (*noTagsAdapter) AppendName(rec export.Record, buf *bytes.Buffer) { - _, _ = buf.WriteString(rec.Descriptor().Name()) - - iter := rec.Labels().Iter() - for iter.Next() { - tag := iter.Label() - _, _ = buf.WriteString(".") - _, _ = buf.WriteString(tag.Value.Emit()) - } -} - -func (*noTagsAdapter) AppendTags(rec export.Record, buf *bytes.Buffer) { -} - -func newNoTagsAdapter() *noTagsAdapter { - return &noTagsAdapter{} -} - -type testWriter struct { - vec []string -} - -func (w *testWriter) Write(b []byte) (int, error) { - w.vec = append(w.vec, string(b)) - return len(b), nil -} - -func TestBasicFormat(t *testing.T) { - type adapterOutput struct { - adapter statsd.Adapter - expected string - } - - 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 -`}, { - adapter: newNoTagsAdapter(), - expected: `counter.B.D:%s|c -observer.B.D:%s|g -measure.B.D:%s|h -timer.B.D:%s|ms -`}, - } { - adapter := ao.adapter - expected := ao.expected - t.Run(fmt.Sprintf("%T", adapter), func(t *testing.T) { - for _, nkind := range []core.NumberKind{ - core.Float64NumberKind, - core.Int64NumberKind, - } { - t.Run(nkind.String(), func(t *testing.T) { - ctx := context.Background() - writer := &testWriter{} - config := statsd.Config{ - Writer: writer, - MaxPacketSize: 1024, - } - exp, err := statsd.NewExporter(config, adapter) - if err != nil { - t.Fatal("New error: ", err) - } - - checkpointSet := test.NewCheckpointSet(export.NewDefaultLabelEncoder()) - cdesc := metric.NewDescriptor( - "counter", metric.CounterKind, nkind) - gdesc := metric.NewDescriptor( - "observer", metric.ObserverKind, nkind) - mdesc := metric.NewDescriptor( - "measure", metric.MeasureKind, nkind) - tdesc := metric.NewDescriptor( - "timer", metric.MeasureKind, nkind, metric.WithUnit(unit.Milliseconds)) - - labels := []core.KeyValue{ - key.New("A").String("B"), - key.New("C").String("D"), - } - const value = 123.456 - - checkpointSet.AddCounter(&cdesc, value, labels...) - checkpointSet.AddLastValue(&gdesc, value, labels...) - checkpointSet.AddMeasure(&mdesc, value, labels...) - checkpointSet.AddMeasure(&tdesc, value, labels...) - - err = exp.Export(ctx, checkpointSet) - require.Nil(t, err) - - var vfmt string - if nkind == core.Int64NumberKind { - fv := value - vfmt = strconv.FormatInt(int64(fv), 10) - } else { - vfmt = strconv.FormatFloat(value, 'g', -1, 64) - } - - require.Equal(t, 1, len(writer.vec)) - require.Equal(t, fmt.Sprintf(expected, vfmt, vfmt, vfmt, vfmt), writer.vec[0]) - }) - } - }) - } -} - -func makeLabels(offset, nkeys int) []core.KeyValue { - r := make([]core.KeyValue, nkeys) - for i := range r { - r[i] = key.New(fmt.Sprint("k", offset+i)).String(fmt.Sprint("v", offset+i)) - } - return r -} - -type splitTestCase struct { - name string - setup func(add func(int)) - check func(expected, got []string, t *testing.T) -} - -var splitTestCases = []splitTestCase{ - // These test use the number of keys to control where packets - // are split. - {"Simple", - func(add func(int)) { - add(1) - add(1000) - add(1) - }, - func(expected, got []string, t *testing.T) { - require.EqualValues(t, expected, got) - }, - }, - {"LastBig", - func(add func(int)) { - add(1) - add(1) - add(1000) - }, - func(expected, got []string, t *testing.T) { - require.Equal(t, 2, len(got)) - require.EqualValues(t, []string{ - expected[0] + expected[1], - expected[2], - }, got) - }, - }, - {"FirstBig", - func(add func(int)) { - add(1000) - add(1) - add(1) - add(1000) - add(1) - add(1) - }, - func(expected, got []string, t *testing.T) { - require.Equal(t, 4, len(got)) - require.EqualValues(t, []string{ - expected[0], - expected[1] + expected[2], - expected[3], - expected[4] + expected[5], - }, got) - }, - }, - {"OneBig", - func(add func(int)) { - add(1000) - }, - func(expected, got []string, t *testing.T) { - require.EqualValues(t, expected, got) - }, - }, - {"LastSmall", - func(add func(int)) { - add(1000) - add(1) - }, - func(expected, got []string, t *testing.T) { - require.EqualValues(t, expected, got) - }, - }, - {"Overflow", - func(add func(int)) { - for i := 0; i < 1000; i++ { - add(1) - } - }, - func(expected, got []string, t *testing.T) { - require.Less(t, 1, len(got)) - require.Equal(t, strings.Join(expected, ""), strings.Join(got, "")) - }, - }, - {"Empty", - func(add func(int)) { - }, - func(expected, got []string, t *testing.T) { - require.Equal(t, 0, len(got)) - }, - }, - {"AllBig", - func(add func(int)) { - add(1000) - add(1000) - add(1000) - }, - func(expected, got []string, t *testing.T) { - require.EqualValues(t, expected, got) - }, - }, -} - -func TestPacketSplit(t *testing.T) { - for _, tcase := range splitTestCases { - t.Run(tcase.name, func(t *testing.T) { - ctx := context.Background() - writer := &testWriter{} - config := statsd.Config{ - Writer: writer, - MaxPacketSize: 1024, - } - adapter := newWithTagsAdapter() - exp, err := statsd.NewExporter(config, adapter) - if err != nil { - t.Fatal("New error: ", err) - } - - checkpointSet := test.NewCheckpointSet(adapter.LabelEncoder) - desc := metric.NewDescriptor("counter", metric.CounterKind, core.Int64NumberKind) - - var expected []string - - offset := 0 - tcase.setup(func(nkeys int) { - labels := makeLabels(offset, nkeys) - offset += nkeys - iter := export.LabelSlice(labels).Iter() - encoded := adapter.LabelEncoder.Encode(iter) - expect := fmt.Sprint("counter:100|c", encoded, "\n") - expected = append(expected, expect) - checkpointSet.AddCounter(&desc, 100, labels...) - }) - - err = exp.Export(ctx, checkpointSet) - require.Nil(t, err) - - tcase.check(expected, writer.vec, t) - }) - } -} diff --git a/exporters/metric/internal/statsd/labels.go b/exporters/metric/internal/statsd/labels.go deleted file mode 100644 index 6e7ac742e71..00000000000 --- a/exporters/metric/internal/statsd/labels.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package statsd - -import ( - "bytes" - "sync" - - export "go.opentelemetry.io/otel/sdk/export/metric" -) - -// LabelEncoder encodes metric labels in the dogstatsd syntax. -// -// TODO: find a link for this syntax. It's been copied out of code, -// not a specification: -// -// https://github.com/stripe/veneur/blob/master/sinks/datadog/datadog.go -type LabelEncoder struct { - pool sync.Pool -} - -var _ export.LabelEncoder = &LabelEncoder{} -var leID = export.NewLabelEncoderID() - -// NewLabelEncoder returns a new encoder for dogstatsd-syntax metric -// labels. -func NewLabelEncoder() *LabelEncoder { - return &LabelEncoder{ - pool: sync.Pool{ - New: func() interface{} { - return &bytes.Buffer{} - }, - }, - } -} - -// Encode emits a string like "|#key1:value1,key2:value2". -func (e *LabelEncoder) Encode(iter export.LabelIterator) string { - buf := e.pool.Get().(*bytes.Buffer) - defer e.pool.Put(buf) - buf.Reset() - - delimiter := "|#" - - for iter.Next() { - kv := iter.Label() - _, _ = buf.WriteString(delimiter) - _, _ = buf.WriteString(string(kv.Key)) - _, _ = buf.WriteRune(':') - _, _ = buf.WriteString(kv.Value.Emit()) - delimiter = "," - } - return buf.String() -} - -func (*LabelEncoder) ID() int64 { - return leID -} diff --git a/exporters/metric/internal/statsd/labels_test.go b/exporters/metric/internal/statsd/labels_test.go deleted file mode 100644 index 09a0bf5fffb..00000000000 --- a/exporters/metric/internal/statsd/labels_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package statsd_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "go.opentelemetry.io/otel/api/core" - "go.opentelemetry.io/otel/api/key" - "go.opentelemetry.io/otel/exporters/metric/internal/statsd" - export "go.opentelemetry.io/otel/sdk/export/metric" -) - -var testLabels = []core.KeyValue{ - key.New("A").String("B"), - key.New("C").String("D"), - key.New("E").Float64(1.5), -} - -func TestLabelSyntax(t *testing.T) { - encoder := statsd.NewLabelEncoder() - - require.Equal(t, `|#A:B,C:D,E:1.5`, encoder.Encode(export.LabelSlice(testLabels).Iter())) - - kvs := []core.KeyValue{ - key.New("A").String("B"), - } - require.Equal(t, `|#A:B`, encoder.Encode(export.LabelSlice(kvs).Iter())) - - require.Equal(t, "", encoder.Encode(export.LabelSlice(nil).Iter())) -}