Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dogstatsd metrics exporter #326

Merged
merged 111 commits into from
Nov 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
19346a4
Add MetricAggregator.Merge() implementations
Oct 30, 2019
cb9557a
Upstream
Oct 30, 2019
03f7854
Update from feedback
Oct 30, 2019
09d88a1
Type
Oct 30, 2019
865b7ab
Upstream
Oct 30, 2019
5719abe
Upstream
Oct 30, 2019
c6ca4fd
Ckpt
Oct 31, 2019
acc2450
Ckpt
Oct 31, 2019
5bebed5
Upstream
Oct 31, 2019
5b23af4
Add push controller
Oct 31, 2019
80e0df8
Ckpt
Oct 31, 2019
f533814
Upstream
Oct 31, 2019
3423242
Add aggregator interfaces, stdout encoder
Oct 31, 2019
a788631
Modify basic main.go
Oct 31, 2019
fbc50a1
Main is working
Oct 31, 2019
49aa969
Batch stdout output
Oct 31, 2019
c2c73be
Sum udpate
Oct 31, 2019
5ea9128
Rename stdout
Oct 31, 2019
f0d986c
Add stateless/stateful Batcher options
Oct 31, 2019
63d79a8
Undo a for-loop in the example, remove a done TODO
Oct 31, 2019
8716da3
Merge
Nov 3, 2019
837035d
Update imports
Nov 3, 2019
9586471
Add note
Nov 3, 2019
0c09f8c
Rename defaultkeys
Nov 4, 2019
03ff7d2
Support variable label encoder to speed OpenMetrics/Statsd export
Nov 4, 2019
88a236e
Lint
Nov 4, 2019
bf313da
Upstream
Nov 5, 2019
37a7c7a
Checkpoint
Nov 5, 2019
c8a321c
Checkpoint
Nov 5, 2019
db41c9d
Doc
Nov 5, 2019
dd6229c
Merge
Nov 5, 2019
0bc5ffe
Precommit/lint
Nov 6, 2019
c575b08
Simplify Aggregator API
Nov 6, 2019
3be8e6e
Record->Identifier
Nov 6, 2019
214b882
Remove export.Record a.k.a. Identifier
Nov 6, 2019
048c8d9
Checkpoint
Nov 6, 2019
657c064
Propagate errors to the SDK, remove a bunch of 'TODO warn'
Nov 6, 2019
0d78cfa
Checkpoint
Nov 6, 2019
f81aa34
Introduce export.Labels
Nov 6, 2019
94580ae
Comments in export/metric.go
Nov 7, 2019
2dee926
Comment
Nov 7, 2019
e4c9dde
Merge
Nov 7, 2019
a7623d8
More merge
Nov 7, 2019
8fcfe95
More doc
Nov 7, 2019
df3b3af
Complete example
Nov 7, 2019
4121362
Lint fixes
Nov 7, 2019
41d3f7b
Add a testable example
Nov 7, 2019
72872fa
Lint
Nov 7, 2019
f0f0906
Upstream
Nov 8, 2019
2425008
Dogstats
Nov 8, 2019
0160c3d
Let Export return an error
Nov 8, 2019
79eadad
Upstream
Nov 8, 2019
760fe57
Checkpoint
Nov 8, 2019
77e4c3f
add a basic stdout exporter test
Nov 8, 2019
dc23ae1
Add measure test; fix aggregator APIs
Nov 9, 2019
18b8340
Upstream
Nov 9, 2019
dfbc881
Use JSON numbers, not strings
Nov 9, 2019
9ecdf51
Test stdout exporter error
Nov 9, 2019
60ab98b
Add a test for the call to RangeTest
Nov 9, 2019
bf2f1e6
Add error handler API to improve correctness test; return errors from…
Nov 9, 2019
419ed4f
Undo the previous -- do not expose errors
Nov 9, 2019
0953112
Add simple selector variations, test
Nov 9, 2019
8034ebe
Repair examples
Nov 9, 2019
a472048
Test push controller error handling
Nov 9, 2019
08a26de
Add SDK label encoder tests
Nov 9, 2019
c09bd0e
Add a defaultkeys batcher test
Nov 9, 2019
9ef0a37
Add an ungrouped batcher test
Nov 9, 2019
557f912
Lint new tests
Nov 9, 2019
0133786
Respond to krnowak's feedback
Nov 10, 2019
2d3e3cf
Upstream
Nov 10, 2019
d12ab8b
Checkpoint
Nov 10, 2019
7749675
Funciontal example using unixgram
Nov 11, 2019
6bf8cad
Tidy the example
Nov 11, 2019
cc88c9d
Add a packet-split test
Nov 12, 2019
3ff9bf9
More tests
Nov 12, 2019
e518398
Undo comment
Nov 12, 2019
72e3d30
Use concrete receivers for export records and labels, since the const…
Nov 12, 2019
9acdc5a
Bug fix for stateful batchers; clone an aggregator for long term storage
Nov 12, 2019
4d331e6
Upstream
Nov 12, 2019
d7a5cda
Upstream
Nov 14, 2019
399c34c
Remove TODO addressed in #318
Nov 14, 2019
2a75566
Add errors to all aggregator interfaces
Nov 14, 2019
efb75ed
Handle ErrNoLastValue case in stdout exporter
Nov 14, 2019
1153503
Move aggregator API into sdk/export/metric/aggregator
Nov 15, 2019
623a63a
Update all aggregator exported-method comments
Nov 15, 2019
e298c94
Document the aggregator APIs
Nov 15, 2019
d75bc6e
More aggregator comments
Nov 15, 2019
723d084
Add multiple updates to the ungrouped test
Nov 15, 2019
8b5a4d6
Fixes for feedback from Gustavo and Liz
Nov 15, 2019
13e0580
Producer->CheckpointSet; add FinishedCollection
Nov 15, 2019
b75059e
Process takes an export.Record
Nov 15, 2019
d03709a
ReadCheckpoint->CheckpointSet
Nov 15, 2019
93fe58f
EncodeLabels->Encode
Nov 15, 2019
782cabf
Format a better inconsistent type error; add more aggregator API tests
Nov 15, 2019
eedaaab
More RangeTest test coverage
Nov 15, 2019
f67b47c
Make benbjohnson/clock a test-only dependency
Nov 15, 2019
2e7007d
Merge
Nov 15, 2019
046db4a
Handle ErrNoLastValue in stress_test
Nov 15, 2019
a294720
Upstream
Nov 15, 2019
2ed8fe1
Upstream
Nov 15, 2019
423fb82
Merge upstream
Nov 16, 2019
d507863
Upstream
Nov 19, 2019
12cb491
Upstream
Nov 20, 2019
4b2e5c3
Update comments; use a pipe vs a unix socket in the example test
Nov 20, 2019
f93dbe2
Update test
Nov 20, 2019
c831b6e
Spelling
Nov 20, 2019
c48ff79
Typo fix
Nov 20, 2019
260f5fe
Rename DefaultLabelEncoder to NewDefaultLabelEncoder for clarity
Nov 22, 2019
e50c004
Rename DefaultLabelEncoder to NewDefaultLabelEncoder for clarity
Nov 22, 2019
4cf2f3c
Test different adapters; add ForceEncode to statsd label encoder
Nov 22, 2019
889414d
Upstream
Nov 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/basic/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func initMeter() *push.Controller {
if err != nil {
log.Panicf("failed to initialize metric stdout exporter %v", err)
}
batcher := defaultkeys.New(selector, metricsdk.DefaultLabelEncoder(), true)
batcher := defaultkeys.New(selector, metricsdk.NewDefaultLabelEncoder(), true)
pusher := push.New(batcher, exporter, time.Second)
pusher.Start()

Expand Down
75 changes: 75 additions & 0 deletions exporter/metric/dogstatsd/dogstatsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2019, 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/exporter/metric/dogstatsd"

import (
"bytes"

"go.opentelemetry.io/otel/exporter/metric/internal/statsd"
export "go.opentelemetry.io/otel/sdk/export/metric"
)

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
*statsd.LabelEncoder

ReencodedLabelsCount int
}
)

