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

feat(autometer): add WithLookupExporter option #139

Merged
merged 2 commits into from
Sep 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 19 additions & 8 deletions autometer/autometer.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,6 @@ func getEnvOr(name, def string) string {

func noopHandler(_ context.Context) error { return nil }

type stoppableReader interface {
sdkmetric.Reader
Shutdown(ctx context.Context) error
}

// ShutdownFunc is a function that shuts down the MeterProvider.
type ShutdownFunc func(ctx context.Context) error

Expand All @@ -81,13 +76,14 @@ func NewMeterProvider(ctx context.Context, options ...Option) (
metricOptions = append(metricOptions, sdkmetric.WithResource(cfg.res))
}

ret := func(r stoppableReader) (metric.MeterProvider, func(ctx context.Context) error, error) {
ret := func(r sdkmetric.Reader) (metric.MeterProvider, func(ctx context.Context) error, error) {
metricOptions = append(metricOptions, sdkmetric.WithReader(r))
return sdkmetric.NewMeterProvider(metricOptions...), r.Shutdown, nil
}

// Metrics exporter.
switch exporter := strings.TrimSpace(getEnvOr("OTEL_METRICS_EXPORTER", expOTLP)); exporter {
exporter := strings.TrimSpace(getEnvOr("OTEL_METRICS_EXPORTER", expOTLP))
switch exporter {
case expPrometheus:
lg.Debug("Using Prometheus metrics exporter")
reg := cfg.prom
Expand Down Expand Up @@ -154,6 +150,21 @@ func NewMeterProvider(ctx context.Context, options ...Option) (
lg.Debug("Using no-op metrics exporter")
return noop.NewMeterProvider(), noopHandler, nil
default:
return nil, nil, errors.Errorf("unsupported OTEL_METRICS_EXPORTER %q", exporter)
lookup := cfg.lookup
if lookup == nil {
break
}
lg.Debug("Looking for metrics exporter", zap.String("exporter", exporter))
exp, ok, err := lookup(ctx, exporter)
if err != nil {
return nil, nil, errors.Wrap(err, exporter)
}
if !ok {
break
}

lg.Debug("Using user-defined metrics exporter", zap.String("exporter", exporter))
return ret(exp)
}
return nil, nil, errors.Errorf("unsupported OTEL_METRICS_EXPORTER %q", exporter)
}
14 changes: 14 additions & 0 deletions autometer/config.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package autometer

import (
"context"
"io"

"github.com/prometheus/client_golang/prometheus"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
)

// config contains configuration options for a MeterProvider.
type config struct {
res *resource.Resource
writer io.Writer
lookup LookupExporter

prom prometheus.Registerer
promCallback func(reg *prometheus.Registry)
Expand Down Expand Up @@ -72,3 +75,14 @@ func WithWriter(out io.Writer) Option {
return conf
})
}

// LookupExporter creates exporter by name.
type LookupExporter func(ctx context.Context, name string) (sdkmetric.Reader, bool, error)

// WithLookupExporter sets exporter lookup function.
func WithLookupExporter(lookup LookupExporter) Option {
return optionFunc(func(conf config) config {
conf.lookup = lookup
return conf
})
}
47 changes: 47 additions & 0 deletions autometer/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package autometer

import (
"context"
"errors"
"fmt"
"testing"

"github.com/stretchr/testify/require"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
)

func TestWithLookupExporter(t *testing.T) {
var lookup LookupExporter = func(ctx context.Context, name string) (sdkmetric.Reader, bool, error) {
switch name {
case "return_something":
r := sdkmetric.NewManualReader()
return r, true, nil
case "return_error":
return nil, false, errors.New("test error")
default:
return nil, false, nil
}
}

for i, tt := range []struct {
name string
containsErr string
}{
{"return_something", ``},
{"return_error", `test error`},
{"return_not_exist", `unsupported OTEL_METRICS_EXPORTER "return_not_exist"`},
} {
tt := tt
t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
t.Setenv("OTEL_METRICS_EXPORTER", tt.name)
ctx := context.Background()

_, _, err := NewMeterProvider(ctx, WithLookupExporter(lookup))
if tt.containsErr != "" {
require.ErrorContains(t, err, tt.containsErr)
return
}
require.NoError(t, err)
})
}
}
21 changes: 19 additions & 2 deletions autotracer/autotracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ func NewTracerProvider(ctx context.Context, options ...Option) (
traceOptions = append(traceOptions, sdktrace.WithBatcher(e))
return sdktrace.NewTracerProvider(traceOptions...), e.Shutdown, nil
}
switch exporter := strings.TrimSpace(getEnvOr("OTEL_TRACES_EXPORTER", expOTLP)); exporter {

exporter := strings.TrimSpace(getEnvOr("OTEL_TRACES_EXPORTER", expOTLP))
switch exporter {
case expOTLP:
proto := os.Getenv("OTEL_EXPORTER_OTLP_PROTOCOL")
if proto == "" {
Expand Down Expand Up @@ -112,6 +114,21 @@ func NewTracerProvider(ctx context.Context, options ...Option) (
lg.Debug("Using no-op trace exporter")
return noop.NewTracerProvider(), nop, nil
default:
return nil, nil, errors.Errorf("unsupported OTEL_TRACES_EXPORTER %q", exporter)
lookup := cfg.lookup
if lookup == nil {
break
}
lg.Debug("Looking for traces exporter", zap.String("exporter", exporter))
exp, ok, err := lookup(ctx, exporter)
if err != nil {
return nil, nil, errors.Wrap(err, exporter)
}
if !ok {
break
}

lg.Debug("Using user-defined traces exporter", zap.String("exporter", exporter))
return ret(exp)
}
return nil, nil, errors.Errorf("unsupported OTEL_TRACES_EXPORTER %q", exporter)
}
14 changes: 14 additions & 0 deletions autotracer/config.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package autotracer

import (
"context"
"io"

"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

// config contains configuration options for a MeterProvider.
type config struct {
res *resource.Resource
writer io.Writer
lookup LookupExporter
}

// newConfig returns a config configured with options.
Expand Down Expand Up @@ -54,3 +57,14 @@ func WithWriter(out io.Writer) Option {
return conf
})
}

// LookupExporter creates exporter by name.
type LookupExporter func(ctx context.Context, name string) (sdktrace.SpanExporter, bool, error)

// WithLookupExporter sets exporter lookup function.
func WithLookupExporter(lookup LookupExporter) Option {
return optionFunc(func(conf config) config {
conf.lookup = lookup
return conf
})
}
49 changes: 49 additions & 0 deletions autotracer/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package autotracer

import (
"context"
"errors"
"fmt"
"io"
"testing"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/sdk/trace"
)

func TestWithLookupExporter(t *testing.T) {
var lookup LookupExporter = func(ctx context.Context, name string) (trace.SpanExporter, bool, error) {
switch name {
case "return_something":
e, err := stdouttrace.New(stdouttrace.WithWriter(io.Discard))
return e, true, err
case "return_error":
return nil, false, errors.New("test error")
default:
return nil, false, nil
}
}

for i, tt := range []struct {
name string
containsErr string
}{
{"return_something", ``},
{"return_error", `test error`},
{"return_not_exist", `unsupported OTEL_TRACES_EXPORTER "return_not_exist"`},
} {
tt := tt
t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
t.Setenv("OTEL_TRACES_EXPORTER", tt.name)
ctx := context.Background()

_, _, err := NewTracerProvider(ctx, WithLookupExporter(lookup))
if tt.containsErr != "" {
require.ErrorContains(t, err, tt.containsErr)
return
}
require.NoError(t, err)
})
}
}
Loading