1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-06-06 23:16:14 +02:00

[metrics] standardize/simplify export pipeline setup (#395)

* Introduce simplified export pipeline setup for stdout

* Standardize dogstatsd,stdout,prometheus calling.

* Creates NewRawExporter, NewExportPipeline, InstallNewPipeline methods.
* Uses Options rather than Config throughout for options.

* fix merge conflicts.

Co-authored-by: Liz Fong-Jones <elizabeth@ctyalcove.org>
This commit is contained in:
Matej Gera 2020-01-02 19:41:21 +01:00 committed by Joshua MacDonald
parent 5eb457a119
commit 067aa9e142
14 changed files with 220 additions and 111 deletions

View File

@ -5,7 +5,6 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -15,9 +14,6 @@ import (
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/exporter/metric/stdout" "go.opentelemetry.io/otel/exporter/metric/stdout"
metrictest "go.opentelemetry.io/otel/internal/metric" metrictest "go.opentelemetry.io/otel/internal/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"
) )
func TestDirect(t *testing.T) { func TestDirect(t *testing.T) {
@ -207,26 +203,13 @@ func TestDefaultSDK(t *testing.T) {
counter.Add(ctx, 1, labels1) counter.Add(ctx, 1, labels1)
in, out := io.Pipe() in, out := io.Pipe()
// TODO this should equal a stdout.NewPipeline(), use it. pusher, err := stdout.InstallNewPipeline(stdout.Options{
// Consider also moving the io.Pipe() and go func() call
// below into a test helper somewhere.
sdk := func(options stdout.Options) *push.Controller {
selector := simple.NewWithInexpensiveMeasure()
exporter, err := stdout.New(options)
if err != nil {
panic(err)
}
batcher := ungrouped.New(selector, true)
pusher := push.New(batcher, exporter, time.Second)
pusher.Start()
return pusher
}(stdout.Options{
Writer: out, Writer: out,
DoNotPrintTime: true, DoNotPrintTime: true,
}) })
if err != nil {
global.SetMeterProvider(sdk) panic(err)
}
counter.Add(ctx, 1, labels1) counter.Add(ctx, 1, labels1)
@ -236,9 +219,9 @@ func TestDefaultSDK(t *testing.T) {
ch <- string(data) ch <- string(data)
}() }()
sdk.Stop() pusher.Stop()
out.Close() out.Close()
require.Equal(t, `{"updates":[{"name":"test.builtin{A=B}","sum":1}]} require.Equal(t, `{"updates":[{"name":"test.builtin","sum":1}]}
`, <-ch) `, <-ch)
} }

View File

@ -17,7 +17,6 @@ package main
import ( import (
"context" "context"
"log" "log"
"time"
"go.opentelemetry.io/otel/api/distributedcontext" "go.opentelemetry.io/otel/api/distributedcontext"
"go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/global"
@ -26,10 +25,7 @@ import (
"go.opentelemetry.io/otel/api/trace" "go.opentelemetry.io/otel/api/trace"
metricstdout "go.opentelemetry.io/otel/exporter/metric/stdout" metricstdout "go.opentelemetry.io/otel/exporter/metric/stdout"
tracestdout "go.opentelemetry.io/otel/exporter/trace/stdout" tracestdout "go.opentelemetry.io/otel/exporter/trace/stdout"
metricsdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/batcher/defaultkeys"
"go.opentelemetry.io/otel/sdk/metric/controller/push" "go.opentelemetry.io/otel/sdk/metric/controller/push"
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
sdktrace "go.opentelemetry.io/otel/sdk/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace"
) )
@ -57,19 +53,13 @@ func initTracer() {
} }
func initMeter() *push.Controller { func initMeter() *push.Controller {
selector := simple.NewWithExactMeasure() pusher, err := metricstdout.InstallNewPipeline(metricstdout.Options{
exporter, err := metricstdout.New(metricstdout.Options{
Quantiles: []float64{0.5, 0.9, 0.99}, Quantiles: []float64{0.5, 0.9, 0.99},
PrettyPrint: false, PrettyPrint: false,
}) })
if err != nil { if err != nil {
log.Panicf("failed to initialize metric stdout exporter %v", err) log.Panicf("failed to initialize metric stdout exporter %v", err)
} }
batcher := defaultkeys.New(selector, metricsdk.NewDefaultLabelEncoder(), true)
pusher := push.New(batcher, exporter, time.Second)
pusher.Start()
global.SetMeterProvider(pusher)
return pusher return pusher
} }

View File

