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 ```go
// newConfig returns an appropriately configured config. // newConfig returns an appropriately configured config.
func newConfig([]Option) config { func newConfig(options ...Option) config {
// Set default values for config. // Set default values for config.
config := config{/* […] */} config := config{/* […] */}
for _, option := range options { for _, option := range options {
option.apply(&config) config = option.apply(config)
} }
// Preform any validation here. // Preform any validation here.
return config return config
@ -253,7 +253,7 @@ To set the value of the options a `config` contains, a corresponding
```go ```go
type Option interface { 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 Moreover, the interface becomes sealed so the user cannot easily implement
the interface on its own. 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 The name of the interface should be prefixed in the same way the
corresponding `config` is (if at all). corresponding `config` is (if at all).
@ -283,8 +286,9 @@ func With*(…) Option { … }
```go ```go
type defaultFalseOption bool type defaultFalseOption bool
func (o defaultFalseOption) apply(c *config) { func (o defaultFalseOption) apply(c config) config {
c.Bool = bool(o) c.Bool = bool(o)
return c
} }
// WithOption sets a T to have an option included. // WithOption sets a T to have an option included.
@ -296,8 +300,9 @@ func WithOption() Option {
```go ```go
type defaultTrueOption bool type defaultTrueOption bool
func (o defaultTrueOption) apply(c *config) { func (o defaultTrueOption) apply(c config) config {
c.Bool = bool(o) c.Bool = bool(o)
return c
} }
// WithoutOption sets a T to have Bool option excluded. // WithoutOption sets a T to have Bool option excluded.
@ -313,8 +318,9 @@ type myTypeOption struct {
MyType MyType MyType MyType
} }
func (o myTypeOption) apply(c *config) { func (o myTypeOption) apply(c config) config {
c.MyType = o.MyType c.MyType = o.MyType
return c
} }
// WithMyType sets T to have include MyType. // WithMyType sets T to have include MyType.
@ -326,16 +332,17 @@ func WithMyType(t MyType) Option {
##### Functional Options ##### Functional Options
```go ```go
type optionFunc func(*config) type optionFunc func(config) config
func (fn optionFunc) apply(c *config) { func (fn optionFunc) apply(c config) config {
fn(c) return fn(c)
} }
// WithMyType sets t as MyType. // WithMyType sets t as MyType.
func WithMyType(t MyType) Option { func WithMyType(t MyType) Option {
return optionFunc(func(c *config) { return optionFunc(func(c config) config {
c.MyType = t c.MyType = t
return c
}) })
} }
``` ```
@ -370,12 +377,12 @@ type config struct {
// DogOption apply Dog specific options. // DogOption apply Dog specific options.
type DogOption interface { type DogOption interface {
applyDog(*config) applyDog(config) config
} }
// BirdOption apply Bird specific options. // BirdOption apply Bird specific options.
type BirdOption interface { type BirdOption interface {
applyBird(*config) applyBird(config) config
} }
// Option apply options for all animals. // Option apply options for all animals.
@ -385,16 +392,35 @@ type Option interface {
} }
type weightOption float64 type weightOption float64
func (o weightOption) applyDog(c *config) { c.Weight = float64(o) }
func (o weightOption) applyBird(c *config) { c.Weight = float64(o) } 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) } func WithWeight(w float64) Option { return weightOption(w) }
type furColorOption string type furColorOption string
func (o furColorOption) applyDog(c *config) { c.Color = string(o) }
func (o furColorOption) applyDog(c config) config {
c.Color = string(o)
return c
}
func WithFurColor(c string) DogOption { return furColorOption(c) } func WithFurColor(c string) DogOption { return furColorOption(c) }
type maxAltitudeOption float64 type maxAltitudeOption float64
func (o maxAltitudeOption) applyBird(c *config) { c.MaxAltitude = float64(o) }
func (o maxAltitudeOption) applyBird(c config) config {
c.MaxAltitude = float64(o)
return c
}
func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) } func WithMaxAltitude(a float64) BirdOption { return maxAltitudeOption(a) }
func NewDog(name string, o ...DogOption) Dog {…} func NewDog(name string, o ...DogOption) Dog {…}

View File

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

View File

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

View File

@ -33,12 +33,12 @@ var DefaultEnvOptionsReader = EnvOptionsReader{
ReadFile: ioutil.ReadFile, ReadFile: ioutil.ReadFile,
} }
func ApplyGRPCEnvConfigs(cfg *Config) { func ApplyGRPCEnvConfigs(cfg Config) Config {
DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg) return DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg)
} }
func ApplyHTTPEnvConfigs(cfg *Config) { func ApplyHTTPEnvConfigs(cfg Config) Config {
DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg) return DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg)
} }
type EnvOptionsReader struct { type EnvOptionsReader struct {
@ -46,18 +46,20 @@ type EnvOptionsReader struct {
ReadFile func(filename string) ([]byte, error) ReadFile func(filename string) ([]byte, error)
} }
func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg *Config) { func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg Config) Config {
opts := e.GetOptionsFromEnv() opts := e.GetOptionsFromEnv()
for _, opt := range opts { 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() opts := e.GetOptionsFromEnv()
for _, opt := range opts { for _, opt := range opts {
opt.ApplyGRPCOption(cfg) cfg = opt.ApplyGRPCOption(cfg)
} }
return cfg
} }
func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption { func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
@ -74,7 +76,7 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
} else { } else {
opts = append(opts, WithSecure()) opts = append(opts, WithSecure())
} }
opts = append(opts, newSplitOption(func(cfg *Config) { opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Metrics.Endpoint = u.Host cfg.Metrics.Endpoint = u.Host
// For endpoint URLs for OTLP/HTTP per-signal variables, the // For endpoint URLs for OTLP/HTTP per-signal variables, the
// URL MUST be used as-is without any modification. The only // URL MUST be used as-is without any modification. The only
@ -85,10 +87,12 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
path = "/" path = "/"
} }
cfg.Metrics.URLPath = 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 // For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry. // exporter is going to send telemetry.
cfg.Metrics.Endpoint = path.Join(u.Host, u.Path) cfg.Metrics.Endpoint = path.Join(u.Host, u.Path)
return cfg
})) }))
} }
} else if v, ok = e.getEnvValue("ENDPOINT"); ok { } else if v, ok = e.getEnvValue("ENDPOINT"); ok {
@ -101,16 +105,18 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
} else { } else {
opts = append(opts, WithSecure()) opts = append(opts, WithSecure())
} }
opts = append(opts, newSplitOption(func(cfg *Config) { opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Metrics.Endpoint = u.Host cfg.Metrics.Endpoint = u.Host
// For OTLP/HTTP endpoint URLs without a per-signal // For OTLP/HTTP endpoint URLs without a per-signal
// configuration, the passed endpoint is used as a base URL // configuration, the passed endpoint is used as a base URL
// and the signals are sent to these paths relative to that. // and the signals are sent to these paths relative to that.
cfg.Metrics.URLPath = path.Join(u.Path, DefaultMetricsPath) 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 // For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry. // exporter is going to send telemetry.
cfg.Metrics.Endpoint = path.Join(u.Host, u.Path) 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. // any unset setting using the default gRPC config values.
func NewGRPCConfig(opts ...GRPCOption) Config { func NewGRPCConfig(opts ...GRPCOption) Config {
cfg := NewDefaultConfig() cfg := NewDefaultConfig()
ApplyGRPCEnvConfigs(&cfg) cfg = ApplyGRPCEnvConfigs(cfg)
for _, opt := range opts { for _, opt := range opts {
opt.ApplyGRPCOption(&cfg) cfg = opt.ApplyGRPCOption(cfg)
} }
if cfg.ServiceConfig != "" { if cfg.ServiceConfig != "" {
@ -129,8 +129,8 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
type ( type (
// GenericOption applies an option to the HTTP or gRPC driver. // GenericOption applies an option to the HTTP or gRPC driver.
GenericOption interface { GenericOption interface {
ApplyHTTPOption(*Config) ApplyHTTPOption(Config) Config
ApplyGRPCOption(*Config) ApplyGRPCOption(Config) Config
// A private method to prevent users implementing the // A private method to prevent users implementing the
// interface and so future additions to it will not // interface and so future additions to it will not
@ -140,7 +140,7 @@ type (
// HTTPOption applies an option to the HTTP driver. // HTTPOption applies an option to the HTTP driver.
HTTPOption interface { HTTPOption interface {
ApplyHTTPOption(*Config) ApplyHTTPOption(Config) Config
// A private method to prevent users implementing the // A private method to prevent users implementing the
// interface and so future additions to it will not // interface and so future additions to it will not
@ -150,7 +150,7 @@ type (
// GRPCOption applies an option to the gRPC driver. // GRPCOption applies an option to the gRPC driver.
GRPCOption interface { GRPCOption interface {
ApplyGRPCOption(*Config) ApplyGRPCOption(Config) Config
// A private method to prevent users implementing the // A private method to prevent users implementing the
// interface and so future additions to it will not // interface and so future additions to it will not
@ -162,128 +162,138 @@ type (
// genericOption is an option that applies the same logic // genericOption is an option that applies the same logic
// for both gRPC and HTTP. // for both gRPC and HTTP.
type genericOption struct { type genericOption struct {
fn func(*Config) fn func(Config) Config
} }
func (g *genericOption) ApplyGRPCOption(cfg *Config) { func (g *genericOption) ApplyGRPCOption(cfg Config) Config {
g.fn(cfg) return g.fn(cfg)
} }
func (g *genericOption) ApplyHTTPOption(cfg *Config) { func (g *genericOption) ApplyHTTPOption(cfg Config) Config {
g.fn(cfg) return g.fn(cfg)
} }
func (genericOption) private() {} func (genericOption) private() {}
func newGenericOption(fn func(cfg *Config)) GenericOption { func newGenericOption(fn func(cfg Config) Config) GenericOption {
return &genericOption{fn: fn} return &genericOption{fn: fn}
} }
// splitOption is an option that applies different logics // splitOption is an option that applies different logics
// for gRPC and HTTP. // for gRPC and HTTP.
type splitOption struct { type splitOption struct {
httpFn func(*Config) httpFn func(Config) Config
grpcFn func(*Config) grpcFn func(Config) Config
} }
func (g *splitOption) ApplyGRPCOption(cfg *Config) { func (g *splitOption) ApplyGRPCOption(cfg Config) Config {
g.grpcFn(cfg) return g.grpcFn(cfg)
} }
func (g *splitOption) ApplyHTTPOption(cfg *Config) { func (g *splitOption) ApplyHTTPOption(cfg Config) Config {
g.httpFn(cfg) return g.httpFn(cfg)
} }
func (splitOption) private() {} 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} return &splitOption{httpFn: httpFn, grpcFn: grpcFn}
} }
// httpOption is an option that is only applied to the HTTP driver. // httpOption is an option that is only applied to the HTTP driver.
type httpOption struct { type httpOption struct {
fn func(*Config) fn func(Config) Config
} }
func (h *httpOption) ApplyHTTPOption(cfg *Config) { func (h *httpOption) ApplyHTTPOption(cfg Config) Config {
h.fn(cfg) return h.fn(cfg)
} }
func (httpOption) private() {} func (httpOption) private() {}
func NewHTTPOption(fn func(cfg *Config)) HTTPOption { func NewHTTPOption(fn func(cfg Config) Config) HTTPOption {
return &httpOption{fn: fn} return &httpOption{fn: fn}
} }
// grpcOption is an option that is only applied to the gRPC driver. // grpcOption is an option that is only applied to the gRPC driver.
type grpcOption struct { type grpcOption struct {
fn func(*Config) fn func(Config) Config
} }
func (h *grpcOption) ApplyGRPCOption(cfg *Config) { func (h *grpcOption) ApplyGRPCOption(cfg Config) Config {
h.fn(cfg) return h.fn(cfg)
} }
func (grpcOption) private() {} func (grpcOption) private() {}
func NewGRPCOption(fn func(cfg *Config)) GRPCOption { func NewGRPCOption(fn func(cfg Config) Config) GRPCOption {
return &grpcOption{fn: fn} return &grpcOption{fn: fn}
} }
// Generic Options // Generic Options
func WithEndpoint(endpoint string) GenericOption { func WithEndpoint(endpoint string) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Endpoint = endpoint cfg.Metrics.Endpoint = endpoint
return cfg
}) })
} }
func WithCompression(compression Compression) GenericOption { func WithCompression(compression Compression) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Compression = compression cfg.Metrics.Compression = compression
return cfg
}) })
} }
func WithURLPath(urlPath string) GenericOption { func WithURLPath(urlPath string) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Metrics.URLPath = urlPath cfg.Metrics.URLPath = urlPath
return cfg
}) })
} }
func WithRetry(rc retry.Config) GenericOption { func WithRetry(rc retry.Config) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.RetryConfig = rc cfg.RetryConfig = rc
return cfg
}) })
} }
func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption { func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption {
return newSplitOption(func(cfg *Config) { return newSplitOption(func(cfg Config) Config {
cfg.Metrics.TLSCfg = tlsCfg.Clone() cfg.Metrics.TLSCfg = tlsCfg.Clone()
}, func(cfg *Config) { return cfg
}, func(cfg Config) Config {
cfg.Metrics.GRPCCredentials = credentials.NewTLS(tlsCfg) cfg.Metrics.GRPCCredentials = credentials.NewTLS(tlsCfg)
return cfg
}) })
} }
func WithInsecure() GenericOption { func WithInsecure() GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Insecure = true cfg.Metrics.Insecure = true
return cfg
}) })
} }
func WithSecure() GenericOption { func WithSecure() GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Insecure = false cfg.Metrics.Insecure = false
return cfg
}) })
} }
func WithHeaders(headers map[string]string) GenericOption { func WithHeaders(headers map[string]string) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Headers = headers cfg.Metrics.Headers = headers
return cfg
}) })
} }
func WithTimeout(duration time.Duration) GenericOption { func WithTimeout(duration time.Duration) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Metrics.Timeout = duration cfg.Metrics.Timeout = duration
return cfg
}) })
} }

View File

@ -387,9 +387,9 @@ func TestConfigs(t *testing.T) {
// Tests Generic options as HTTP Options // Tests Generic options as HTTP Options
cfg := otlpconfig.NewDefaultConfig() cfg := otlpconfig.NewDefaultConfig()
otlpconfig.ApplyHTTPEnvConfigs(&cfg) cfg = otlpconfig.ApplyHTTPEnvConfigs(cfg)
for _, opt := range tt.opts { for _, opt := range tt.opts {
opt.ApplyHTTPOption(&cfg) cfg = opt.ApplyHTTPOption(cfg)
} }
tt.asserts(t, &cfg, false) 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. // Option are setting options passed to an Exporter on creation.
type Option interface { type Option interface {
apply(*config) apply(config) config
} }
type exporterOptionFunc func(*config) type exporterOptionFunc func(config) config
func (fn exporterOptionFunc) apply(cfg *config) { func (fn exporterOptionFunc) apply(cfg config) config {
fn(cfg) return fn(cfg)
} }
type config struct { type config struct {
@ -36,7 +36,8 @@ type config struct {
// aggregation). If not specified otherwise, exporter will use a // aggregation). If not specified otherwise, exporter will use a
// cumulative temporality selector. // cumulative temporality selector.
func WithMetricAggregationTemporalitySelector(selector aggregation.TemporalitySelector) Option { func WithMetricAggregationTemporalitySelector(selector aggregation.TemporalitySelector) Option {
return exporterOptionFunc(func(cfg *config) { return exporterOptionFunc(func(cfg config) config {
cfg.temporalitySelector = selector cfg.temporalitySelector = selector
return cfg
}) })
} }

View File

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

View File

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

View File

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

View File

@ -33,12 +33,12 @@ var DefaultEnvOptionsReader = EnvOptionsReader{
ReadFile: ioutil.ReadFile, ReadFile: ioutil.ReadFile,
} }
func ApplyGRPCEnvConfigs(cfg *Config) { func ApplyGRPCEnvConfigs(cfg Config) Config {
DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg) return DefaultEnvOptionsReader.ApplyGRPCEnvConfigs(cfg)
} }
func ApplyHTTPEnvConfigs(cfg *Config) { func ApplyHTTPEnvConfigs(cfg Config) Config {
DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg) return DefaultEnvOptionsReader.ApplyHTTPEnvConfigs(cfg)
} }
type EnvOptionsReader struct { type EnvOptionsReader struct {
@ -46,18 +46,20 @@ type EnvOptionsReader struct {
ReadFile func(filename string) ([]byte, error) ReadFile func(filename string) ([]byte, error)
} }
func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg *Config) { func (e *EnvOptionsReader) ApplyHTTPEnvConfigs(cfg Config) Config {
opts := e.GetOptionsFromEnv() opts := e.GetOptionsFromEnv()
for _, opt := range opts { 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() opts := e.GetOptionsFromEnv()
for _, opt := range opts { for _, opt := range opts {
opt.ApplyGRPCOption(cfg) cfg = opt.ApplyGRPCOption(cfg)
} }
return cfg
} }
func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption { func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
@ -74,7 +76,7 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
} else { } else {
opts = append(opts, WithSecure()) opts = append(opts, WithSecure())
} }
opts = append(opts, newSplitOption(func(cfg *Config) { opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Traces.Endpoint = u.Host cfg.Traces.Endpoint = u.Host
// For endpoint URLs for OTLP/HTTP per-signal variables, the // For endpoint URLs for OTLP/HTTP per-signal variables, the
// URL MUST be used as-is without any modification. The only // URL MUST be used as-is without any modification. The only
@ -85,10 +87,12 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
path = "/" path = "/"
} }
cfg.Traces.URLPath = 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 // For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry. // exporter is going to send telemetry.
cfg.Traces.Endpoint = path.Join(u.Host, u.Path) cfg.Traces.Endpoint = path.Join(u.Host, u.Path)
return cfg
})) }))
} }
} else if v, ok = e.getEnvValue("ENDPOINT"); ok { } else if v, ok = e.getEnvValue("ENDPOINT"); ok {
@ -101,16 +105,18 @@ func (e *EnvOptionsReader) GetOptionsFromEnv() []GenericOption {
} else { } else {
opts = append(opts, WithSecure()) opts = append(opts, WithSecure())
} }
opts = append(opts, newSplitOption(func(cfg *Config) { opts = append(opts, newSplitOption(func(cfg Config) Config {
cfg.Traces.Endpoint = u.Host cfg.Traces.Endpoint = u.Host
// For OTLP/HTTP endpoint URLs without a per-signal // For OTLP/HTTP endpoint URLs without a per-signal
// configuration, the passed endpoint is used as a base URL // configuration, the passed endpoint is used as a base URL
// and the signals are sent to these paths relative to that. // and the signals are sent to these paths relative to that.
cfg.Traces.URLPath = path.Join(u.Path, DefaultTracesPath) 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 // For OTLP/gRPC endpoints, this is the target to which the
// exporter is going to send telemetry. // exporter is going to send telemetry.
cfg.Traces.Endpoint = path.Join(u.Host, u.Path) 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. // any unset setting using the default gRPC config values.
func NewGRPCConfig(opts ...GRPCOption) Config { func NewGRPCConfig(opts ...GRPCOption) Config {
cfg := NewDefaultConfig() cfg := NewDefaultConfig()
ApplyGRPCEnvConfigs(&cfg) cfg = ApplyGRPCEnvConfigs(cfg)
for _, opt := range opts { for _, opt := range opts {
opt.ApplyGRPCOption(&cfg) cfg = opt.ApplyGRPCOption(cfg)
} }
if cfg.ServiceConfig != "" { if cfg.ServiceConfig != "" {
@ -122,8 +122,8 @@ func NewGRPCConfig(opts ...GRPCOption) Config {
type ( type (
// GenericOption applies an option to the HTTP or gRPC driver. // GenericOption applies an option to the HTTP or gRPC driver.
GenericOption interface { GenericOption interface {
ApplyHTTPOption(*Config) ApplyHTTPOption(Config) Config
ApplyGRPCOption(*Config) ApplyGRPCOption(Config) Config
// A private method to prevent users implementing the // A private method to prevent users implementing the
// interface and so future additions to it will not // interface and so future additions to it will not
@ -133,7 +133,7 @@ type (
// HTTPOption applies an option to the HTTP driver. // HTTPOption applies an option to the HTTP driver.
HTTPOption interface { HTTPOption interface {
ApplyHTTPOption(*Config) ApplyHTTPOption(Config) Config
// A private method to prevent users implementing the // A private method to prevent users implementing the
// interface and so future additions to it will not // interface and so future additions to it will not
@ -143,7 +143,7 @@ type (
// GRPCOption applies an option to the gRPC driver. // GRPCOption applies an option to the gRPC driver.
GRPCOption interface { GRPCOption interface {
ApplyGRPCOption(*Config) ApplyGRPCOption(Config) Config
// A private method to prevent users implementing the // A private method to prevent users implementing the
// interface and so future additions to it will not // interface and so future additions to it will not
@ -155,128 +155,138 @@ type (
// genericOption is an option that applies the same logic // genericOption is an option that applies the same logic
// for both gRPC and HTTP. // for both gRPC and HTTP.
type genericOption struct { type genericOption struct {
fn func(*Config) fn func(Config) Config
} }
func (g *genericOption) ApplyGRPCOption(cfg *Config) { func (g *genericOption) ApplyGRPCOption(cfg Config) Config {
g.fn(cfg) return g.fn(cfg)
} }
func (g *genericOption) ApplyHTTPOption(cfg *Config) { func (g *genericOption) ApplyHTTPOption(cfg Config) Config {
g.fn(cfg) return g.fn(cfg)
} }
func (genericOption) private() {} func (genericOption) private() {}
func newGenericOption(fn func(cfg *Config)) GenericOption { func newGenericOption(fn func(cfg Config) Config) GenericOption {
return &genericOption{fn: fn} return &genericOption{fn: fn}
} }
// splitOption is an option that applies different logics // splitOption is an option that applies different logics
// for gRPC and HTTP. // for gRPC and HTTP.
type splitOption struct { type splitOption struct {
httpFn func(*Config) httpFn func(Config) Config
grpcFn func(*Config) grpcFn func(Config) Config
} }
func (g *splitOption) ApplyGRPCOption(cfg *Config) { func (g *splitOption) ApplyGRPCOption(cfg Config) Config {
g.grpcFn(cfg) return g.grpcFn(cfg)
} }
func (g *splitOption) ApplyHTTPOption(cfg *Config) { func (g *splitOption) ApplyHTTPOption(cfg Config) Config {
g.httpFn(cfg) return g.httpFn(cfg)
} }
func (splitOption) private() {} 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} return &splitOption{httpFn: httpFn, grpcFn: grpcFn}
} }
// httpOption is an option that is only applied to the HTTP driver. // httpOption is an option that is only applied to the HTTP driver.
type httpOption struct { type httpOption struct {
fn func(*Config) fn func(Config) Config
} }
func (h *httpOption) ApplyHTTPOption(cfg *Config) { func (h *httpOption) ApplyHTTPOption(cfg Config) Config {
h.fn(cfg) return h.fn(cfg)
} }
func (httpOption) private() {} func (httpOption) private() {}
func NewHTTPOption(fn func(cfg *Config)) HTTPOption { func NewHTTPOption(fn func(cfg Config) Config) HTTPOption {
return &httpOption{fn: fn} return &httpOption{fn: fn}
} }
// grpcOption is an option that is only applied to the gRPC driver. // grpcOption is an option that is only applied to the gRPC driver.
type grpcOption struct { type grpcOption struct {
fn func(*Config) fn func(Config) Config
} }
func (h *grpcOption) ApplyGRPCOption(cfg *Config) { func (h *grpcOption) ApplyGRPCOption(cfg Config) Config {
h.fn(cfg) return h.fn(cfg)
} }
func (grpcOption) private() {} func (grpcOption) private() {}
func NewGRPCOption(fn func(cfg *Config)) GRPCOption { func NewGRPCOption(fn func(cfg Config) Config) GRPCOption {
return &grpcOption{fn: fn} return &grpcOption{fn: fn}
} }
// Generic Options // Generic Options
func WithEndpoint(endpoint string) GenericOption { func WithEndpoint(endpoint string) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Traces.Endpoint = endpoint cfg.Traces.Endpoint = endpoint
return cfg
}) })
} }
func WithCompression(compression Compression) GenericOption { func WithCompression(compression Compression) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Traces.Compression = compression cfg.Traces.Compression = compression
return cfg
}) })
} }
func WithURLPath(urlPath string) GenericOption { func WithURLPath(urlPath string) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Traces.URLPath = urlPath cfg.Traces.URLPath = urlPath
return cfg
}) })
} }
func WithRetry(rc retry.Config) GenericOption { func WithRetry(rc retry.Config) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.RetryConfig = rc cfg.RetryConfig = rc
return cfg
}) })
} }
func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption { func WithTLSClientConfig(tlsCfg *tls.Config) GenericOption {
return newSplitOption(func(cfg *Config) { return newSplitOption(func(cfg Config) Config {
cfg.Traces.TLSCfg = tlsCfg.Clone() cfg.Traces.TLSCfg = tlsCfg.Clone()
}, func(cfg *Config) { return cfg
}, func(cfg Config) Config {
cfg.Traces.GRPCCredentials = credentials.NewTLS(tlsCfg) cfg.Traces.GRPCCredentials = credentials.NewTLS(tlsCfg)
return cfg
}) })
} }
func WithInsecure() GenericOption { func WithInsecure() GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Traces.Insecure = true cfg.Traces.Insecure = true
return cfg
}) })
} }
func WithSecure() GenericOption { func WithSecure() GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Traces.Insecure = false cfg.Traces.Insecure = false
return cfg
}) })
} }
func WithHeaders(headers map[string]string) GenericOption { func WithHeaders(headers map[string]string) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Traces.Headers = headers cfg.Traces.Headers = headers
return cfg
}) })
} }
func WithTimeout(duration time.Duration) GenericOption { func WithTimeout(duration time.Duration) GenericOption {
return newGenericOption(func(cfg *Config) { return newGenericOption(func(cfg Config) Config {
cfg.Traces.Timeout = duration cfg.Traces.Timeout = duration
return cfg
}) })
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -38,7 +38,7 @@ func (cfg *InstrumentConfig) Unit() unit.Unit {
type InstrumentOption interface { type InstrumentOption interface {
// ApplyMeter is used to set a InstrumentOption value of a // ApplyMeter is used to set a InstrumentOption value of a
// InstrumentConfig. // InstrumentConfig.
applyInstrument(*InstrumentConfig) applyInstrument(InstrumentConfig) InstrumentConfig
} }
// NewInstrumentConfig creates a new InstrumentConfig // NewInstrumentConfig creates a new InstrumentConfig
@ -46,28 +46,30 @@ type InstrumentOption interface {
func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig { func NewInstrumentConfig(opts ...InstrumentOption) InstrumentConfig {
var config InstrumentConfig var config InstrumentConfig
for _, o := range opts { for _, o := range opts {
o.applyInstrument(&config) config = o.applyInstrument(config)
} }
return config return config
} }
type instrumentOptionFunc func(*InstrumentConfig) type instrumentOptionFunc func(InstrumentConfig) InstrumentConfig
func (fn instrumentOptionFunc) applyInstrument(cfg *InstrumentConfig) { func (fn instrumentOptionFunc) applyInstrument(cfg InstrumentConfig) InstrumentConfig {
fn(cfg) return fn(cfg)
} }
// WithDescription applies provided description. // WithDescription applies provided description.
func WithDescription(desc string) InstrumentOption { func WithDescription(desc string) InstrumentOption {
return instrumentOptionFunc(func(cfg *InstrumentConfig) { return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
cfg.description = desc cfg.description = desc
return cfg
}) })
} }
// WithUnit applies provided unit. // WithUnit applies provided unit.
func WithUnit(unit unit.Unit) InstrumentOption { func WithUnit(unit unit.Unit) InstrumentOption {
return instrumentOptionFunc(func(cfg *InstrumentConfig) { return instrumentOptionFunc(func(cfg InstrumentConfig) InstrumentConfig {
cfg.unit = unit cfg.unit = unit
return cfg
}) })
} }
@ -90,7 +92,7 @@ func (cfg *MeterConfig) SchemaURL() string {
// MeterOption is an interface for applying Meter options. // MeterOption is an interface for applying Meter options.
type MeterOption interface { type MeterOption interface {
// ApplyMeter is used to set a MeterOption value of a MeterConfig. // ApplyMeter is used to set a MeterOption value of a MeterConfig.
applyMeter(*MeterConfig) applyMeter(MeterConfig) MeterConfig
} }
// NewMeterConfig creates a new MeterConfig and applies // NewMeterConfig creates a new MeterConfig and applies
@ -98,27 +100,29 @@ type MeterOption interface {
func NewMeterConfig(opts ...MeterOption) MeterConfig { func NewMeterConfig(opts ...MeterOption) MeterConfig {
var config MeterConfig var config MeterConfig
for _, o := range opts { for _, o := range opts {
o.applyMeter(&config) config = o.applyMeter(config)
} }
return config return config
} }
type meterOptionFunc func(*MeterConfig) type meterOptionFunc func(MeterConfig) MeterConfig
func (fn meterOptionFunc) applyMeter(cfg *MeterConfig) { func (fn meterOptionFunc) applyMeter(cfg MeterConfig) MeterConfig {
fn(cfg) return fn(cfg)
} }
// WithInstrumentationVersion sets the instrumentation version. // WithInstrumentationVersion sets the instrumentation version.
func WithInstrumentationVersion(version string) MeterOption { func WithInstrumentationVersion(version string) MeterOption {
return meterOptionFunc(func(config *MeterConfig) { return meterOptionFunc(func(config MeterConfig) MeterConfig {
config.instrumentationVersion = version config.instrumentationVersion = version
return config
}) })
} }
// WithSchemaURL sets the schema URL. // WithSchemaURL sets the schema URL.
func WithSchemaURL(schemaURL string) MeterOption { func WithSchemaURL(schemaURL string) MeterOption {
return meterOptionFunc(func(config *MeterConfig) { return meterOptionFunc(func(config MeterConfig) MeterConfig {
config.schemaURL = schemaURL 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. // Option is the interface that applies the value to a configuration option.
type Option interface { type Option interface {
// apply sets the Option value of a Config. // 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 // 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 } 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) res, err := resource.Merge(cfg.Resource, o.Resource)
if err != nil { if err != nil {
otel.Handle(err) otel.Handle(err)
} }
cfg.Resource = res cfg.Resource = res
return cfg
} }
// WithCollectPeriod sets the CollectPeriod configuration option of a Config. // WithCollectPeriod sets the CollectPeriod configuration option of a Config.
@ -88,8 +89,9 @@ func WithCollectPeriod(period time.Duration) Option {
type collectPeriodOption time.Duration type collectPeriodOption time.Duration
func (o collectPeriodOption) apply(cfg *config) { func (o collectPeriodOption) apply(cfg config) config {
cfg.CollectPeriod = time.Duration(o) cfg.CollectPeriod = time.Duration(o)
return cfg
} }
// WithCollectTimeout sets the CollectTimeout configuration option of a Config. // WithCollectTimeout sets the CollectTimeout configuration option of a Config.
@ -99,8 +101,9 @@ func WithCollectTimeout(timeout time.Duration) Option {
type collectTimeoutOption time.Duration type collectTimeoutOption time.Duration
func (o collectTimeoutOption) apply(cfg *config) { func (o collectTimeoutOption) apply(cfg config) config {
cfg.CollectTimeout = time.Duration(o) cfg.CollectTimeout = time.Duration(o)
return cfg
} }
// WithExporter sets the exporter configuration option of a Config. // 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 } type exporterOption struct{ exporter export.Exporter }
func (o exporterOption) apply(cfg *config) { func (o exporterOption) apply(cfg config) config {
cfg.Exporter = o.exporter cfg.Exporter = o.exporter
return cfg
} }
// WithPushTimeout sets the PushTimeout configuration option of a Config. // WithPushTimeout sets the PushTimeout configuration option of a Config.
@ -121,6 +125,7 @@ func WithPushTimeout(timeout time.Duration) Option {
type pushTimeoutOption time.Duration type pushTimeoutOption time.Duration
func (o pushTimeoutOption) apply(cfg *config) { func (o pushTimeoutOption) apply(cfg config) config {
cfg.PushTimeout = time.Duration(o) cfg.PushTimeout = time.Duration(o)
return cfg
} }

View File

@ -26,12 +26,12 @@ import (
func TestWithResource(t *testing.T) { func TestWithResource(t *testing.T) {
r := resource.NewSchemaless(attribute.String("A", "a")) r := resource.NewSchemaless(attribute.String("A", "a"))
c := &config{} c := config{}
WithResource(r).apply(c) c = WithResource(r).apply(c)
assert.Equal(t, r.Equivalent(), c.Resource.Equivalent()) assert.Equal(t, r.Equivalent(), c.Resource.Equivalent())
// Ensure overwriting works. // Ensure overwriting works.
c = &config{Resource: &resource.Resource{}} c = config{Resource: &resource.Resource{}}
WithResource(r).apply(c) c = WithResource(r).apply(c)
assert.Equal(t, r.Equivalent(), c.Resource.Equivalent()) 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 // and options (including optional exporter) to configure a metric
// export pipeline. // export pipeline.
func New(checkpointerFactory export.CheckpointerFactory, opts ...Option) *Controller { func New(checkpointerFactory export.CheckpointerFactory, opts ...Option) *Controller {
c := &config{ c := config{
CollectPeriod: DefaultPeriod, CollectPeriod: DefaultPeriod,
CollectTimeout: DefaultPeriod, CollectTimeout: DefaultPeriod,
PushTimeout: DefaultPeriod, PushTimeout: DefaultPeriod,
} }
for _, opt := range opts { for _, opt := range opts {
opt.apply(c) c = opt.apply(c)
} }
if c.Resource == nil { if c.Resource == nil {
c.Resource = resource.Default() 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 { func NewFactory(aselector export.AggregatorSelector, tselector aggregation.TemporalitySelector, opts ...Option) export.CheckpointerFactory {
var config config var config config
for _, opt := range opts { for _, opt := range opts {
opt.applyProcessor(&config) config = opt.applyProcessor(config)
} }
return factory{ return factory{
aselector: aselector, aselector: aselector,

View File

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

View File

@ -31,7 +31,7 @@ type config struct {
// Option is the interface that applies a configuration option. // Option is the interface that applies a configuration option.
type Option interface { type Option interface {
// apply sets the Option value of a config. // apply sets the Option value of a config.
apply(*config) apply(config) config
} }
// WithAttributes adds attributes to the configured Resource. // WithAttributes adds attributes to the configured Resource.
@ -56,8 +56,9 @@ type detectorsOption struct {
detectors []Detector detectors []Detector
} }
func (o detectorsOption) apply(cfg *config) { func (o detectorsOption) apply(cfg config) config {
cfg.detectors = append(cfg.detectors, o.detectors...) cfg.detectors = append(cfg.detectors, o.detectors...)
return cfg
} }
// WithFromEnv adds attributes from environment variables to the configured resource. // WithFromEnv adds attributes from environment variables to the configured resource.
@ -82,8 +83,9 @@ func WithSchemaURL(schemaURL string) Option {
type schemaURLOption string type schemaURLOption string
func (o schemaURLOption) apply(cfg *config) { func (o schemaURLOption) apply(cfg config) config {
cfg.schemaURL = string(o) cfg.schemaURL = string(o)
return cfg
} }
// WithOS adds all the OS attributes to the configured Resource. // 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) { func New(ctx context.Context, opts ...Option) (*Resource, error) {
cfg := config{} cfg := config{}
for _, opt := range opts { for _, opt := range opts {
opt.apply(&cfg) cfg = opt.apply(cfg)
} }
resource, err := Detect(ctx, cfg.detectors...) 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 // The passed opts are used to override these default values and configure the
// returned TracerProvider appropriately. // returned TracerProvider appropriately.
func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider { func NewTracerProvider(opts ...TracerProviderOption) *TracerProvider {
o := &tracerProviderConfig{} o := tracerProviderConfig{}
for _, opt := range opts { for _, opt := range opts {
opt.apply(o) o = opt.apply(o)
} }
ensureValidTracerProviderConfig(o) o = ensureValidTracerProviderConfig(o)
tp := &TracerProvider{ tp := &TracerProvider{
namedTracer: make(map[instrumentation.Library]*tracer), namedTracer: make(map[instrumentation.Library]*tracer),
@ -256,13 +256,13 @@ func (p *TracerProvider) Shutdown(ctx context.Context) error {
} }
type TracerProviderOption interface { type TracerProviderOption interface {
apply(*tracerProviderConfig) apply(tracerProviderConfig) tracerProviderConfig
} }
type traceProviderOptionFunc func(*tracerProviderConfig) type traceProviderOptionFunc func(tracerProviderConfig) tracerProviderConfig
func (fn traceProviderOptionFunc) apply(cfg *tracerProviderConfig) { func (fn traceProviderOptionFunc) apply(cfg tracerProviderConfig) tracerProviderConfig {
fn(cfg) return fn(cfg)
} }
// WithSyncer registers the exporter with the TracerProvider using a // 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. // WithSpanProcessor registers the SpanProcessor with a TracerProvider.
func WithSpanProcessor(sp SpanProcessor) TracerProviderOption { func WithSpanProcessor(sp SpanProcessor) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
cfg.processors = append(cfg.processors, sp) 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 // If this option is not used, the TracerProvider will use the
// resource.Default() Resource by default. // resource.Default() Resource by default.
func WithResource(r *resource.Resource) TracerProviderOption { func WithResource(r *resource.Resource) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
var err error var err error
cfg.resource, err = resource.Merge(resource.Environment(), r) cfg.resource, err = resource.Merge(resource.Environment(), r)
if err != nil { if err != nil {
otel.Handle(err) 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 // If this option is not used, the TracerProvider will use a random number
// IDGenerator by default. // IDGenerator by default.
func WithIDGenerator(g IDGenerator) TracerProviderOption { func WithIDGenerator(g IDGenerator) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
if g != nil { if g != nil {
cfg.idGenerator = g 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 // If this option is not used, the TracerProvider will use a
// ParentBased(AlwaysSample) Sampler by default. // ParentBased(AlwaysSample) Sampler by default.
func WithSampler(s Sampler) TracerProviderOption { func WithSampler(s Sampler) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
if s != nil { if s != nil {
cfg.sampler = s 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 // If this option is not used, the TracerProvider will use the default
// SpanLimits. // SpanLimits.
func WithSpanLimits(sl SpanLimits) TracerProviderOption { func WithSpanLimits(sl SpanLimits) TracerProviderOption {
return traceProviderOptionFunc(func(cfg *tracerProviderConfig) { return traceProviderOptionFunc(func(cfg tracerProviderConfig) tracerProviderConfig {
cfg.spanLimits = sl cfg.spanLimits = sl
return cfg
}) })
} }
// ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid. // ensureValidTracerProviderConfig ensures that given TracerProviderConfig is valid.
func ensureValidTracerProviderConfig(cfg *tracerProviderConfig) { func ensureValidTracerProviderConfig(cfg tracerProviderConfig) tracerProviderConfig {
if cfg.sampler == nil { if cfg.sampler == nil {
cfg.sampler = ParentBased(AlwaysSample()) cfg.sampler = ParentBased(AlwaysSample())
} }
@ -362,4 +367,5 @@ func ensureValidTracerProviderConfig(cfg *tracerProviderConfig) {
if cfg.resource == nil { if cfg.resource == nil {
cfg.resource = resource.Default() cfg.resource = resource.Default()
} }
return cfg
} }

View File

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

View File

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

View File

@ -252,3 +252,71 @@ func TestTracerConfig(t *testing.T) {
assert.Equal(t, test.expected, config) 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...)
}
}