Change Options to accept type not pointer (#2558)

* Change trace options to accept type not pointer

Add benchmark to show allocation improvement.

* Update CONTRIBUTING.md guidelines

* Update all Option iface

* Fix grammar in CONTRIBUTING
This commit is contained in:
Tyler Yahn 2022-02-01 13:51:23 -08:00 committed by GitHub
parent e58070a114
commit bd817df68c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 477 additions and 277 deletions

View File

@ -228,11 +228,11 @@ all options to create a configured `config`.
```go
// newConfig returns an appropriately configured config.
func newConfig([]Option) config {
func newConfig(options ...Option) config {
// Set default values for config.
config := config{/* […] */}
for _, option := range options {
option.apply(&config)
config = option.apply(config)
}
// Preform any validation here.
return config
@ -253,7 +253,7 @@ To set the value of the options a `config` contains, a corresponding
```go
type Option interface {
apply(*config)
apply(config) config
}
```
@ -261,6 +261,9 @@ Having `apply` unexported makes sure that it will not be used externally.
Moreover, the interface becomes sealed so the user cannot easily implement
the interface on its own.
The `apply` method should return a modified version of the passed config.
This approach, instead of passing a pointer, is used to prevent the config from being allocated to the heap.
The name of the interface should be prefixed in the same way the
corresponding `config` is (if at all).
@ -283,8 +286,9 @@ func With*(…) Option { … }
```go
type defaultFalseOption bool
func (o defaultFalseOption) apply(c *config) {
func (o defaultFalseOption) apply(c config) config {
c.Bool = bool(o)
return c
}
// WithOption sets a T to have an option included.
@ -296,8 +300,9 @@ func WithOption() Option {
```go
type defaultTrueOption bool
func (o defaultTrueOption) apply(c *config) {
func (o defaultTrueOption) apply(c config) config {
c.Bool = bool(o)
return c
}
// WithoutOption sets a T to have Bool option excluded.
@ -313,8 +318,9 @@ type myTypeOption struct {
MyType MyType
}
func (o myTypeOption) apply(c *config) {
func (o myTypeOption) apply(c config) config {
c.MyType = o.MyType
return c
}
// WithMyType sets T to have include MyType.
@ -326,16 +332,17 @@ func WithMyType(t MyType) Option {
##### Functional Options
```go
type optionFunc func(*config)
type optionFunc func(config) config
func (fn optionFunc) apply(c *config) {
fn(c)
func (fn optionFunc) apply(c config) config {
return fn(c)
}
// WithMyType sets t as MyType.
func WithMyType(t MyType) Option {
return optionFunc(func(c *config) {
return optionFunc(func(c config) config {
c.MyType = t
return c
})
}
```
@ -370,12 +377,12 @@ type config struct {
// DogOption apply Dog specific options.
type DogOption interface {
applyDog(*config)
applyDog(config) config
}
// BirdOption apply Bird specific options.
type BirdOption interface {
applyBird(*config)
applyBird(config) config
}
// Option apply options for all animals.
@ -385,17 +392,36 @@ type Option interface {
}
type weightOption float64
func (o weightOption) applyDog(c *config) { c.Weight = float64(o) }
func (o weightOption) applyBird(c *config) { c.Weight = float64(o) }
func WithWeight(w float64) Option { return weightOption(w) }
func (o weightOption) applyDog(c config) config {
c.Weight = float64(o)
return c
}
func (o weightOption) applyBird(c config) config {
c.Weight = float64(o)
return c
}
func WithWeight(w float64) Option { return weightOption(w) }
type furColorOption string
func (o furColorOption) applyDog(c *config) { c.Color = string(o) }
func WithFurColor(c string) DogOption { return furColorOption(c) }
func (o furColorOption) applyDog(c config) config {
c.Color = string(o)
return c
}
func WithFurColor(c string) DogOption { return furColorOption(c) }
type maxAltitudeOption float64
func (o maxAltitudeOption) applyBird(c *config) { c.MaxAltitude = float64(o) }
func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) }
func (o maxAltitudeOption) applyBird(c config) config {
c.MaxAltitude = float64(o)
return c
}
func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) }
func NewDog(name string, o ...DogOption) Dog {…}
func NewBird(name string, o ...BirdOption) Bird {…}

View File

@ -55,7 +55,7 @@ func (fn endpointOptionFunc) newBatchUploader() (batchUploader, error) {
// will be used if neither are provided.
func WithAgentEndpoint(options ...AgentEndpointOption) EndpointOption {
return endpointOptionFunc(func() (batchUploader, error) {
cfg := &agentEndpointConfig{
cfg := agentEndpointConfig{
agentClientUDPParams{
AttemptReconnecting: true,
Host: envOr(envAgentHost, "localhost"),
@ -63,7 +63,7 @@ func WithAgentEndpoint(options ...AgentEndpointOption) EndpointOption {
},
}
for _, opt := range options {
opt.apply(cfg)
cfg = opt.apply(cfg)
}
client, err := newAgentClientUDP(cfg.agentClientUDPParams)
@ -76,17 +76,17 @@ func WithAgentEndpoint(options ...AgentEndpointOption) EndpointOption {
}
type AgentEndpointOption interface {
apply(*agentEndpointConfig)
apply(agentEndpointConfig) agentEndpointConfig
}
type agentEndpointConfig struct {
agentClientUDPParams
}
type agentEndpointOptionFunc func(*agentEndpointConfig)
type agentEndpointOptionFunc func(agentEndpointConfig) agentEndpointConfig
func (fn agentEndpointOptionFunc) apply(cfg *agentEndpointConfig) {
fn(cfg)
func (fn agentEndpointOptionFunc) apply(cfg agentEndpointConfig) agentEndpointConfig {
return fn(cfg)
}
// WithAgentHost sets a host to be used in the agent client endpoint.
@ -94,8 +94,9 @@ func (fn agentEndpointOptionFunc) apply(cfg *agentEndpointConfig) {
// OTEL_EXPORTER_JAEGER_AGENT_HOST environment variable.
// If this option is not passed and the env var is not set, "localhost" will be used by default.
func WithAgentHost(host string) AgentEndpointOption {
return agentEndpointOptionFunc(func(o *agentEndpointConfig) {
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
o.Host = host
return o
})
}
@ -104,36 +105,41 @@ func WithAgentHost(host string) AgentEndpointOption {
// OTEL_EXPORTER_JAEGER_AGENT_PORT environment variable.
// If this option is not passed and the env var is not set, "6831" will be used by default.
func WithAgentPort(port string) AgentEndpointOption {
return agentEndpointOptionFunc(func(o *agentEndpointConfig) {
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
o.Port = port
return o
})
}
// WithLogger sets a logger to be used by agent client.
func WithLogger(logger *log.Logger) AgentEndpointOption {
return agentEndpointOptionFunc(func(o *agentEndpointConfig) {
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
o.Logger = logger
return o
})
}
// WithDisableAttemptReconnecting sets option to disable reconnecting udp client.
func WithDisableAttemptReconnecting() AgentEndpointOption {
return agentEndpointOptionFunc(func(o *agentEndpointConfig) {
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
o.AttemptReconnecting = false
return o
})
}
// WithAttemptReconnectingInterval sets the interval between attempts to re resolve agent endpoint.
func WithAttemptReconnectingInterval(interval time.Duration) AgentEndpointOption {
return agentEndpointOptionFunc(func(o *agentEndpointConfig) {
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
o.AttemptReconnectInterval = interval
return o
})
}
// WithMaxPacketSize sets the maximum UDP packet size for transport to the Jaeger agent.
func WithMaxPacketSize(size int) AgentEndpointOption {
return agentEndpointOptionFunc(func(o *agentEndpointConfig) {
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
o.MaxPacketSize = size
return o
})
}
@ -149,7 +155,7 @@ func WithMaxPacketSize(size int) AgentEndpointOption {
// If neither values are provided for the username or the password, they will not be set since there is no default.
func WithCollectorEndpoint(options ...CollectorEndpointOption) EndpointOption {
return endpointOptionFunc(func() (batchUploader, error) {
cfg := &collectorEndpointConfig{
cfg := collectorEndpointConfig{
endpoint: envOr(envEndpoint, "http://localhost:14268/api/traces"),
username: envOr(envUser, ""),
password: envOr(envPassword, ""),
@ -157,7 +163,7 @@ func WithCollectorEndpoint(options ...CollectorEndpointOption) EndpointOption {
}
for _, opt := range options {
opt.apply(cfg)
cfg = opt.apply(cfg)
}
return &collectorUploader{
@ -170,7 +176,7 @@ func WithCollectorEndpoint(options ...CollectorEndpointOption) EndpointOption {
}
type CollectorEndpointOption interface {
apply(*collectorEndpointConfig)
apply(collectorEndpointConfig) collectorEndpointConfig
}
type collectorEndpointConfig struct {
@ -187,10 +193,10 @@ type collectorEndpointConfig struct {
httpClient *http.Client
}
type collectorEndpointOptionFunc func(*collectorEndpointConfig)
type collectorEndpointOptionFunc func(collectorEndpointConfig) collectorEndpointConfig
func (fn collectorEndpointOptionFunc) apply(cfg *collectorEndpointConfig) {
fn(cfg)
func (fn collectorEndpointOptionFunc) apply(cfg collectorEndpointConfig) collectorEndpointConfig {
return fn(cfg)
}
// WithEndpoint is the URL for the Jaeger collector that spans are sent to.
@ -199,8 +205,9 @@ func (fn collectorEndpointOptionFunc) apply(cfg *collectorEndpointConfig) {
// If this option is not passed and the environment variable is not set,
// "http://localhost:14268/api/traces" will be used by default.
func WithEndpoint(endpoint string) CollectorEndpointOption {
return collectorEndpointOptionFunc(func(o *collectorEndpointConfig) {
return collectorEndpointOptionFunc(func(o collectorEndpointConfig) collectorEndpointConfig {
o.endpoint = endpoint
return o
})
}
@ -209,8 +216,9 @@ func WithEndpoint(endpoint string) CollectorEndpointOption {
// OTEL_EXPORTER_JAEGER_USER environment variable.
// If this option is not passed and the environment variable is not set, no username will be set.
func WithUsername(username string) CollectorEndpointOption {
return collectorEndpointOptionFunc(func(o *collectorEndpointConfig) {
return collectorEndpointOptionFunc(func(o collectorEndpointConfig) collectorEndpointConfig {
o.username = username
return o
})
}
@ -219,15 +227,17 @@ func WithUsername(username string) CollectorEndpointOption {
// OTEL_EXPORTER_JAEGER_PASSWORD environment variable.
// If this option is not passed and the environment variable is not set, no password will be set.
func WithPassword(password string) CollectorEndpointOption {
return collectorEndpointOptionFunc(func(o *collectorEndpointConfig) {
return collectorEndpointOptionFunc(func(o collectorEndpointConfig) collectorEndpointConfig {
o.password = password
return o
})
}
// WithHTTPClient sets the http client to be used to make request to the collector endpoint.
func WithHTTPClient(client *http.Client) CollectorEndpointOption {
return collectorEndpointOptionFunc(func(o *collectorEndpointConfig) {
return collectorEndpointOptionFunc(func(o collectorEndpointConfig) collectorEndpointConfig {
o.httpClient = client
return o
})
}

View File

@ -120,7 +120,7 @@ func NewUnstarted(client Client, opts ...Option) *Exporter {
}
for _, opt := range opts {
opt.apply(&cfg)
cfg = opt.apply(cfg)
}
e := &Exporter{

View File

@ -33,12 +33,12 @@ var DefaultEnvOptionsReader = EnvOptionsReader{
ReadFile: ioutil.ReadFile,
}
func ApplyGRPCEnvConfigs(cfg *Config) {
DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg)
func ApplyGRPCEnvConfigs(cfg Config) Config {
return DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg)
}
func ApplyHTTPEnvConfigs(cfg *Config) {
DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg)
func ApplyHTTPEnvConfigs(cfg Config) Config {
return DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg)
}
type EnvOptionsReader struct {
@ -46,18 +46,20 @@ type EnvOptionsReader struct {
ReadFile func(filename string) ([]byte, error)
}
func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg *Config) {
func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg Config) Config {
opts := e.GetOptionsFromEnv()
for _, opt := range opts {
opt.ApplyHTTPOption(cfg)
cfg = opt.ApplyHTTPOption(cfg)
}
return cfg
}
func (e *EnvOptionsReader) ApplyGRPCEnvConfigs(cfg *Config) {
func (e *EnvOptionsReader) ApplyGRPCEnvConfigs(cfg Config) Config {
opts := e.GetOptionsFromEnv()
for _, opt := range opts {
opt.ApplyGRPCOption(cfg)
cfg = opt.ApplyGRPCOption(cfg)
}
return cfg
}
func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
@ -74,7 +76,7 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
} else {
opts = append(opts, WithSecure())
}
opts = append(opts, newSplitOption(func(cfg *Config) {
opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Metrics.Endpoint = u.Host
// For endpoint URLs for OTLP/HTTP per-signal variables, the
// URL MUST be used as-is without any modification. The only
@ -85,10 +87,12 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
path = "/"
}
cfg.Metrics.URLPath = path
}, func(cfg *Config) {
return cfg
}, func(cfg Config) Config {
// For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry.
cfg.Metrics.Endpoint = path.Join(u.Host, u.Path)
return cfg
}))
}
} else if v, ok = e.getEnvValue("ENDPOINT"); ok {
@ -101,16 +105,18 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
} else {
opts = append(opts, WithSecure())
}
opts = append(opts, newSplitOption(func(cfg *Config) {
opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Metrics.Endpoint = u.Host
// For OTLP/HTTP endpoint URLs without a per-signal
// configuration, the passed endpoint is used as a base URL
// and the signals are sent to these paths relative to that.
cfg.Metrics.URLPath = path.Join(u.Path, DefaultMetricsPath)
}, func(cfg *Config) {
return cfg
}, func(cfg Config) Config {
// For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry.
cfg.Metrics.Endpoint = path.Join(u.Host, u.Path)
return cfg
}))
}
}

View File

@ -90,9 +90,9 @@ func NewDefaultConfig() Config {
// any unset setting using the default gRPC config values.
func NewGRPCConfig(opts ...GRPCOption) Config {
cfg := NewDefaultConfig()
ApplyGRPCEnvConfigs(&cfg)
cfg = ApplyGRPCEnvConfigs(cfg)
for _, opt := range opts {
opt.ApplyGRPCOption(&cfg)
cfg = opt.ApplyGRPCOption(cfg)
}
if cfg.ServiceConfig != "" {
@ -129,8 +129,8 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
type (
// GenericOption applies an option to the HTTP or gRPC driver.
GenericOption interface {
ApplyHTTPOption(*Config)
ApplyGRPCOption(*Config)
ApplyHTTPOption(Config) Config
ApplyGRPCOption(Config) Config
// A private method to prevent users implementing the
// interface and so future additions to it will not
@ -140,7 +140,7 @@ type (
// HTTPOption applies an option to the HTTP driver.
HTTPOption interface {
ApplyHTTPOption(*Config)
ApplyHTTPOption(Config) Config
// A private method to prevent users implementing the
// interface and so future additions to it will not
@ -150,7 +150,7 @@ type (
// GRPCOption applies an option to the gRPC driver.
GRPCOption interface {
ApplyGRPCOption(*Config)
ApplyGRPCOption(Config) Config
// A private method to prevent users implementing the
// interface and so future additions to it will not
@ -162,128 +162,138 @@ type (
// genericOption is an option that applies the same logic
// for both gRPC and HTTP.
type genericOption struct {
fn func(*Config)
fn func(Config) Config
}
func (g *genericOption) ApplyGRPCOption(cfg *Config) {
g.fn(cfg)
func (g *genericOption) ApplyGRPCOption(cfg Config) Config {
return g.fn(cfg)
}
func (g *genericOption) ApplyHTTPOption(cfg *Config) {
g.fn(cfg)
func (g *genericOption) ApplyHTTPOption(cfg Config) Config {
return g.fn(cfg)
}
func (genericOption) private() {}
func newGenericOption(fn func(cfg *Config)) GenericOption {
func newGenericOption(fn func(cfg Config) Config) GenericOption {
return &genericOption{fn: fn}
}
// splitOption is an option that applies different logics
// for gRPC and HTTP.
type splitOption struct {
httpFn func(*Config)
grpcFn func(*Config)
httpFn func(Config) Config
grpcFn func(Config) Config
}
func (g *splitOption) ApplyGRPCOption(cfg *Config) {
g.grpcFn(cfg)
func (g *splitOption) ApplyGRPCOption(cfg Config) Config {
return g.grpcFn(cfg)
}
func (g *splitOption) ApplyHTTPOption(cfg *Config) {
g.httpFn(cfg)
func (g *splitOption) ApplyHTTPOption(cfg Config) Config {
return g.httpFn(cfg)
}
func (splitOption) private() {}
func newSplitOption(httpFn func(cfg *Config), grpcFn func(cfg *Config)) GenericOption {
func newSplitOption(httpFn func(cfg Config) Config, grpcFn func(cfg Config) Config) GenericOption {
return &splitOption{httpFn: httpFn, grpcFn: grpcFn}
}
// httpOption is an option that is only applied to the HTTP driver.
type httpOption struct {
fn func(*Config)
fn func(Config) Config
}
func (h *httpOption) ApplyHTTPOption(cfg *Config) {
h.fn(cfg)
func (h *httpOption) ApplyHTTPOption(cfg Config) Config {
return h.fn(cfg)
}
func (httpOption) private() {}
func NewHTTPOption(fn func(cfg *Config)) HTTPOption {
func NewHTTPOption(fn func(cfg Config) Config) HTTPOption {
return &httpOption{fn: fn}
}
// grpcOption is an option that is only applied to the gRPC driver.
type grpcOption struct {
fn func(*Config)
fn func(Config) Config
}
func (h *grpcOption) ApplyGRPCOption(cfg *Config) {
h.fn(cfg)
func (h *grpcOption) ApplyGRPCOption(cfg Config) Config {
return h.fn(cfg)
}
func (grpcOption) private() {}
func NewGRPCOption(fn func(cfg *Config)) GRPCOption {
func NewGRPCOption(fn func(cfg Config) Config) GRPCOption {
return &grpcOption{fn: fn}
}
// Generic Options
func WithEndpoint(endpoint string) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Endpoint = endpoint
return cfg
})
}
func WithCompression(compression Compression) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Compression = compression
return cfg
})
}
func WithURLPath(urlPath string) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Metrics.URLPath = urlPath
return cfg
})
}
func WithRetry(rc retry.Config) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.RetryConfig = rc
return cfg
})
}
func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption {
return newSplitOption(func(cfg *Config) {
return newSplitOption(func(cfg Config) Config {
cfg.Metrics.TLSCfg = tlsCfg.Clone()
}, func(cfg *Config) {
return cfg
}, func(cfg Config) Config {
cfg.Metrics.GRPCCredentials = credentials.NewTLS(tlsCfg)
return cfg
})
}
func WithInsecure() GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Insecure = true
return cfg
})
}
func WithSecure() GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Insecure = false
return cfg
})
}
func WithHeaders(headers map[string]string) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Headers = headers
return cfg
})
}
func WithTimeout(duration time.Duration) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Timeout = duration
return cfg
})
}

View File

@ -387,9 +387,9 @@ func TestConfigs(t *testing.T) {
// Tests Generic options as HTTP Options
cfg := otlpconfig.NewDefaultConfig()
otlpconfig.ApplyHTTPEnvConfigs(&cfg)
cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg)
for _, opt := range tt.opts {
opt.ApplyHTTPOption(&cfg)
cfg = opt.ApplyHTTPOption(cfg)
}
tt.asserts(t, &cfg, false)

View File

@ -18,13 +18,13 @@ import "go.opentelemetry.io/otel/sdk/metric/export/aggregation"
// Option are setting options passed to an Exporter on creation.
type Option interface {
apply(*config)
apply(config) config
}
type exporterOptionFunc func(*config)
type exporterOptionFunc func(config) config
func (fn exporterOptionFunc) apply(cfg *config) {
fn(cfg)
func (fn exporterOptionFunc) apply(cfg config) config {
return fn(cfg)
}
type config struct {
@ -36,7 +36,8 @@ type config struct {
// aggregation). If not specified otherwise, exporter will use a
// cumulative temporality selector.
func WithMetricAggregationTemporalitySelector(selector aggregation.TemporalitySelector) Option {
return exporterOptionFunc(func(cfg *config) {
return exporterOptionFunc(func(cfg config) config {
cfg.temporalitySelector = selector
return cfg
})
}

View File

@ -28,7 +28,7 @@ import (
// Option applies an option to the gRPC driver.
type Option interface {
applyGRPCOption(*otlpconfig.Config)
applyGRPCOption(otlpconfig.Config) otlpconfig.Config
}
func asGRPCOptions(opts []Option) []otlpconfig.GRPCOption {
@ -50,8 +50,8 @@ type wrappedOption struct {
otlpconfig.GRPCOption
}
func (w wrappedOption) applyGRPCOption(cfg *otlpconfig.Config) {
w.ApplyGRPCOption(cfg)
func (w wrappedOption) applyGRPCOption(cfg otlpconfig.Config) otlpconfig.Config {
return w.ApplyGRPCOption(cfg)
}
// WithInsecure disables client transport security for the exporter's gRPC
@ -77,8 +77,9 @@ func WithEndpoint(endpoint string) Option {
//
// This option has no effect if WithGRPCConn is used.
func WithReconnectionPeriod(rp time.Duration) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.ReconnectionPeriod = rp
return cfg
})}
}
@ -117,8 +118,9 @@ func WithHeaders(headers map[string]string) Option {
//
// This option has no effect if WithGRPCConn is used.
func WithTLSCredentials(creds credentials.TransportCredentials) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.Metrics.GRPCCredentials = creds
return cfg
})}
}
@ -126,8 +128,9 @@ func WithTLSCredentials(creds credentials.TransportCredentials) Option {
//
// This option has no effect if WithGRPCConn is used.
func WithServiceConfig(serviceConfig string) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.ServiceConfig = serviceConfig
return cfg
})}
}
@ -138,8 +141,9 @@ func WithServiceConfig(serviceConfig string) Option {
//
// This option has no effect if WithGRPCConn is used.
func WithDialOption(opts ...grpc.DialOption) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.DialOptions = opts
return cfg
})}
}
@ -152,8 +156,9 @@ func WithDialOption(opts ...grpc.DialOption) Option {
// It is the callers responsibility to close the passed conn. The client
// Shutdown method will not close this connection.
func WithGRPCConn(conn *grpc.ClientConn) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.GRPCConn = conn
return cfg
})}
}

View File

@ -78,9 +78,9 @@ type client struct {
// NewClient creates a new HTTP metric client.
func NewClient(opts ...Option) otlpmetric.Client {
cfg := otlpconfig.NewDefaultConfig()
otlpconfig.ApplyHTTPEnvConfigs(&cfg)
cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg)
for _, opt := range opts {
opt.applyHTTPOption(&cfg)
cfg = opt.applyHTTPOption(cfg)
}
for pathPtr, defaultPath := range map[*string]string{

View File

@ -37,7 +37,7 @@ const (
// Option applies an option to the HTTP client.
type Option interface {
applyHTTPOption(*otlpconfig.Config)
applyHTTPOption(otlpconfig.Config) otlpconfig.Config
}
// RetryConfig defines configuration for retrying batches in case of export
@ -48,8 +48,8 @@ type wrappedOption struct {
otlpconfig.HTTPOption
}
func (w wrappedOption) applyHTTPOption(cfg *otlpconfig.Config) {
w.ApplyHTTPOption(cfg)
func (w wrappedOption) applyHTTPOption(cfg otlpconfig.Config) otlpconfig.Config {
return w.ApplyHTTPOption(cfg)
}
// WithEndpoint allows one to set the address of the collector
@ -83,7 +83,7 @@ func WithMaxAttempts(maxAttempts int) Option {
maxAttempts = 5
}
return wrappedOption{
otlpconfig.NewHTTPOption(func(cfg *otlpconfig.Config) {
otlpconfig.NewHTTPOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.RetryConfig.Enabled = true
var (
@ -104,7 +104,7 @@ func WithMaxAttempts(maxAttempts int) Option {
attempts := int64(maxE+init) / int64(maxI)
if int64(maxAttempts) == attempts {
return
return cfg
}
maxE = time.Duration(int64(maxAttempts)*int64(maxI)) - init
@ -112,6 +112,8 @@ func WithMaxAttempts(maxAttempts int) Option {
cfg.RetryConfig.InitialInterval = init
cfg.RetryConfig.MaxInterval = maxI
cfg.RetryConfig.MaxElapsedTime = maxE
return cfg
}),
}
}
@ -126,7 +128,7 @@ func WithBackoff(duration time.Duration) Option {
duration = 300 * time.Millisecond
}
return wrappedOption{
otlpconfig.NewHTTPOption(func(cfg *otlpconfig.Config) {
otlpconfig.NewHTTPOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.RetryConfig.Enabled = true
cfg.RetryConfig.MaxInterval = duration
if cfg.RetryConfig.InitialInterval == 0 {
@ -135,6 +137,7 @@ func WithBackoff(duration time.Duration) Option {
if cfg.RetryConfig.MaxElapsedTime == 0 {
cfg.RetryConfig.MaxElapsedTime = retry.DefaultConfig.MaxElapsedTime
}
return cfg
}),
}
}

View File

@ -33,12 +33,12 @@ var DefaultEnvOptionsReader = EnvOptionsReader{
ReadFile: ioutil.ReadFile,
}
func ApplyGRPCEnvConfigs(cfg *Config) {
DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg)
func ApplyGRPCEnvConfigs(cfg Config) Config {
return DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg)
}
func ApplyHTTPEnvConfigs(cfg *Config) {
DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg)
func ApplyHTTPEnvConfigs(cfg Config) Config {
return DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg)
}
type EnvOptionsReader struct {
@ -46,18 +46,20 @@ type EnvOptionsReader struct {
ReadFile func(filename string) ([]byte, error)
}
func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg *Config) {
func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg Config) Config {
opts := e.GetOptionsFromEnv()
for _, opt := range opts {
opt.ApplyHTTPOption(cfg)
cfg = opt.ApplyHTTPOption(cfg)
}
return cfg
}
func (e *EnvOptionsReader) ApplyGRPCEnvConfigs(cfg *Config) {
func (e *EnvOptionsReader) ApplyGRPCEnvConfigs(cfg Config) Config {
opts := e.GetOptionsFromEnv()
for _, opt := range opts {
opt.ApplyGRPCOption(cfg)
cfg = opt.ApplyGRPCOption(cfg)
}
return cfg
}
func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
@ -74,7 +76,7 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
} else {
opts = append(opts, WithSecure())
}
opts = append(opts, newSplitOption(func(cfg *Config) {
opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Traces.Endpoint = u.Host
// For endpoint URLs for OTLP/HTTP per-signal variables, the
// URL MUST be used as-is without any modification. The only
@ -85,10 +87,12 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
path = "/"
}
cfg.Traces.URLPath = path
}, func(cfg *Config) {
return cfg
}, func(cfg Config) Config {
// For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry.
cfg.Traces.Endpoint = path.Join(u.Host, u.Path)
return cfg
}))
}
} else if v, ok = e.getEnvValue("ENDPOINT"); ok {
@ -101,16 +105,18 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
} else {
opts = append(opts, WithSecure())
}
opts = append(opts, newSplitOption(func(cfg *Config) {
opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Traces.Endpoint = u.Host
// For OTLP/HTTP endpoint URLs without a per-signal
// configuration, the passed endpoint is used as a base URL
// and the signals are sent to these paths relative to that.
cfg.Traces.URLPath = path.Join(u.Path, DefaultTracesPath)
}, func(cfg *Config) {
return cfg
}, func(cfg Config) Config {
// For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry.
cfg.Traces.Endpoint = path.Join(u.Host, u.Path)
return cfg
}))
}
}

View File

@ -83,9 +83,9 @@ func NewDefaultConfig() Config {
// any unset setting using the default gRPC config values.
func NewGRPCConfig(opts ...GRPCOption) Config {
cfg := NewDefaultConfig()
ApplyGRPCEnvConfigs(&cfg)
cfg = ApplyGRPCEnvConfigs(cfg)
for _, opt := range opts {
opt.ApplyGRPCOption(&cfg)
cfg = opt.ApplyGRPCOption(cfg)
}
if cfg.ServiceConfig != "" {
@ -122,8 +122,8 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
type (
// GenericOption applies an option to the HTTP or gRPC driver.
GenericOption interface {
ApplyHTTPOption(*Config)
ApplyGRPCOption(*Config)
ApplyHTTPOption(Config) Config
ApplyGRPCOption(Config) Config
// A private method to prevent users implementing the
// interface and so future additions to it will not
@ -133,7 +133,7 @@ type (
// HTTPOption applies an option to the HTTP driver.
HTTPOption interface {
ApplyHTTPOption(*Config)
ApplyHTTPOption(Config) Config
// A private method to prevent users implementing the
// interface and so future additions to it will not
@ -143,7 +143,7 @@ type (
// GRPCOption applies an option to the gRPC driver.
GRPCOption interface {
ApplyGRPCOption(*Config)
ApplyGRPCOption(Config) Config
// A private method to prevent users implementing the
// interface and so future additions to it will not
@ -155,128 +155,138 @@ type (
// genericOption is an option that applies the same logic
// for both gRPC and HTTP.
type genericOption struct {
fn func(*Config)
fn func(Config) Config
}
func (g *genericOption) ApplyGRPCOption(cfg *Config) {
g.fn(cfg)
func (g *genericOption) ApplyGRPCOption(cfg Config) Config {
return g.fn(cfg)
}
func (g *genericOption) ApplyHTTPOption(cfg *Config) {
g.fn(cfg)
func (g *genericOption) ApplyHTTPOption(cfg Config) Config {
return g.fn(cfg)
}
func (genericOption) private() {}
func newGenericOption(fn func(cfg *Config)) GenericOption {
func newGenericOption(fn func(cfg Config) Config) GenericOption {
return &genericOption{fn: fn}
}
// splitOption is an option that applies different logics
// for gRPC and HTTP.
type splitOption struct {
httpFn func(*Config)
grpcFn func(*Config)
httpFn func(Config) Config
grpcFn func(Config) Config
}
func (g *splitOption) ApplyGRPCOption(cfg *Config) {
g.grpcFn(cfg)
func (g *splitOption) ApplyGRPCOption(cfg Config) Config {
return g.grpcFn(cfg)
}
func (g *splitOption) ApplyHTTPOption(cfg *Config) {
g.httpFn(cfg)
func (g *splitOption) ApplyHTTPOption(cfg Config) Config {
return g.httpFn(cfg)
}
func (splitOption) private() {}
func newSplitOption(httpFn func(cfg *Config), grpcFn func(cfg *Config)) GenericOption {
func newSplitOption(httpFn func(cfg Config) Config, grpcFn func(cfg Config) Config) GenericOption {
return &splitOption{httpFn: httpFn, grpcFn: grpcFn}
}
// httpOption is an option that is only applied to the HTTP driver.
type httpOption struct {
fn func(*Config)
fn func(Config) Config
}
func (h *httpOption) ApplyHTTPOption(cfg *Config) {
h.fn(cfg)
func (h *httpOption) ApplyHTTPOption(cfg Config) Config {
return h.fn(cfg)
}
func (httpOption) private() {}
func NewHTTPOption(fn func(cfg *Config)) HTTPOption {
func NewHTTPOption(fn func(cfg Config) Config) HTTPOption {
return &httpOption{fn: fn}
}
// grpcOption is an option that is only applied to the gRPC driver.
type grpcOption struct {
fn func(*Config)
fn func(Config) Config
}
func (h *grpcOption) ApplyGRPCOption(cfg *Config) {
h.fn(cfg)
func (h *grpcOption) ApplyGRPCOption(cfg Config) Config {
return h.fn(cfg)
}
func (grpcOption) private() {}
func NewGRPCOption(fn func(cfg *Config)) GRPCOption {
func NewGRPCOption(fn func(cfg Config) Config) GRPCOption {
return &grpcOption{fn: fn}
}
// Generic Options
func WithEndpoint(endpoint string) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Traces.Endpoint = endpoint
return cfg
})
}
func WithCompression(compression Compression) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Traces.Compression = compression
return cfg
})
}
func WithURLPath(urlPath string) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Traces.URLPath = urlPath
return cfg
})
}
func WithRetry(rc retry.Config) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.RetryConfig = rc
return cfg
})
}
func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption {
return newSplitOption(func(cfg *Config) {
return newSplitOption(func(cfg Config) Config {
cfg.Traces.TLSCfg = tlsCfg.Clone()
}, func(cfg *Config) {
return cfg
}, func(cfg Config) Config {
cfg.Traces.GRPCCredentials = credentials.NewTLS(tlsCfg)
return cfg
})
}
func WithInsecure() GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Traces.Insecure = true
return cfg
})
}
func WithSecure() GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Traces.Insecure = false
return cfg
})
}
func WithHeaders(headers map[string]string) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Traces.Headers = headers
return cfg
})
}
func WithTimeout(duration time.Duration) GenericOption {
return newGenericOption(func(cfg *Config) {
return newGenericOption(func(cfg Config) Config {
cfg.Traces.Timeout = duration
return cfg
})
}

View File

@ -385,9 +385,9 @@ func TestConfigs(t *testing.T) {
// Tests Generic options as HTTP Options
cfg := otlpconfig.NewDefaultConfig()
otlpconfig.ApplyHTTPEnvConfigs(&cfg)
cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg)
for _, opt := range tt.opts {
opt.ApplyHTTPOption(&cfg)
cfg = opt.ApplyHTTPOption(cfg)
}
tt.asserts(t, &cfg, false)

View File

@ -28,7 +28,7 @@ import (
// Option applies an option to the gRPC driver.
type Option interface {
applyGRPCOption(*otlpconfig.Config)
applyGRPCOption(otlpconfig.Config) otlpconfig.Config
}
func asGRPCOptions(opts []Option) []otlpconfig.GRPCOption {
@ -50,8 +50,8 @@ type wrappedOption struct {
otlpconfig.GRPCOption
}
func (w wrappedOption) applyGRPCOption(cfg *otlpconfig.Config) {
w.ApplyGRPCOption(cfg)
func (w wrappedOption) applyGRPCOption(cfg otlpconfig.Config) otlpconfig.Config {
return w.ApplyGRPCOption(cfg)
}
// WithInsecure disables client transport security for the exporter's gRPC
@ -77,8 +77,9 @@ func WithEndpoint(endpoint string) Option {
//
// This option has no effect if WithGRPCConn is used.
func WithReconnectionPeriod(rp time.Duration) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.ReconnectionPeriod = rp
return cfg
})}
}
@ -117,8 +118,9 @@ func WithHeaders(headers map[string]string) Option {
//
// This option has no effect if WithGRPCConn is used.
func WithTLSCredentials(creds credentials.TransportCredentials) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.Traces.GRPCCredentials = creds
return cfg
})}
}
@ -126,8 +128,9 @@ func WithTLSCredentials(creds credentials.TransportCredentials) Option {
//
// This option has no effect if WithGRPCConn is used.
func WithServiceConfig(serviceConfig string) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.ServiceConfig = serviceConfig
return cfg
})}
}
@ -138,8 +141,9 @@ func WithServiceConfig(serviceConfig string) Option {
//
// This option has no effect if WithGRPCConn is used.
func WithDialOption(opts ...grpc.DialOption) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.DialOptions = opts
return cfg
})}
}
@ -152,8 +156,9 @@ func WithDialOption(opts ...grpc.DialOption) Option {
// It is the callers responsibility to close the passed conn. The client
// Shutdown method will not close this connection.
func WithGRPCConn(conn *grpc.ClientConn) Option {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg *otlpconfig.Config) {
return wrappedOption{otlpconfig.NewGRPCOption(func(cfg otlpconfig.Config) otlpconfig.Config {
cfg.GRPCConn = conn
return cfg
})}
}

View File

@ -80,9 +80,9 @@ var _ otlptrace.Client = (*client)(nil)
// NewClient creates a new HTTP trace client.
func NewClient(opts ...Option) otlptrace.Client {
cfg := otlpconfig.NewDefaultConfig()
otlpconfig.ApplyHTTPEnvConfigs(&cfg)
cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg)
for _, opt := range opts {
opt.applyHTTPOption(&cfg)
cfg = opt.applyHTTPOption(cfg)
}
for pathPtr, defaultPath := range map[*string]string{

View File

@ -37,7 +37,7 @@ const (
// Option applies an option to the HTTP client.
type Option interface {
applyHTTPOption(*otlpconfig.Config)
applyHTTPOption(otlpconfig.Config) otlpconfig.Config
}
// RetryConfig defines configuration for retrying batches in case of export
@ -48,8 +48,8 @@ type wrappedOption struct {
otlpconfig.HTTPOption
}
func (w wrappedOption) applyHTTPOption(cfg *otlpconfig.Config) {
w.ApplyHTTPOption(cfg)
func (w wrappedOption) applyHTTPOption(cfg otlpconfig.Config) otlpconfig.Config {
return w.ApplyHTTPOption(cfg)
}
// WithEndpoint allows one to set the address of the collector

View File

@ -54,7 +54,7 @@ func newConfig(options ...Option) (config, error) {
LabelEncoder: defaultLabelEncoder,
}
for _, opt := range options {
opt.apply(&cfg)
cfg = opt.apply(cfg)
}
return cfg, nil
@ -62,7 +62,7 @@ func newConfig(options ...Option) (config, error) {
// Option sets the value of an option for a Config.
type Option interface {
apply(*config)
apply(config) config
}
// WithWriter sets the export stream destination.
@ -74,8 +74,9 @@ type writerOption struct {
W io.Writer
}
func (o writerOption) apply(cfg *config) {
func (o writerOption) apply(cfg config) config {
cfg.Writer = o.W
return cfg
}
// WithPrettyPrint sets the export stream format to use JSON.
@ -85,8 +86,9 @@ func WithPrettyPrint() Option {
type prettyPrintOption bool
func (o prettyPrintOption) apply(cfg *config) {
func (o prettyPrintOption) apply(cfg config) config {
cfg.PrettyPrint = bool(o)
return cfg
}
// WithoutTimestamps sets the export stream to not include timestamps.
@ -96,8 +98,9 @@ func WithoutTimestamps() Option {
type timestampsOption bool
func (o timestampsOption) apply(cfg *config) {
func (o timestampsOption) apply(cfg config) config {
cfg.Timestamps = bool(o)
return cfg
}
// WithLabelEncoder sets the label encoder used in export.
@ -109,6 +112,7 @@ type labelEncoderOption struct {
LabelEncoder attribute.Encoder
}
func (o labelEncoderOption) apply(cfg *config) {
func (o labelEncoderOption) apply(cfg config) config {
cfg.LabelEncoder = o.LabelEncoder
return cfg
}

View File

@ -47,7 +47,7 @@ func newConfig(options ...Option) (config, error) {
Timestamps: defaultTimestamps,
}
for _, opt := range options {
opt.apply(&cfg)
cfg = opt.apply(cfg)
}
return cfg, nil
@ -55,7 +55,7 @@ func newConfig(options ...Option) (config, error) {
// Option sets the value of an option for a Config.
type Option interface {
apply(*config)
apply(config) config
}
// WithWriter sets the export stream destination.
@ -67,8 +67,9 @@ type writerOption struct {
W io.Writer
}
func (o writerOption) apply(cfg *config) {
func (o writerOption) apply(cfg config) config {
cfg.Writer = o.W
return cfg
}
// WithPrettyPrint sets the export stream format to use JSON.
@ -78,8 +79,9 @@ func WithPrettyPrint() Option {
type prettyPrintOption bool
func (o prettyPrintOption) apply(cfg *config) {
func (o prettyPrintOption) apply(cfg config) config {
cfg.PrettyPrint = bool(o)
return cfg
}
// WithoutTimestamps sets the export stream to not include timestamps.
@ -89,6 +91,7 @@ func WithoutTimestamps() Option {
type timestampsOption bool
func (o timestampsOption) apply(cfg *config) {
func (o timestampsOption) apply(cfg config) config {
cfg.Timestamps = bool(o)
return cfg
}

View File

@ -55,26 +55,28 @@ type config struct {
// Option defines a function that configures the exporter.
type Option interface {
apply(*config)
apply(config) config
}
type optionFunc func(*config)
type optionFunc func(config) config
func (fn optionFunc) apply(cfg *config) {
fn(cfg)
func (fn optionFunc) apply(cfg config) config {
return fn(cfg)
}
// WithLogger configures the exporter to use the passed logger.
func WithLogger(logger *log.Logger) Option {
return optionFunc(func(cfg *config) {
return optionFunc(func(cfg config) config {
cfg.logger = logger
return cfg
})
}
// WithClient configures the exporter to use the passed HTTP client.
func WithClient(client *http.Client) Option {
return optionFunc(func(cfg *config) {
return optionFunc(func(cfg config) config {
cfg.client = client
return cfg
})
}
@ -94,7 +96,7 @@ func New(collectorURL string, opts ...Option) (*Exporter, error) {
cfg := config{}
for _, opt := range opts {
opt.apply(&cfg)
cfg = opt.apply(cfg)
}
if cfg.client == nil {

View File

@ -38,7 +38,7 @@ func (cfg *InstrumentConfig) Unit() unit.Unit {
type InstrumentOption interface {
// ApplyMeter is used to set a InstrumentOption value of a
// InstrumentConfig.
applyInstrument(*InstrumentConfig)
applyInstrument(InstrumentConfig) InstrumentConfig
}
// NewInstrumentConfig creates a new InstrumentConfig
@ -46,28 +46,30 @@ type InstrumentOption interface {
func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig {
var config InstrumentConfig
for _, o := range opts {
o.applyInstrument(&config)
config = o.applyInstrument(config)
}
return config
}
type instrumentOptionFunc func(*InstrumentConfig)
type instrumentOptionFunc func(InstrumentConfig) InstrumentConfig
func (fn instrumentOptionFunc) applyInstrument(cfg *InstrumentConfig) {
fn(cfg)
func (fn instrumentOptionFunc) applyInstrument(cfg InstrumentConfig) InstrumentConfig {
return fn(cfg)
}
// WithDescription applies provided description.
func WithDescription(desc string) InstrumentOption {
return instrumentOptionFunc(func(cfg *InstrumentConfig) {
return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
cfg.description = desc
return cfg
})
}
// WithUnit applies provided unit.
func WithUnit(unit unit.Unit) InstrumentOption {
return instrumentOptionFunc(func(cfg *InstrumentConfig) {
return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
cfg.unit = unit
return cfg
})
}
@ -90,7 +92,7 @@ func (cfg *MeterConfig) SchemaURL() string {
// MeterOption is an interface for applying Meter options.
type MeterOption interface {
// ApplyMeter is used to set a MeterOption value of a MeterConfig.
applyMeter(*MeterConfig)
applyMeter(MeterConfig) MeterConfig
}
// NewMeterConfig creates a new MeterConfig and applies
@ -98,27 +100,29 @@ type MeterOption interface {
func NewMeterConfig(opts ...MeterOption) MeterConfig {
var config MeterConfig
for _, o := range opts {
o.applyMeter(&config)
config = o.applyMeter(config)
}
return config
}
type meterOptionFunc func(*MeterConfig)
type meterOptionFunc func(MeterConfig) MeterConfig
func (fn meterOptionFunc) applyMeter(cfg *MeterConfig) {
fn(cfg)
func (fn meterOptionFunc) applyMeter(cfg MeterConfig) MeterConfig {
return fn(cfg)
}
// WithInstrumentationVersion sets the instrumentation version.
func WithInstrumentationVersion(version string) MeterOption {
return meterOptionFunc(func(config *MeterConfig) {
return meterOptionFunc(func(config MeterConfig) MeterConfig {
config.instrumentationVersion = version
return config
})
}
// WithSchemaURL sets the schema URL.
func WithSchemaURL(schemaURL string) MeterOption {
return meterOptionFunc(func(config *MeterConfig) {
return meterOptionFunc(func(config MeterConfig) MeterConfig {
config.schemaURL = schemaURL
return config
})
}

View File

@ -62,7 +62,7 @@ type config struct {
// Option is the interface that applies the value to a configuration option.
type Option interface {
// apply sets the Option value of a Config.
apply(*config)
apply(config) config
}
// WithResource sets the Resource configuration option of a Config by merging it
@ -73,12 +73,13 @@ func WithResource(r *resource.Resource) Option {
type resourceOption struct{ *resource.Resource }
func (o resourceOption) apply(cfg *config) {
func (o resourceOption) apply(cfg config) config {
res, err := resource.Merge(cfg.Resource, o.Resource)
if err != nil {
otel.Handle(err)
}
cfg.Resource = res
return cfg
}
// WithCollectPeriod sets the CollectPeriod configuration option of a Config.
@ -88,8 +89,9 @@ func WithCollectPeriod(period time.Duration) Option {
type collectPeriodOption time.Duration
func (o collectPeriodOption) apply(cfg *config) {
func (o collectPeriodOption) apply(cfg config) config {
cfg.CollectPeriod = time.Duration(o)
return cfg
}
// WithCollectTimeout sets the CollectTimeout configuration option of a Config.
@ -99,8 +101,9 @@ func WithCollectTimeout(timeout time.Duration) Option {
type collectTimeoutOption time.Duration
func (o collectTimeoutOption) apply(cfg *config) {
func (o collectTimeoutOption) apply(cfg config) config {
cfg.CollectTimeout = time.Duration(o)
return cfg
}
// WithExporter sets the exporter configuration option of a Config.
@ -110,8 +113,9 @@ func WithExporter(exporter export.Exporter) Option {
type exporterOption struct{ exporter export.Exporter }
func (o exporterOption) apply(cfg *config) {
func (o exporterOption) apply(cfg config) config {
cfg.Exporter = o.exporter
return cfg
}
// WithPushTimeout sets the PushTimeout configuration option of a Config.
@ -121,6 +125,7 @@ func WithPushTimeout(timeout time.Duration) Option {
type pushTimeoutOption time.Duration
func (o pushTimeoutOption) apply(cfg *config) {
func (o pushTimeoutOption) apply(cfg config) config {
cfg.PushTimeout = time.Duration(o)
return cfg
}

View File

@ -26,12 +26,12 @@ import (
func TestWithResource(t *testing.T) {
r := resource.NewSchemaless(attribute.String("A", "a"))
c := &config{}
WithResource(r).apply(c)
c := config{}
c = WithResource(r).apply(c)
assert.Equal(t, r.Equivalent(), c.Resource.Equivalent())
// Ensure overwriting works.
c = &config{Resource: &resource.Resource{}}
WithResource(r).apply(c)
c = config{Resource: &resource.Resource{}}
c = WithResource(r).apply(c)
assert.Equal(t, r.Equivalent(), c.Resource.Equivalent())
}

View File

@ -112,13 +112,13 @@ type accumulatorCheckpointer struct {
// and options (including optional exporter) to configure a metric
// export pipeline.
func New(checkpointerFactory export.CheckpointerFactory, opts ...Option) *Controller {
c := &config{
c := config{
CollectPeriod: DefaultPeriod,
CollectTimeout: DefaultPeriod,
PushTimeout: DefaultPeriod,
}
for _, opt := range opts {
opt.apply(c)
c = opt.apply(c)
}
if c.Resource == nil {
c.Resource = resource.Default()

View File

@ -132,7 +132,7 @@ type factory struct {
func NewFactory(aselector export.AggregatorSelector, tselector aggregation.TemporalitySelector, opts ...Option) export.CheckpointerFactory {
var config config
for _, opt := range opts {
opt.applyProcessor(&config)
config = opt.applyProcessor(config)
}
return factory{
aselector: aselector,

View File

@ -24,7 +24,7 @@ type config struct {
}
type Option interface {
applyProcessor(*config)
applyProcessor(config) config
}
// WithMemory sets the memory behavior of a Processor. If this is
@ -37,6 +37,7 @@ func WithMemory(memory bool) Option {
type memoryOption bool
func (m memoryOption) applyProcessor(cfg *config) {
func (m memoryOption) applyProcessor(cfg config) config {
cfg.Memory = bool(m)
return cfg
}

View File

@ -31,7 +31,7 @@ type config struct {
// Option is the interface that applies a configuration option.
type Option interface {
// apply sets the Option value of a config.
apply(*config)
apply(config) config
}
// WithAttributes adds attributes to the configured Resource.
@ -56,8 +56,9 @@ type detectorsOption struct {
detectors []Detector
}
func (o detectorsOption) apply(cfg *config) {
func (o detectorsOption) apply(cfg config) config {
cfg.detectors = append(cfg.detectors, o.detectors...)
return cfg
}
// WithFromEnv adds attributes from environment variables to the configured resource.
@ -82,8 +83,9 @@ func WithSchemaURL(schemaURL string) Option {
type schemaURLOption string
func (o schemaURLOption) apply(cfg *config) {
func (o schemaURLOption) apply(cfg config) config {
cfg.schemaURL = string(o)
return cfg
}
// WithOS adds all the OS attributes to the configured Resource.

View File

@ -48,7 +48,7 @@ var errMergeConflictSchemaURL = errors.New("cannot merge resource due to conflic
func New(ctx context.Context, opts ...Option) (*Resource, error) {
cfg := config{}
for _, opt := range opts {
opt.apply(&cfg)
cfg = opt.apply(cfg)
}
resource, err := Detect(ctx, cfg.detectors...)

View File

@ -93,13 +93,13 @@ var _ trace.TracerProvider = &TracerProvider{}
// The passed opts are used to override these default values and configure the
// returned TracerProvider appropriately.
func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
o := &tracerProviderConfig{}
o := tracerProviderConfig{}
for _, opt := range opts {
opt.apply(o)
o = opt.apply(o)
}
ensureValidTracerProviderConfig(o)
o = ensureValidTracerProviderConfig(o)
tp := &TracerProvider{
namedTracer: make(map[instrumentation.Library]*tracer),
@ -256,13 +256,13 @@ func (p *TracerProvider) Shutdown(ctx context.Context) error {
}
type TracerProviderOption interface {
apply(*tracerProviderConfig)
apply(tracerProviderConfig) tracerProviderConfig
}
type traceProviderOptionFunc func(*tracerProviderConfig)
type traceProviderOptionFunc func(tracerProviderConfig) tracerProviderConfig
func (fn traceProviderOptionFunc) apply(cfg *tracerProviderConfig) {
fn(cfg)
func (fn traceProviderOptionFunc) apply(cfg tracerProviderConfig) tracerProviderConfig {
return fn(cfg)
}
// WithSyncer registers the exporter with the TracerProvider using a
@ -285,8 +285,9 @@ func WithBatcher(e SpanExporter, opts ...BatchSpanProcessorOption) TracerProvide
// WithSpanProcessor registers the SpanProcessor with a TracerProvider.
func WithSpanProcessor(sp SpanProcessor) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) {
return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
cfg.processors = append(cfg.processors, sp)
return cfg
})
}
@ -298,12 +299,13 @@ func WithSpanProcessor(sp SpanProcessor) TracerProviderOption {
// If this option is not used, the TracerProvider will use the
// resource.Default() Resource by default.
func WithResource(r *resource.Resource) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) {
return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
var err error
cfg.resource, err = resource.Merge(resource.Environment(), r)
if err != nil {
otel.Handle(err)
}
return cfg
})
}
@ -315,10 +317,11 @@ func WithResource(r *resource.Resource) TracerProviderOption {
// If this option is not used, the TracerProvider will use a random number
// IDGenerator by default.
func WithIDGenerator(g IDGenerator) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) {
return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
if g != nil {
cfg.idGenerator = g
}
return cfg
})
}
@ -330,10 +333,11 @@ func WithIDGenerator(g IDGenerator) TracerProviderOption {
// If this option is not used, the TracerProvider will use a
// ParentBased(AlwaysSample) Sampler by default.
func WithSampler(s Sampler) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) {
return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
if s != nil {
cfg.sampler = s
}
return cfg
})
}
@ -345,13 +349,14 @@ func WithSampler(s Sampler) TracerProviderOption {
// If this option is not used, the TracerProvider will use the default
// SpanLimits.
func WithSpanLimits(sl SpanLimits) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) {
return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
cfg.spanLimits = sl
return cfg
})
}
// ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid.
func ensureValidTracerProviderConfig(cfg *tracerProviderConfig) {
func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderConfig {
if cfg.sampler == nil {
cfg.sampler = ParentBased(AlwaysSample())
}
@ -362,4 +367,5 @@ func ensureValidTracerProviderConfig(cfg *tracerProviderConfig) {
if cfg.resource == nil {
cfg.resource = resource.Default()
}
return cfg
}

View File

@ -187,7 +187,7 @@ func configureSamplersForParentBased(samplers []ParentBasedSamplerOption) sample
}
for _, so := range samplers {
so.apply(&c)
c = so.apply(c)
}
return c
@ -201,7 +201,7 @@ type samplerConfig struct {
// ParentBasedSamplerOption configures the sampler for a particular sampling case.
type ParentBasedSamplerOption interface {
apply(*samplerConfig)
apply(samplerConfig) samplerConfig
}
// WithRemoteParentSampled sets the sampler for the case of sampled remote parent.
@ -213,8 +213,9 @@ type remoteParentSampledOption struct {
s Sampler
}
func (o remoteParentSampledOption) apply(config *samplerConfig) {
func (o remoteParentSampledOption) apply(config samplerConfig) samplerConfig {
config.remoteParentSampled = o.s
return config
}
// WithRemoteParentNotSampled sets the sampler for the case of remote parent
@ -227,8 +228,9 @@ type remoteParentNotSampledOption struct {
s Sampler
}
func (o remoteParentNotSampledOption) apply(config *samplerConfig) {
func (o remoteParentNotSampledOption) apply(config samplerConfig) samplerConfig {
config.remoteParentNotSampled = o.s
return config
}
// WithLocalParentSampled sets the sampler for the case of sampled local parent.
@ -240,8 +242,9 @@ type localParentSampledOption struct {
s Sampler
}
func (o localParentSampledOption) apply(config *samplerConfig) {
func (o localParentSampledOption) apply(config samplerConfig) samplerConfig {
config.localParentSampled = o.s
return config
}
// WithLocalParentNotSampled sets the sampler for the case of local parent
@ -254,8 +257,9 @@ type localParentNotSampledOption struct {
s Sampler
}
func (o localParentNotSampledOption) apply(config *samplerConfig) {
func (o localParentNotSampledOption) apply(config samplerConfig) samplerConfig {
config.localParentNotSampled = o.s
return config
}
func (pb parentBased) ShouldSample(p SamplingParameters) SamplingResult {

View File

@ -41,20 +41,20 @@ func (t *TracerConfig) SchemaURL() string {
func NewTracerConfig(options ...TracerOption) TracerConfig {
var config TracerConfig
for _, option := range options {
option.apply(&config)
config = option.apply(config)
}
return config
}
// TracerOption applies an option to a TracerConfig.
type TracerOption interface {
apply(*TracerConfig)
apply(TracerConfig) TracerConfig
}
type tracerOptionFunc func(*TracerConfig)
type tracerOptionFunc func(TracerConfig) TracerConfig
func (fn tracerOptionFunc) apply(cfg *TracerConfig) {
fn(cfg)
func (fn tracerOptionFunc) apply(cfg TracerConfig) TracerConfig {
return fn(cfg)
}
// SpanConfig is a group of options for a Span.
@ -106,7 +106,7 @@ func (cfg *SpanConfig) SpanKind() SpanKind {
func NewSpanStartConfig(options ...SpanStartOption) SpanConfig {
var c SpanConfig
for _, option := range options {
option.applySpanStart(&c)
c = option.applySpanStart(c)
}
return c
}
@ -118,7 +118,7 @@ func NewSpanStartConfig(options ...SpanStartOption) SpanConfig {
func NewSpanEndConfig(options ...SpanEndOption) SpanConfig {
var c SpanConfig
for _, option := range options {
option.applySpanEnd(&c)
c = option.applySpanEnd(c)
}
return c
}
@ -126,19 +126,19 @@ func NewSpanEndConfig(options ...SpanEndOption) SpanConfig {
// SpanStartOption applies an option to a SpanConfig. These options are applicable
// only when the span is created
type SpanStartOption interface {
applySpanStart(*SpanConfig)
applySpanStart(SpanConfig) SpanConfig
}
type spanOptionFunc func(*SpanConfig)
type spanOptionFunc func(SpanConfig) SpanConfig
func (fn spanOptionFunc) applySpanStart(cfg *SpanConfig) {
fn(cfg)
func (fn spanOptionFunc) applySpanStart(cfg SpanConfig) SpanConfig {
return fn(cfg)
}
// SpanEndOption applies an option to a SpanConfig. These options are
// applicable only when the span is ended.
type SpanEndOption interface {
applySpanEnd(*SpanConfig)
applySpanEnd(SpanConfig) SpanConfig
}
// EventConfig is a group of options for an Event.
@ -170,7 +170,7 @@ func (cfg *EventConfig) StackTrace() bool {
func NewEventConfig(options ...EventOption) EventConfig {
var c EventConfig
for _, option := range options {
option.applyEvent(&c)
c = option.applyEvent(c)
}
if c.timestamp.IsZero() {
c.timestamp = time.Now()
@ -180,7 +180,7 @@ func NewEventConfig(options ...EventOption) EventConfig {
// EventOption applies span event options to an EventConfig.
type EventOption interface {
applyEvent(*EventConfig)
applyEvent(EventConfig) EventConfig
}
// SpanOption are options that can be used at both the beginning and end of a span.
@ -203,12 +203,14 @@ type SpanEndEventOption interface {
type attributeOption []attribute.KeyValue
func (o attributeOption) applySpan(c *SpanConfig) {
func (o attributeOption) applySpan(c SpanConfig) SpanConfig {
c.attributes = append(c.attributes, []attribute.KeyValue(o)...)
return c
}
func (o attributeOption) applySpanStart(c *SpanConfig) { o.applySpan(c) }
func (o attributeOption) applyEvent(c *EventConfig) {
func (o attributeOption) applySpanStart(c SpanConfig) SpanConfig { return o.applySpan(c) }
func (o attributeOption) applyEvent(c EventConfig) EventConfig {
c.attributes = append(c.attributes, []attribute.KeyValue(o)...)
return c
}
var _ SpanStartEventOption = attributeOption{}
@ -234,10 +236,16 @@ type SpanEventOption interface {
type timestampOption time.Time
func (o timestampOption) applySpan(c *SpanConfig) { c.timestamp = time.Time(o) }
func (o timestampOption) applySpanStart(c *SpanConfig) { o.applySpan(c) }
func (o timestampOption) applySpanEnd(c *SpanConfig) { o.applySpan(c) }
func (o timestampOption) applyEvent(c *EventConfig) { c.timestamp = time.Time(o) }
func (o timestampOption) applySpan(c SpanConfig) SpanConfig {
c.timestamp = time.Time(o)
return c
}
func (o timestampOption) applySpanStart(c SpanConfig) SpanConfig { return o.applySpan(c) }
func (o timestampOption) applySpanEnd(c SpanConfig) SpanConfig { return o.applySpan(c) }
func (o timestampOption) applyEvent(c EventConfig) EventConfig {
c.timestamp = time.Time(o)
return c
}
var _ SpanEventOption = timestampOption{}
@ -249,9 +257,15 @@ func WithTimestamp(t time.Time) SpanEventOption {
type stackTraceOption bool
func (o stackTraceOption) applyEvent(c *EventConfig) { c.stackTrace = bool(o) }
func (o stackTraceOption) applySpan(c *SpanConfig) { c.stackTrace = bool(o) }
func (o stackTraceOption) applySpanEnd(c *SpanConfig) { o.applySpan(c) }
func (o stackTraceOption) applyEvent(c EventConfig) EventConfig {
c.stackTrace = bool(o)
return c
}
func (o stackTraceOption) applySpan(c SpanConfig) SpanConfig {
c.stackTrace = bool(o)
return c
}
func (o stackTraceOption) applySpanEnd(c SpanConfig) SpanConfig { return o.applySpan(c) }
// WithStackTrace sets the flag to capture the error with stack trace (e.g. true, false).
func WithStackTrace(b bool) SpanEndEventOption {
@ -261,8 +275,9 @@ func WithStackTrace(b bool) SpanEndEventOption {
// WithLinks adds links to a Span. The links are added to the existing Span
// links, i.e. this does not overwrite. Links with invalid span context are ignored.
func WithLinks(links ...Link) SpanStartOption {
return spanOptionFunc(func(cfg *SpanConfig) {
return spanOptionFunc(func(cfg SpanConfig) SpanConfig {
cfg.links = append(cfg.links, links...)
return cfg
})
}
@ -270,28 +285,32 @@ func WithLinks(links ...Link) SpanStartOption {
// existing parent span context will be ignored when defining the Span's trace
// identifiers.
func WithNewRoot() SpanStartOption {
return spanOptionFunc(func(cfg *SpanConfig) {
return spanOptionFunc(func(cfg SpanConfig) SpanConfig {
cfg.newRoot = true
return cfg
})
}
// WithSpanKind sets the SpanKind of a Span.
func WithSpanKind(kind SpanKind) SpanStartOption {
return spanOptionFunc(func(cfg *SpanConfig) {
return spanOptionFunc(func(cfg SpanConfig) SpanConfig {
cfg.spanKind = kind
return cfg
})
}
// WithInstrumentationVersion sets the instrumentation version.
func WithInstrumentationVersion(version string) TracerOption {
return tracerOptionFunc(func(cfg *TracerConfig) {
return tracerOptionFunc(func(cfg TracerConfig) TracerConfig {
cfg.instrumentationVersion = version
return cfg
})
}
// WithSchemaURL sets the schema URL for the Tracer.
func WithSchemaURL(schemaURL string) TracerOption {
return tracerOptionFunc(func(cfg *TracerConfig) {
return tracerOptionFunc(func(cfg TracerConfig) TracerConfig {
cfg.schemaURL = schemaURL
return cfg
})
}

View File

@ -252,3 +252,71 @@ func TestTracerConfig(t *testing.T) {
assert.Equal(t, test.expected, config)
}
}
// Save benchmark results to a file level var to avoid the compiler optimizing
// away the actual work.
var (
tracerConfig TracerConfig
spanConfig SpanConfig
eventConfig EventConfig
)
func BenchmarkNewTracerConfig(b *testing.B) {
opts := []TracerOption{
WithInstrumentationVersion("testing verion"),
WithSchemaURL("testing URL"),
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tracerConfig = NewTracerConfig(opts...)
}
}
func BenchmarkNewSpanStartConfig(b *testing.B) {
opts := []SpanStartOption{
WithAttributes(attribute.Bool("key", true)),
WithTimestamp(time.Now()),
WithLinks(Link{}),
WithNewRoot(),
WithSpanKind(SpanKindClient),
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
spanConfig = NewSpanStartConfig(opts...)
}
}
func BenchmarkNewSpanEndConfig(b *testing.B) {
opts := []SpanEndOption{
WithTimestamp(time.Now()),
WithStackTrace(true),
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
spanConfig = NewSpanEndConfig(opts...)
}
}
func BenchmarkNewEventConfig(b *testing.B) {
opts := []EventOption{
WithAttributes(attribute.Bool("key", true)),
WithTimestamp(time.Now()),
WithStackTrace(true),
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
eventConfig = NewEventConfig(opts...)
}
}