@ -24,10 +24,7 @@ import (
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporter/metric/prometheus" "go.opentelemetry.io/otel/exporter/metric/prometheus"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/batcher/defaultkeys"
"go.opentelemetry.io/otel/sdk/metric/controller/push" "go.opentelemetry.io/otel/sdk/metric/controller/push"
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
) )
var ( var (
@ -37,29 +34,15 @@ var (
) )
func initMeter() *push.Controller { func initMeter() *push.Controller {
selector := simple.NewWithExactMeasure() pusher, hf, err := prometheus.InstallNewPipeline(prometheus.Options{})
exporter, err := prometheus.NewExporter(prometheus.Options{})
if err != nil { if err != nil {
log.Panicf("failed to initialize metric stdout exporter %v", err) log.Panicf("failed to initialize prometheus exporter %v", err)
} }
// Prometheus needs to use a stateful batcher since counters (and histogram since they are a collection of Counters) http.HandleFunc("/", hf)
// are cumulative (i.e., monotonically increasing values) and should not be resetted after each export.
//
// Prometheus uses this approach to be resilient to scrape failures.
// If a Prometheus server tries to scrape metrics from a host and fails for some reason,
// it could try again on the next scrape and no data would be lost, only resolution.
//
// Gauges (or LastValues) and Summaries are an exception to this and have different behaviors.
batcher := defaultkeys.New(selector, sdkmetric.NewDefaultLabelEncoder(), true)
pusher := push.New(batcher, exporter, time.Second)
pusher.Start()
go func() { go func() {
_ = http.ListenAndServe(":2222", exporter) _ = http.ListenAndServe(":2222", nil)
}() }()
global.SetMeterProvider(pusher)
return pusher return pusher
} }

View File

@ -16,13 +16,19 @@ package dogstatsd // import "go.opentelemetry.io/otel/exporter/metric/dogstatsd"
import ( import (
"bytes" "bytes"
"time"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/exporter/metric/internal/statsd" "go.opentelemetry.io/otel/exporter/metric/internal/statsd"
export "go.opentelemetry.io/otel/sdk/export/metric" 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 ( type (
Config = statsd.Config Options = statsd.Options
// Exporter implements a dogstatsd-format statsd exporter, // Exporter implements a dogstatsd-format statsd exporter,
// which encodes label sets as independent fields in the // which encodes label sets as independent fields in the
@ -45,20 +51,59 @@ var (
_ export.LabelEncoder = &Exporter{} _ export.LabelEncoder = &Exporter{}
) )
// New returns a new Dogstatsd-syntax exporter. This type implements // NewRawExporter returns a new Dogstatsd-syntax exporter for use in a pipeline.
// the metric.LabelEncoder interface, allowing the SDK's unique label // This type implements the metric.LabelEncoder interface,
// encoding to be pre-computed for the exporter and stored in the // allowing the SDK's unique label encoding to be pre-computed
// LabelSet. // for the exporter and stored in the LabelSet.
func New(config Config) (*Exporter, error) { func NewRawExporter(options Options) (*Exporter, error) {
exp := &Exporter{ exp := &Exporter{
LabelEncoder: statsd.NewLabelEncoder(), LabelEncoder: statsd.NewLabelEncoder(),
} }
var err error var err error
exp.Exporter, err = statsd.NewExporter(config, exp) exp.Exporter, err = statsd.NewExporter(options, exp)
return exp, err return exp, err
} }
// InstallNewPipeline instantiates a NewExportPipeline and registers it globally.
// Typically called as:
// pipeline, err := dogstatsd.InstallNewPipeline(dogstatsd.Options{...})
// if err != nil {
// ...
// }
// defer pipeline.Stop()
// ... Done
func InstallNewPipeline(options Options) (*push.Controller, error) {
controller, err := NewExportPipeline(options)
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(options Options) (*push.Controller, error) {
selector := simple.NewWithExactMeasure()
exporter, err := NewRawExporter(options)
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, 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()
return pusher, nil
}
// AppendName is part of the stats-internal adapter interface. // AppendName is part of the stats-internal adapter interface.
func (*Exporter) AppendName(rec export.Record, buf *bytes.Buffer) { func (*Exporter) AppendName(rec export.Record, buf *bytes.Buffer) {
_, _ = buf.WriteString(rec.Descriptor().Name()) _, _ = buf.WriteString(rec.Descriptor().Name())

View File

@ -52,7 +52,7 @@ func TestDogstatsLabels(t *testing.T) {
checkpointSet.Add(desc, cagg, key.New("A").String("B")) checkpointSet.Add(desc, cagg, key.New("A").String("B"))
var buf bytes.Buffer var buf bytes.Buffer
exp, err := dogstatsd.New(dogstatsd.Config{ exp, err := dogstatsd.NewRawExporter(dogstatsd.Options{
Writer: &buf, Writer: &buf,
}) })
require.Nil(t, err) require.Nil(t, err)

View File

@ -6,14 +6,10 @@ import (
"io" "io"
"log" "log"
"sync" "sync"
"time"
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporter/metric/dogstatsd" "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() { func ExampleNew() {
@ -42,8 +38,7 @@ func ExampleNew() {
}() }()
// Create a meter // Create a meter
selector := simple.NewWithExactMeasure() pusher, err := dogstatsd.NewExportPipeline(dogstatsd.Options{
exporter, err := dogstatsd.New(dogstatsd.Config{
// The Writer field provides test support. // The Writer field provides test support.
Writer: writer, Writer: writer,
@ -54,15 +49,6 @@ func ExampleNew() {
if err != nil { if err != nil {
log.Fatal("Could not initialize dogstatsd exporter:", err) 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() ctx := context.Background()

View File

@ -34,8 +34,8 @@ import (
) )
type ( type (
// Config supports common options that apply to statsd exporters. // Options supports common options that apply to statsd exporters.
Config struct { Options struct {
// URL describes the destination for exporting statsd data. // URL describes the destination for exporting statsd data.
// e.g., udp://host:port // e.g., udp://host:port
// tcp://host:port // tcp://host:port
@ -57,7 +57,7 @@ type (
// exporters. // exporters.
Exporter struct { Exporter struct {
adapter Adapter adapter Adapter
config Config options Options
conn net.Conn conn net.Conn
writer io.Writer writer io.Writer
buffer bytes.Buffer buffer bytes.Buffer
@ -88,17 +88,17 @@ var (
// NewExport returns a common implementation for exporters that Export // NewExport returns a common implementation for exporters that Export
// statsd syntax. // statsd syntax.
func NewExporter(config Config, adapter Adapter) (*Exporter, error) { func NewExporter(options Options, adapter Adapter) (*Exporter, error) {
if config.MaxPacketSize <= 0 { if options.MaxPacketSize <= 0 {
config.MaxPacketSize = MaxPacketSize options.MaxPacketSize = MaxPacketSize
} }
var writer io.Writer var writer io.Writer
var conn net.Conn var conn net.Conn
var err error var err error
if config.Writer != nil { if options.Writer != nil {
writer = config.Writer writer = options.Writer
} else { } else {
conn, err = dial(config.URL) conn, err = dial(options.URL)
if conn != nil { if conn != nil {
writer = conn writer = conn
} }
@ -108,7 +108,7 @@ func NewExporter(config Config, adapter Adapter) (*Exporter, error) {
// Start() and Stop() API. // Start() and Stop() API.
return &Exporter{ return &Exporter{
adapter: adapter, adapter: adapter,
config: config, options: options,
conn: conn, conn: conn,
writer: writer, writer: writer,
}, err }, err
@ -171,7 +171,7 @@ func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet)
return return
} }
if buf.Len() < e.config.MaxPacketSize { if buf.Len() < e.options.MaxPacketSize {
return return
} }
if before == 0 { if before == 0 {

View File

@ -113,11 +113,11 @@ timer.B.D:%s|ms
t.Run(nkind.String(), func(t *testing.T) { t.Run(nkind.String(), func(t *testing.T) {
ctx := context.Background() ctx := context.Background()
writer := &testWriter{} writer := &testWriter{}
config := statsd.Config{ options := statsd.Options{
Writer: writer, Writer: writer,
MaxPacketSize: 1024, MaxPacketSize: 1024,
} }
exp, err := statsd.NewExporter(config, adapter) exp, err := statsd.NewExporter(options, adapter)
if err != nil { if err != nil {
t.Fatal("New error: ", err) t.Fatal("New error: ", err)
} }
@ -274,12 +274,12 @@ func TestPacketSplit(t *testing.T) {
t.Run(tcase.name, func(t *testing.T) { t.Run(tcase.name, func(t *testing.T) {
ctx := context.Background() ctx := context.Background()
writer := &testWriter{} writer := &testWriter{}
config := statsd.Config{ options := statsd.Options{
Writer: writer, Writer: writer,
MaxPacketSize: 1024, MaxPacketSize: 1024,
} }
adapter := newWithTagsAdapter() adapter := newWithTagsAdapter()
exp, err := statsd.NewExporter(config, adapter) exp, err := statsd.NewExporter(options, adapter)
if err != nil { if err != nil {
t.Fatal("New error: ", err) t.Fatal("New error: ", err)
} }

View File

@ -18,13 +18,19 @@ import (
"context" "context"
"fmt" "fmt"
"net/http" "net/http"
"time"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"go.opentelemetry.io/otel/api/core" "go.opentelemetry.io/otel/api/core"
"go.opentelemetry.io/otel/api/global"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/batcher/defaultkeys"
"go.opentelemetry.io/otel/sdk/metric/controller/push"
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
) )
// Exporter is an implementation of metric.Exporter that sends metrics to // Exporter is an implementation of metric.Exporter that sends metrics to
@ -73,8 +79,9 @@ type Options struct {
OnError func(error) OnError func(error)
} }
// NewExporter returns a new prometheus exporter for prometheus metrics. // NewRawExporter returns a new prometheus exporter for prometheus metrics
func NewExporter(opts Options) (*Exporter, error) { // for use in a pipeline.
func NewRawExporter(opts Options) (*Exporter, error) {
if opts.Registry == nil { if opts.Registry == nil {
opts.Registry = prometheus.NewRegistry() opts.Registry = prometheus.NewRegistry()
} }
@ -108,6 +115,48 @@ func NewExporter(opts Options) (*Exporter, error) {
return e, nil return e, nil
} }
// InstallNewPipeline instantiates a NewExportPipeline and registers it globally.
// Typically called as:
// pipeline, hf, err := prometheus.InstallNewPipeline(prometheus.Options{...})
// if err != nil {
// ...
// }
// http.HandleFunc("/metrics", hf)
// defer pipeline.Stop()
// ... Done
func InstallNewPipeline(options Options) (*push.Controller, http.HandlerFunc, error) {
controller, hf, err := NewExportPipeline(options)
if err != nil {
return controller, hf, err
}
global.SetMeterProvider(controller)
return controller, hf, err
}
// NewExportPipeline sets up a complete export pipeline with the recommended setup,
// chaining a NewRawExporter into the recommended selectors and batchers.
func NewExportPipeline(options Options) (*push.Controller, http.HandlerFunc, error) {
selector := simple.NewWithExactMeasure()
exporter, err := NewRawExporter(options)
if err != nil {
return nil, nil, err
}
// Prometheus needs to use a stateful batcher since counters (and histogram since they are a collection of Counters)
// are cumulative (i.e., monotonically increasing values) and should not be resetted after each export.
//
// Prometheus uses this approach to be resilient to scrape failures.
// If a Prometheus server tries to scrape metrics from a host and fails for some reason,
// it could try again on the next scrape and no data would be lost, only resolution.
//
// Gauges (or LastValues) and Summaries are an exception to this and have different behaviors.
batcher := defaultkeys.New(selector, sdkmetric.NewDefaultLabelEncoder(), false)
pusher := push.New(batcher, exporter, time.Second)
pusher.Start()
return pusher, exporter.ServeHTTP, nil
}
// Export exports the provide metric record to prometheus. // Export exports the provide metric record to prometheus.
func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error {
e.snapshot = checkpointSet e.snapshot = checkpointSet

View File

@ -19,11 +19,11 @@ import (
) )
func TestPrometheusExporter(t *testing.T) { func TestPrometheusExporter(t *testing.T) {
exporter, err := prometheus.NewExporter(prometheus.Options{ exporter, err := prometheus.NewRawExporter(prometheus.Options{
DefaultSummaryQuantiles: []float64{0.5, 0.9, 0.99}, DefaultSummaryQuantiles: []float64{0.5, 0.9, 0.99},
}) })
if err != nil { if err != nil {
log.Panicf("failed to initialize metric stdout exporter %v", err) log.Panicf("failed to initialize prometheus exporter %v", err)
} }
var expected []string var expected []string

View File

@ -0,0 +1,43 @@
package stdout_test
import (
"context"
"log"
"go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporter/metric/stdout"
)
func ExampleNewExportPipeline() {
// Create a meter
pusher, err := stdout.NewExportPipeline(stdout.Options{
PrettyPrint: true,
DoNotPrintTime: true,
})
if err != nil {
log.Fatal("Could not initialize stdout exporter:", err)
}
defer pusher.Stop()
ctx := context.Background()
key := key.New("key")
meter := pusher.Meter("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)
// Output:
// {
// "updates": [
// {
// "name": "a.counter{key=value}",
// "sum": 100
// }
// ]
// }
}

View File

@ -23,8 +23,14 @@ import (
"strings" "strings"
"time" "time"
"go.opentelemetry.io/otel/api/global"
export "go.opentelemetry.io/otel/sdk/export/metric" export "go.opentelemetry.io/otel/sdk/export/metric"
"go.opentelemetry.io/otel/sdk/export/metric/aggregator" "go.opentelemetry.io/otel/sdk/export/metric/aggregator"
metricsdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/batcher/defaultkeys"
"go.opentelemetry.io/otel/sdk/metric/controller/push"
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
) )
type Exporter struct { type Exporter struct {
@ -80,7 +86,8 @@ type expoQuantile struct {
V interface{} `json:"v"` V interface{} `json:"v"`
} }
func New(options Options) (*Exporter, error) { // NewRawExporter creates a stdout Exporter for use in a pipeline.
func NewRawExporter(options Options) (*Exporter, error) {
if options.Writer == nil { if options.Writer == nil {
options.Writer = os.Stdout options.Writer = os.Stdout
} }
@ -98,6 +105,38 @@ func New(options Options) (*Exporter, error) {
}, nil }, nil
} }
// InstallNewPipeline instantiates a NewExportPipeline and registers it globally.
// Typically called as:
// pipeline, err := stdout.InstallNewPipeline(stdout.Options{...})
// if err != nil {
// ...
// }
// defer pipeline.Stop()
// ... Done
func InstallNewPipeline(options Options) (*push.Controller, error) {
controller, err := NewExportPipeline(options)
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(options Options) (*push.Controller, error) {
selector := simple.NewWithExactMeasure()
exporter, err := NewRawExporter(options)
if err != nil {
return nil, err
}
batcher := defaultkeys.New(selector, metricsdk.NewDefaultLabelEncoder(), true)
pusher := push.New(batcher, exporter, time.Second)
pusher.Start()
return pusher, nil
}
func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error { func (e *Exporter) Export(_ context.Context, checkpointSet export.CheckpointSet) error {
// N.B. Only return one aggError, if any occur. They're likely // N.B. Only return one aggError, if any occur. They're likely
// to be duplicates of the same error. // to be duplicates of the same error.

View File

@ -36,7 +36,7 @@ func newFixture(t *testing.T, options stdout.Options) testFixture {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
options.Writer = buf options.Writer = buf
options.DoNotPrintTime = true options.DoNotPrintTime = true
exp, err := stdout.New(options) exp, err := stdout.NewRawExporter(options)
if err != nil { if err != nil {
t.Fatal("Error building fixture: ", err) t.Fatal("Error building fixture: ", err)
} }
@ -60,7 +60,7 @@ func (fix testFixture) Export(checkpointSet export.CheckpointSet) {
} }
func TestStdoutInvalidQuantile(t *testing.T) { func TestStdoutInvalidQuantile(t *testing.T) {
_, err := stdout.New(stdout.Options{ _, err := stdout.NewRawExporter(stdout.Options{
Quantiles: []float64{1.1, 0.9}, Quantiles: []float64{1.1, 0.9},
}) })
require.Error(t, err, "Invalid quantile error expected") require.Error(t, err, "Invalid quantile error expected")
@ -69,7 +69,7 @@ func TestStdoutInvalidQuantile(t *testing.T) {
func TestStdoutTimestamp(t *testing.T) { func TestStdoutTimestamp(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
exporter, err := stdout.New(stdout.Options{ exporter, err := stdout.NewRawExporter(stdout.Options{
Writer: &buf, Writer: &buf,
DoNotPrintTime: false, DoNotPrintTime: false,
}) })

View File

@ -17,29 +17,20 @@ package metric_test
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/api/key"
"go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/api/metric"
"go.opentelemetry.io/otel/exporter/metric/stdout" "go.opentelemetry.io/otel/exporter/metric/stdout"
sdk "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/batcher/defaultkeys"
"go.opentelemetry.io/otel/sdk/metric/controller/push"
"go.opentelemetry.io/otel/sdk/metric/selector/simple"
) )
func ExampleNew() { func ExampleNew() {
selector := simple.NewWithInexpensiveMeasure() pusher, err := stdout.NewExportPipeline(stdout.Options{
exporter, err := stdout.New(stdout.Options{
PrettyPrint: true, PrettyPrint: true,
DoNotPrintTime: true, // This makes the output deterministic DoNotPrintTime: true, // This makes the output deterministic
}) })
if err != nil { if err != nil {
panic(fmt.Sprintln("Could not initialize stdout exporter:", err)) panic(fmt.Sprintln("Could not initialize stdout exporter:", err))
} }
batcher := defaultkeys.New(selector, sdk.NewDefaultLabelEncoder(), true)
pusher := push.New(batcher, exporter, time.Second)
pusher.Start()
defer pusher.Stop() defer pusher.Stop()
ctx := context.Background() ctx := context.Background()