var (
_ export.Exporter = &Exporter{}
_ export.LabelEncoder = &Exporter{}
)

// New returns a new Dogstatsd-syntax exporter. This type implements
// the metric.LabelEncoder interface, allowing the SDK's unique label
// encoding to be pre-computed for the exporter and stored in the
// LabelSet.
func New(config Config) (*Exporter, error) {
jmacd marked this conversation as resolved.
Show resolved Hide resolved
exp := &Exporter{
LabelEncoder: statsd.NewLabelEncoder(),
}

var err error
exp.Exporter, err = statsd.NewExporter(config, exp)
return exp, err
}

// 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, inefficient := e.LabelEncoder.ForceEncode(rec.Labels())
_, _ = buf.WriteString(encoded)

if inefficient {
e.ReencodedLabelsCount++
}
}
69 changes: 69 additions & 0 deletions exporter/metric/dogstatsd/dogstatsd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2019, 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"
"fmt"
"testing"

"github.com/stretchr/testify/require"

"go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/exporter/metric/dogstatsd"
"go.opentelemetry.io/otel/exporter/metric/internal/statsd"
"go.opentelemetry.io/otel/exporter/metric/test"
export "go.opentelemetry.io/otel/sdk/export/metric"
sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/aggregator/counter"
)

// 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) {
for inefficientCount, encoder := range []export.LabelEncoder{
statsd.NewLabelEncoder(), // inefficientCount == 0
sdk.NewDefaultLabelEncoder(), // inefficientCount == 1
} {
t.Run(fmt.Sprintf("%T", encoder), func(t *testing.T) {
ctx := context.Background()
checkpointSet := test.NewCheckpointSet(encoder)

desc := export.NewDescriptor("test.name", export.CounterKind, nil, "", "", core.Int64NumberKind, false)
cagg := counter.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.New(dogstatsd.Config{
Writer: &buf,
})
require.Nil(t, err)
require.Equal(t, 0, exp.ReencodedLabelsCount)

err = exp.Export(ctx, checkpointSet)
require.Nil(t, err)

require.Equal(t, inefficientCount, exp.ReencodedLabelsCount)

require.Equal(t, "test.name:123|c|#A:B\n", buf.String())
})
}
}
87 changes: 87 additions & 0 deletions exporter/metric/dogstatsd/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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/exporter/metric/dogstatsd"
"go.opentelemetry.io/otel/sdk/metric/batcher/ungrouped"
"go.opentelemetry.io/otel/sdk/metric/controller/push"
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
)

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
selector := simple.NewWithExactMeasure()
exporter, err := dogstatsd.New(dogstatsd.Config{
// The Writer field provides test support.
Writer: writer,

// In real code, use the URL field:
//
// URL: fmt.Sprint("unix://", path),
})
if err != nil {
log.Fatal("Could not initialize dogstatsd exporter:", err)
}
// The ungrouped batcher ensures that the export sees the full
// set of labels as dogstatsd tags.
batcher := ungrouped.New(selector, false)

// The pusher automatically recognizes that the exporter
// implements the LabelEncoder interface, which ensures the
// export encoding for labels is encoded in the LabelSet.
pusher := push.New(batcher, exporter, time.Hour)
pusher.Start()

ctx := context.Background()

key := key.New("key")

// pusher implements the metric.MeterProvider interface:
meter := pusher.GetMeter("example")

// Create and update a single counter:
counter := 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
}
Loading