1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-03-25 21:28:58 +02:00

Update API configs. (#1921)

* Added Reason to Contributing and Updated TracerConfig

* PR comment fixup

* Changed how span Options work.

* Fix Markdown linting

* Added meter configs.

* Fixes from PR comments

* fix for missing instrument

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
This commit is contained in:
Aaron Clawson 2021-05-27 09:53:56 -05:00 committed by GitHub
parent 270cc6030a
commit c1f460e097
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 452 additions and 282 deletions

View File

@ -50,6 +50,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Spans created by the global `Tracer` obtained from `go.opentelemetry.io/otel`, prior to a functioning `TracerProvider` being set, now propagate the span context from their parent if one exists. (#1901) - Spans created by the global `Tracer` obtained from `go.opentelemetry.io/otel`, prior to a functioning `TracerProvider` being set, now propagate the span context from their parent if one exists. (#1901)
- The `"go.opentelemetry.io/otel".Tracer` function now accepts tracer options. (#1902) - The `"go.opentelemetry.io/otel".Tracer` function now accepts tracer options. (#1902)
- Move the `go.opentelemetry.io/otel/unit` package to `go.opentelemetry.io/otel/metric/unit`. (#1903) - Move the `go.opentelemetry.io/otel/unit` package to `go.opentelemetry.io/otel/metric/unit`. (#1903)
- Changed `go.opentelemetry.io/otel/trace.TracerConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config.) (#1921)
- Changed `go.opentelemetry.io/otel/trace.SpanConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921)
- Changed `span.End()` now only accepts Options that are allowed at `end()`. (#1921)
- Changed `go.opentelemetry.io/otel/metric.InstrumentConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921)
- Changed `go.opentelemetry.io/otel/metric.MeterConfig` to conform to the [Contributing guidelines](CONTRIBUTING.md#config). (#1921)
- Refactor option types according to the contribution style guide. (#1882) - Refactor option types according to the contribution style guide. (#1882)
- Move the `go.opentelemetry.io/otel/trace.TraceStateFromKeyValues` function to the `go.opentelemetry.io/otel/oteltest` package. - Move the `go.opentelemetry.io/otel/trace.TraceStateFromKeyValues` function to the `go.opentelemetry.io/otel/oteltest` package.
This function is preserved for testing purposes where it may be useful to create a `TraceState` from `attribute.KeyValue`s, but it is not intended for production use. This function is preserved for testing purposes where it may be useful to create a `TraceState` from `attribute.KeyValue`s, but it is not intended for production use.

View File

@ -190,8 +190,16 @@ will likely want to build custom options for the configuration, the `config`
should be exported. Please, include in the documentation for the `config` should be exported. Please, include in the documentation for the `config`
how the user can extend the configuration. how the user can extend the configuration.
It is important that `config` are not shared across package boundaries. It is important that internal `config` are not shared across package boundaries.
Meaning a `config` from one package should not be directly used by another. Meaning a `config` from one package should not be directly used by another. The
one exception is the API packages. The configs from the base API, eg.
`go.opentelemetry.io/otel/trace.TracerConfig` and
`go.opentelemetry.io/otel/metric.InstrumentConfig`, are intended to be consumed
by the SDK therefor it is expected that these are exported.
When a config is exported we want to maintain forward and backward
compatibility, to achieve this no fields should be exported but should
instead be accessed by methods.
Optionally, it is common to include a `newConfig` function (with the same Optionally, it is common to include a `newConfig` function (with the same
naming scheme). This function wraps any defaults setting and looping over naming scheme). This function wraps any defaults setting and looping over

View File

@ -45,12 +45,12 @@ func (o *otelTracer) StartSpan(ctx context.Context, name string, s ...octrace.St
return ctx, octrace.NewSpan(&span{otSpan: sp}) return ctx, octrace.NewSpan(&span{otSpan: sp})
} }
func convertStartOptions(optFns []octrace.StartOption, name string) []trace.SpanOption { func convertStartOptions(optFns []octrace.StartOption, name string) []trace.SpanStartOption {
var ocOpts octrace.StartOptions var ocOpts octrace.StartOptions
for _, fn := range optFns { for _, fn := range optFns {
fn(&ocOpts) fn(&ocOpts)
} }
otOpts := []trace.SpanOption{} otOpts := []trace.SpanStartOption{}
switch ocOpts.SpanKind { switch ocOpts.SpanKind {
case octrace.SpanKindClient: case octrace.SpanKindClient:
otOpts = append(otOpts, trace.WithSpanKind(trace.SpanKindClient)) otOpts = append(otOpts, trace.WithSpanKind(trace.SpanKindClient))

View File

@ -98,7 +98,7 @@ func (s *bridgeSpan) Finish() {
} }
func (s *bridgeSpan) FinishWithOptions(opts ot.FinishOptions) { func (s *bridgeSpan) FinishWithOptions(opts ot.FinishOptions) {
var otelOpts []trace.SpanOption var otelOpts []trace.SpanEndOption
if !opts.FinishTime.IsZero() { if !opts.FinishTime.IsZero() {
otelOpts = append(otelOpts, trace.WithTimestamp(opts.FinishTime)) otelOpts = append(otelOpts, trace.WithTimestamp(opts.FinishTime))

View File

@ -70,9 +70,9 @@ func NewMockTracer() *MockTracer {
} }
} }
func (t *MockTracer) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) { func (t *MockTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
config := trace.NewSpanConfig(opts...) config := trace.NewSpanStartConfig(opts...)
startTime := config.Timestamp startTime := config.Timestamp()
if startTime.IsZero() { if startTime.IsZero() {
startTime = time.Now() startTime = time.Now()
} }
@ -86,13 +86,13 @@ func (t *MockTracer) Start(ctx context.Context, name string, opts ...trace.SpanO
officialTracer: t, officialTracer: t,
spanContext: spanContext, spanContext: spanContext,
Attributes: baggage.NewMap(baggage.MapUpdate{ Attributes: baggage.NewMap(baggage.MapUpdate{
MultiKV: config.Attributes, MultiKV: config.Attributes(),
}), }),
StartTime: startTime, StartTime: startTime,
EndTime: time.Time{}, EndTime: time.Time{},
ParentSpanID: t.getParentSpanID(ctx, config), ParentSpanID: t.getParentSpanID(ctx, config),
Events: nil, Events: nil,
SpanKind: trace.ValidateSpanKind(config.SpanKind), SpanKind: trace.ValidateSpanKind(config.SpanKind()),
} }
if !migration.SkipContextSetup(ctx) { if !migration.SkipContextSetup(ctx) {
ctx = trace.ContextWithSpan(ctx, span) ctx = trace.ContextWithSpan(ctx, span)
@ -137,7 +137,7 @@ func (t *MockTracer) getParentSpanID(ctx context.Context, config *trace.SpanConf
} }
func (t *MockTracer) getParentSpanContext(ctx context.Context, config *trace.SpanConfig) trace.SpanContext { func (t *MockTracer) getParentSpanContext(ctx context.Context, config *trace.SpanConfig) trace.SpanContext {
if !config.NewRoot { if !config.NewRoot() {
return trace.SpanContextFromContext(ctx) return trace.SpanContextFromContext(ctx)
} }
return trace.SpanContext{} return trace.SpanContext{}
@ -232,12 +232,12 @@ func (s *MockSpan) applyUpdate(update baggage.MapUpdate) {
s.Attributes = s.Attributes.Apply(update) s.Attributes = s.Attributes.Apply(update)
} }
func (s *MockSpan) End(options ...trace.SpanOption) { func (s *MockSpan) End(options ...trace.SpanEndOption) {
if !s.EndTime.IsZero() { if !s.EndTime.IsZero() {
return // already finished return // already finished
} }
config := trace.NewSpanConfig(options...) config := trace.NewSpanEndConfig(options...)
endTime := config.Timestamp endTime := config.Timestamp()
if endTime.IsZero() { if endTime.IsZero() {
endTime = time.Now() endTime = time.Now()
} }
@ -269,10 +269,10 @@ func (s *MockSpan) Tracer() trace.Tracer {
func (s *MockSpan) AddEvent(name string, o ...trace.EventOption) { func (s *MockSpan) AddEvent(name string, o ...trace.EventOption) {
c := trace.NewEventConfig(o...) c := trace.NewEventConfig(o...)
s.Events = append(s.Events, MockEvent{ s.Events = append(s.Events, MockEvent{
Timestamp: c.Timestamp, Timestamp: c.Timestamp(),
Name: name, Name: name,
Attributes: baggage.NewMap(baggage.MapUpdate{ Attributes: baggage.NewMap(baggage.MapUpdate{
MultiKV: c.Attributes, MultiKV: c.Attributes(),
}), }),
}) })
} }

View File

@ -74,7 +74,7 @@ func (t *WrapperTracer) otelTracer() trace.Tracer {
// Start forwards the call to the wrapped tracer. It also tries to // Start forwards the call to the wrapped tracer. It also tries to
// override the tracer of the returned span if the span implements the // override the tracer of the returned span if the span implements the
// OverrideTracerSpanExtension interface. // OverrideTracerSpanExtension interface.
func (t *WrapperTracer) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) { func (t *WrapperTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
ctx, span := t.otelTracer().Start(ctx, name, opts...) ctx, span := t.otelTracer().Start(ctx, name, opts...)
if spanWithExtension, ok := span.(migration.OverrideTracerSpanExtension); ok { if spanWithExtension, ok := span.(migration.OverrideTracerSpanExtension); ok {
spanWithExtension.OverrideTracer(t) spanWithExtension.OverrideTracer(t)

View File

@ -153,7 +153,7 @@ func (p *meterProvider) Meter(instrumentationName string, opts ...metric.MeterOp
key := meterKey{ key := meterKey{
Name: instrumentationName, Name: instrumentationName,
Version: metric.NewMeterConfig(opts...).InstrumentationVersion, Version: metric.NewMeterConfig(opts...).InstrumentationVersion(),
} }
entry, ok := p.meters[key] entry, ok := p.meters[key]
if !ok { if !ok {

View File

@ -92,7 +92,7 @@ func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
key := il{ key := il{
name: name, name: name,
version: trace.NewTracerConfig(opts...).InstrumentationVersion, version: trace.NewTracerConfig(opts...).InstrumentationVersion(),
} }
if p.tracers == nil { if p.tracers == nil {
@ -139,7 +139,7 @@ func (t *tracer) setDelegate(provider trace.TracerProvider) {
// Start implements trace.Tracer by forwarding the call to t.delegate if // Start implements trace.Tracer by forwarding the call to t.delegate if
// set, otherwise it forwards the call to a NoopTracer. // set, otherwise it forwards the call to a NoopTracer.
func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) { func (t *tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
delegate := t.delegate.Load() delegate := t.delegate.Load()
if delegate != nil { if delegate != nil {
return delegate.(trace.Tracer).Start(ctx, name, opts...) return delegate.(trace.Tracer).Start(ctx, name, opts...)
@ -175,7 +175,7 @@ func (nonRecordingSpan) SetError(bool) {}
func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {} func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {}
// End does nothing. // End does nothing.
func (nonRecordingSpan) End(...trace.SpanOption) {} func (nonRecordingSpan) End(...trace.SpanEndOption) {}
// RecordError does nothing. // RecordError does nothing.
func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {} func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {}

View File

@ -74,10 +74,10 @@ func (fn fnTracerProvider) Tracer(instrumentationName string, opts ...trace.Trac
} }
type fnTracer struct { type fnTracer struct {
start func(ctx context.Context, spanName string, opts ...trace.SpanOption) (context.Context, trace.Span) start func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span)
} }
func (fn fnTracer) Start(ctx context.Context, spanName string, opts ...trace.SpanOption) (context.Context, trace.Span) { func (fn fnTracer) Start(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
return fn.start(ctx, spanName, opts...) return fn.start(ctx, spanName, opts...)
} }
@ -93,7 +93,6 @@ func TestTraceProviderDelegates(t *testing.T) {
tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
called = true called = true
assert.Equal(t, "abc", name) assert.Equal(t, "abc", name)
assert.Equal(t, []trace.TracerOption{trace.WithInstrumentationVersion("xyz")}, opts)
return trace.NewNoopTracerProvider().Tracer("") return trace.NewNoopTracerProvider().Tracer("")
}, },
}) })
@ -131,7 +130,6 @@ func TestTraceProviderDelegatesConcurrentSafe(t *testing.T) {
tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
newVal := atomic.AddInt32(&called, 1) newVal := atomic.AddInt32(&called, 1)
assert.Equal(t, "abc", name) assert.Equal(t, "abc", name)
assert.Equal(t, []trace.TracerOption{trace.WithInstrumentationVersion("xyz")}, opts)
if newVal == 10 { if newVal == 10 {
// Signal the goroutine to finish. // Signal the goroutine to finish.
close(quit) close(quit)
@ -175,9 +173,8 @@ func TestTracerDelegatesConcurrentSafe(t *testing.T) {
otel.SetTracerProvider(fnTracerProvider{ otel.SetTracerProvider(fnTracerProvider{
tracer: func(name string, opts ...trace.TracerOption) trace.Tracer { tracer: func(name string, opts ...trace.TracerOption) trace.Tracer {
assert.Equal(t, "abc", name) assert.Equal(t, "abc", name)
assert.Equal(t, []trace.TracerOption{trace.WithInstrumentationVersion("xyz")}, opts)
return fnTracer{ return fnTracer{
start: func(ctx context.Context, spanName string, opts ...trace.SpanOption) (context.Context, trace.Span) { start: func(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
newVal := atomic.AddInt32(&called, 1) newVal := atomic.AddInt32(&called, 1)
assert.Equal(t, "name", spanName) assert.Equal(t, "name", spanName)
if newVal == 10 { if newVal == 10 {

View File

@ -20,23 +20,39 @@ import (
// InstrumentConfig contains options for metric instrument descriptors. // InstrumentConfig contains options for metric instrument descriptors.
type InstrumentConfig struct { type InstrumentConfig struct {
// Description describes the instrument in human-readable terms. description string
Description string unit unit.Unit
// Unit describes the measurement unit for a instrument. instrumentationName string
Unit unit.Unit instrumentationVersion string
// InstrumentationName is the name of the library providing }
// instrumentation.
InstrumentationName string // Description describes the instrument in human-readable terms.
// InstrumentationVersion is the version of the library providing func (cfg InstrumentConfig) Description() string {
// instrumentation. return cfg.description
InstrumentationVersion string }
// Unit describes the measurement unit for a instrument.
func (cfg InstrumentConfig) Unit() unit.Unit {
return cfg.unit
}
// InstrumentationName is the name of the library providing
// instrumentation.
func (cfg InstrumentConfig) InstrumentationName() string {
return cfg.instrumentationName
}
// InstrumentationVersion is the version of the library providing
// instrumentation.
func (cfg InstrumentConfig) InstrumentationVersion() string {
return cfg.instrumentationVersion
} }
// InstrumentOption is an interface for applying metric instrument options. // InstrumentOption is an interface for applying metric instrument options.
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)
} }
// NewInstrumentConfig creates a new InstrumentConfig // NewInstrumentConfig creates a new InstrumentConfig
@ -44,55 +60,52 @@ 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) o.applyInstrument(&config)
} }
return config return config
} }
// WithDescription applies provided description. type instrumentOptionFunc func(*InstrumentConfig)
func WithDescription(desc string) InstrumentOption {
return descriptionOption(desc) func (fn instrumentOptionFunc) applyInstrument(cfg *InstrumentConfig) {
fn(cfg)
} }
type descriptionOption string // WithDescription applies provided description.
func WithDescription(desc string) InstrumentOption {
func (d descriptionOption) ApplyInstrument(config *InstrumentConfig) { return instrumentOptionFunc(func(cfg *InstrumentConfig) {
config.Description = string(d) cfg.description = desc
})
} }
// WithUnit applies provided unit. // WithUnit applies provided unit.
func WithUnit(unit unit.Unit) InstrumentOption { func WithUnit(unit unit.Unit) InstrumentOption {
return unitOption(unit) return instrumentOptionFunc(func(cfg *InstrumentConfig) {
} cfg.unit = unit
})
type unitOption unit.Unit
func (u unitOption) ApplyInstrument(config *InstrumentConfig) {
config.Unit = unit.Unit(u)
} }
// WithInstrumentationName sets the instrumentation name. // WithInstrumentationName sets the instrumentation name.
func WithInstrumentationName(name string) InstrumentOption { func WithInstrumentationName(name string) InstrumentOption {
return instrumentationNameOption(name) return instrumentOptionFunc(func(cfg *InstrumentConfig) {
} cfg.instrumentationName = name
})
type instrumentationNameOption string
func (i instrumentationNameOption) ApplyInstrument(config *InstrumentConfig) {
config.InstrumentationName = string(i)
} }
// MeterConfig contains options for Meters. // MeterConfig contains options for Meters.
type MeterConfig struct { type MeterConfig struct {
// InstrumentationVersion is the version of the library providing instrumentationVersion string
// instrumentation. }
InstrumentationVersion string
// InstrumentationVersion is the version of the library providing instrumentation.
func (cfg MeterConfig) InstrumentationVersion() string {
return cfg.instrumentationVersion
} }
// 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)
} }
// NewMeterConfig creates a new MeterConfig and applies // NewMeterConfig creates a new MeterConfig and applies
@ -100,29 +113,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) o.applyMeter(&config)
} }
return config return config
} }
// InstrumentationOption is an interface for applying instrumentation specific // InstrumentMeterOption are options that can be used as both an InstrumentOption
// options. // and MeterOption
type InstrumentationOption interface { type InstrumentMeterOption interface {
InstrumentOption InstrumentOption
MeterOption MeterOption
} }
// WithInstrumentationVersion sets the instrumentation version. // WithInstrumentationVersion sets the instrumentation version.
func WithInstrumentationVersion(version string) InstrumentationOption { func WithInstrumentationVersion(version string) InstrumentMeterOption {
return instrumentationVersionOption(version) return instrumentationVersionOption(version)
} }
type instrumentationVersionOption string type instrumentationVersionOption string
func (i instrumentationVersionOption) ApplyMeter(config *MeterConfig) { func (i instrumentationVersionOption) applyMeter(config *MeterConfig) {
config.InstrumentationVersion = string(i) config.instrumentationVersion = string(i)
} }
func (i instrumentationVersionOption) ApplyInstrument(config *InstrumentConfig) { func (i instrumentationVersionOption) applyInstrument(config *InstrumentConfig) {
config.InstrumentationVersion = string(i) config.instrumentationVersion = string(i)
} }

View File

@ -285,8 +285,8 @@ func (m Meter) newAsync(
return NoopAsync{}, nil return NoopAsync{}, nil
} }
desc := NewDescriptor(name, mkind, nkind, opts...) desc := NewDescriptor(name, mkind, nkind, opts...)
desc.config.InstrumentationName = m.name desc.config.instrumentationName = m.name
desc.config.InstrumentationVersion = m.version desc.config.instrumentationVersion = m.version
return m.impl.NewAsyncInstrument(desc, runner) return m.impl.NewAsyncInstrument(desc, runner)
} }
@ -304,8 +304,8 @@ func (m Meter) newSync(
return NoopSync{}, nil return NoopSync{}, nil
} }
desc := NewDescriptor(name, metricKind, numberKind, opts...) desc := NewDescriptor(name, metricKind, numberKind, opts...)
desc.config.InstrumentationName = m.name desc.config.instrumentationName = m.name
desc.config.InstrumentationVersion = m.version desc.config.instrumentationVersion = m.version
return m.impl.NewSyncInstrument(desc) return m.impl.NewSyncInstrument(desc)
} }
@ -549,13 +549,13 @@ func (d Descriptor) InstrumentKind() InstrumentKind {
// Description provides a human-readable description of the metric // Description provides a human-readable description of the metric
// instrument. // instrument.
func (d Descriptor) Description() string { func (d Descriptor) Description() string {
return d.config.Description return d.config.Description()
} }
// Unit describes the units of the metric instrument. Unitless // Unit describes the units of the metric instrument. Unitless
// metrics return the empty string. // metrics return the empty string.
func (d Descriptor) Unit() unit.Unit { func (d Descriptor) Unit() unit.Unit {
return d.config.Unit return d.config.Unit()
} }
// NumberKind returns whether this instrument is declared over int64, // NumberKind returns whether this instrument is declared over int64,
@ -567,11 +567,11 @@ func (d Descriptor) NumberKind() number.Kind {
// InstrumentationName returns the name of the library that provided // InstrumentationName returns the name of the library that provided
// instrumentation for this instrument. // instrumentation for this instrument.
func (d Descriptor) InstrumentationName() string { func (d Descriptor) InstrumentationName() string {
return d.config.InstrumentationName return d.config.InstrumentationName()
} }
// InstrumentationVersion returns the version of the library that provided // InstrumentationVersion returns the version of the library that provided
// instrumentation for this instrument. // instrumentation for this instrument.
func (d Descriptor) InstrumentationVersion() string { func (d Descriptor) InstrumentationVersion() string {
return d.config.InstrumentationVersion return d.config.InstrumentationVersion()
} }

View File

@ -90,6 +90,6 @@ func WrapMeterImpl(impl MeterImpl, instrumentationName string, opts ...MeterOpti
return Meter{ return Meter{
impl: impl, impl: impl,
name: instrumentationName, name: instrumentationName,
version: NewMeterConfig(opts...).InstrumentationVersion, version: NewMeterConfig(opts...).InstrumentationVersion(),
} }
} }

View File

@ -152,6 +152,8 @@ func TestOptions(t *testing.T) {
opts []metric.InstrumentOption opts []metric.InstrumentOption
desc string desc string
unit unit.Unit unit unit.Unit
iName string
iVer string
} }
testcases := []testcase{ testcases := []testcase{
{ {
@ -159,6 +161,8 @@ func TestOptions(t *testing.T) {
opts: nil, opts: nil,
desc: "", desc: "",
unit: "", unit: "",
iName: "",
iVer: "",
}, },
{ {
name: "description", name: "description",
@ -167,6 +171,8 @@ func TestOptions(t *testing.T) {
}, },
desc: "stuff", desc: "stuff",
unit: "", unit: "",
iName: "",
iVer: "",
}, },
{ {
name: "description override", name: "description override",
@ -176,6 +182,8 @@ func TestOptions(t *testing.T) {
}, },
desc: "things", desc: "things",
unit: "", unit: "",
iName: "",
iVer: "",
}, },
{ {
name: "unit", name: "unit",
@ -184,7 +192,31 @@ func TestOptions(t *testing.T) {
}, },
desc: "", desc: "",
unit: "s", unit: "s",
iName: "",
iVer: "",
}, },
{
name: "description override",
opts: []metric.InstrumentOption{
metric.WithDescription("stuff"),
metric.WithDescription("things"),
},
desc: "things",
unit: "",
iName: "",
iVer: "",
},
{
name: "unit",
opts: []metric.InstrumentOption{
metric.WithUnit("s"),
},
desc: "",
unit: "s",
iName: "",
iVer: "",
},
{ {
name: "unit override", name: "unit override",
opts: []metric.InstrumentOption{ opts: []metric.InstrumentOption{
@ -193,15 +225,81 @@ func TestOptions(t *testing.T) {
}, },
desc: "", desc: "",
unit: "h", unit: "h",
iName: "",
iVer: "",
},
{
name: "name",
opts: []metric.InstrumentOption{
metric.WithInstrumentationName("n"),
},
desc: "",
unit: "",
iName: "n",
iVer: "",
},
{
name: "name override",
opts: []metric.InstrumentOption{
metric.WithInstrumentationName("n"),
metric.WithInstrumentationName("o"),
},
desc: "",
unit: "",
iName: "o",
iVer: "",
},
{
name: "version",
opts: []metric.InstrumentOption{
metric.WithInstrumentationVersion("v"),
},
desc: "",
unit: "",
iName: "",
iVer: "v",
},
{
name: "version override",
opts: []metric.InstrumentOption{
metric.WithInstrumentationVersion("v"),
metric.WithInstrumentationVersion("q"),
},
desc: "",
unit: "",
iName: "",
iVer: "q",
},
{
name: "all",
opts: []metric.InstrumentOption{
metric.WithDescription("stuff"),
metric.WithUnit("s"),
metric.WithInstrumentationName("n"),
metric.WithInstrumentationVersion("v"),
},
desc: "stuff",
unit: "s",
iName: "n",
iVer: "v",
}, },
} }
for idx, tt := range testcases { for idx, tt := range testcases {
t.Logf("Testing counter case %s (%d)", tt.name, idx) t.Logf("Testing counter case %s (%d)", tt.name, idx)
if diff := cmp.Diff(metric.NewInstrumentConfig(tt.opts...), metric.InstrumentConfig{ cfg := metric.NewInstrumentConfig(tt.opts...)
Description: tt.desc, if diff := cmp.Diff(cfg.Description(), tt.desc); diff != "" {
Unit: tt.unit, t.Errorf("Compare Description: -got +want %s", diff)
}); diff != "" { }
t.Errorf("Compare options: -got +want %s", diff) if diff := cmp.Diff(cfg.Unit(), tt.unit); diff != "" {
t.Errorf("Compare Unit: -got +want %s", diff)
}
if diff := cmp.Diff(cfg.InstrumentationName(), tt.iName); diff != "" {
t.Errorf("Compare InstrumentationNam: -got +want %s", diff)
}
if diff := cmp.Diff(cfg.InstrumentationVersion(), tt.iVer); diff != "" {
t.Errorf("Compare InstrumentationVersion: -got +want %s", diff)
} }
} }
} }

View File

@ -51,7 +51,7 @@ func (p *TracerProvider) Tracer(instName string, opts ...trace.TracerOption) tra
inst := instrumentation{ inst := instrumentation{
Name: instName, Name: instName,
Version: conf.InstrumentationVersion, Version: conf.InstrumentationVersion(),
} }
p.tracersMu.Lock() p.tracersMu.Lock()
defer p.tracersMu.Unlock() defer p.tracersMu.Unlock()
@ -59,7 +59,7 @@ func (p *TracerProvider) Tracer(instName string, opts ...trace.TracerOption) tra
if !ok { if !ok {
t = &Tracer{ t = &Tracer{
Name: instName, Name: instName,
Version: conf.InstrumentationVersion, Version: conf.InstrumentationVersion(),
config: &p.config, config: &p.config,
} }
p.tracers[inst] = t p.tracers[inst] = t

View File

@ -49,7 +49,7 @@ type Span struct {
// End ends s. If the Tracer that created s was configured with a // End ends s. If the Tracer that created s was configured with a
// SpanRecorder, that recorder's OnEnd method is called as the final part of // SpanRecorder, that recorder's OnEnd method is called as the final part of
// this method. // this method.
func (s *Span) End(opts ...trace.SpanOption) { func (s *Span) End(opts ...trace.SpanEndOption) {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
@ -57,9 +57,9 @@ func (s *Span) End(opts ...trace.SpanOption) {
return return
} }
c := trace.NewSpanConfig(opts...) c := trace.NewSpanEndConfig(opts...)
s.endTime = time.Now() s.endTime = time.Now()
if endTime := c.Timestamp; !endTime.IsZero() { if endTime := c.Timestamp(); !endTime.IsZero() {
s.endTime = endTime s.endTime = endTime
} }
@ -101,15 +101,15 @@ func (s *Span) AddEvent(name string, o ...trace.EventOption) {
c := trace.NewEventConfig(o...) c := trace.NewEventConfig(o...)
var attributes map[attribute.Key]attribute.Value var attributes map[attribute.Key]attribute.Value
if l := len(c.Attributes); l > 0 { if l := len(c.Attributes()); l > 0 {
attributes = make(map[attribute.Key]attribute.Value, l) attributes = make(map[attribute.Key]attribute.Value, l)
for _, attr := range c.Attributes { for _, attr := range c.Attributes() {
attributes[attr.Key] = attr.Value attributes[attr.Key] = attr.Value
} }
} }
s.events = append(s.events, Event{ s.events = append(s.events, Event{
Timestamp: c.Timestamp, Timestamp: c.Timestamp(),
Name: name, Name: name,
Attributes: attributes, Attributes: attributes,
}) })

View File

@ -36,10 +36,10 @@ type Tracer struct {
// Start creates a span. If t is configured with a SpanRecorder its OnStart // Start creates a span. If t is configured with a SpanRecorder its OnStart
// method will be called after the created Span has been initialized. // method will be called after the created Span has been initialized.
func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.SpanOption) (context.Context, trace.Span) { func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
c := trace.NewSpanConfig(opts...) c := trace.NewSpanStartConfig(opts...)
startTime := time.Now() startTime := time.Now()
if st := c.Timestamp; !st.IsZero() { if st := c.Timestamp(); !st.IsZero() {
startTime = st startTime = st
} }
@ -48,10 +48,10 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.SpanOptio
startTime: startTime, startTime: startTime,
attributes: make(map[attribute.Key]attribute.Value), attributes: make(map[attribute.Key]attribute.Value),
links: []trace.Link{}, links: []trace.Link{},
spanKind: c.SpanKind, spanKind: c.SpanKind(),
} }
if c.NewRoot { if c.NewRoot() {
span.spanContext = trace.SpanContext{} span.spanContext = trace.SpanContext{}
} else { } else {
span.spanContext = t.config.SpanContextFunc(ctx) span.spanContext = t.config.SpanContextFunc(ctx)
@ -61,7 +61,7 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.SpanOptio
} }
} }
for _, link := range c.Links { for _, link := range c.Links() {
for i, sl := range span.links { for i, sl := range span.links {
if sl.SpanContext.SpanID() == link.SpanContext.SpanID() && if sl.SpanContext.SpanID() == link.SpanContext.SpanID() &&
sl.SpanContext.TraceID() == link.SpanContext.TraceID() && sl.SpanContext.TraceID() == link.SpanContext.TraceID() &&
@ -75,7 +75,7 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.SpanOptio
} }
span.SetName(name) span.SetName(name)
span.SetAttributes(c.Attributes...) span.SetAttributes(c.Attributes()...)
if t.config.SpanRecorder != nil { if t.config.SpanRecorder != nil {
t.config.SpanRecorder.OnStart(span) t.config.SpanRecorder.OnStart(span)

View File

@ -116,7 +116,7 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
} }
il := instrumentation.Library{ il := instrumentation.Library{
Name: name, Name: name,
Version: c.InstrumentationVersion, Version: c.InstrumentationVersion(),
} }
t, ok := p.namedTracer[il] t, ok := p.namedTracer[il]
if !ok { if !ok {

View File

@ -214,7 +214,7 @@ func (s *span) SetAttributes(attributes ...attribute.KeyValue) {
// //
// If this method is called while panicking an error event is added to the // If this method is called while panicking an error event is added to the
// Span before ending it and the panic is continued. // Span before ending it and the panic is continued.
func (s *span) End(options ...trace.SpanOption) { func (s *span) End(options ...trace.SpanEndOption) {
// Do not start by checking if the span is being recorded which requires // Do not start by checking if the span is being recorded which requires
// acquiring a lock. Make a minimal check that the span is not nil. // acquiring a lock. Make a minimal check that the span is not nil.
if s == nil { if s == nil {
@ -247,14 +247,14 @@ func (s *span) End(options ...trace.SpanOption) {
s.executionTracerTaskEnd() s.executionTracerTaskEnd()
} }
config := trace.NewSpanConfig(options...) config := trace.NewSpanEndConfig(options...)
s.mu.Lock() s.mu.Lock()
// Setting endTime to non-zero marks the span as ended and not recording. // Setting endTime to non-zero marks the span as ended and not recording.
if config.Timestamp.IsZero() { if config.Timestamp().IsZero() {
s.endTime = et s.endTime = et
} else { } else {
s.endTime = config.Timestamp s.endTime = config.Timestamp()
} }
s.mu.Unlock() s.mu.Unlock()
@ -305,19 +305,19 @@ func (s *span) addEvent(name string, o ...trace.EventOption) {
c := trace.NewEventConfig(o...) c := trace.NewEventConfig(o...)
// Discard over limited attributes // Discard over limited attributes
attributes := c.Attributes()
var discarded int var discarded int
if len(c.Attributes) > s.spanLimits.AttributePerEventCountLimit { if len(attributes) > s.spanLimits.AttributePerEventCountLimit {
discarded = len(c.Attributes) - s.spanLimits.AttributePerEventCountLimit discarded = len(attributes) - s.spanLimits.AttributePerEventCountLimit
c.Attributes = c.Attributes[:s.spanLimits.AttributePerEventCountLimit] attributes = attributes[:s.spanLimits.AttributePerEventCountLimit]
} }
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
s.events.add(Event{ s.events.add(Event{
Name: name, Name: name,
Attributes: c.Attributes, Attributes: attributes,
DroppedAttributeCount: discarded, DroppedAttributeCount: discarded,
Time: c.Timestamp, Time: c.Timestamp(),
}) })
} }
@ -549,7 +549,7 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.Sp
// If told explicitly to make this a new root use a zero value SpanContext // If told explicitly to make this a new root use a zero value SpanContext
// as a parent which contains an invalid trace ID and is not remote. // as a parent which contains an invalid trace ID and is not remote.
var psc trace.SpanContext var psc trace.SpanContext
if !o.NewRoot { if !o.NewRoot() {
psc = trace.SpanContextFromContext(ctx) psc = trace.SpanContextFromContext(ctx)
} }
@ -575,9 +575,9 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.Sp
ParentContext: ctx, ParentContext: ctx,
TraceID: tid, TraceID: tid,
Name: name, Name: name,
Kind: o.SpanKind, Kind: o.SpanKind(),
Attributes: o.Attributes, Attributes: o.Attributes(),
Links: o.Links, Links: o.Links(),
}) })
scc := trace.SpanContextConfig{ scc := trace.SpanContextConfig{
@ -596,13 +596,13 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.Sp
return span return span
} }
startTime := o.Timestamp startTime := o.Timestamp()
if startTime.IsZero() { if startTime.IsZero() {
startTime = time.Now() startTime = time.Now()
} }
span.startTime = startTime span.startTime = startTime
span.spanKind = trace.ValidateSpanKind(o.SpanKind) span.spanKind = trace.ValidateSpanKind(o.SpanKind())
span.name = name span.name = name
span.parent = psc span.parent = psc
span.resource = provider.resource span.resource = provider.resource

View File

@ -813,7 +813,7 @@ func checkChild(t *testing.T, p trace.SpanContext, apiSpan trace.Span) error {
// startSpan starts a span with a name "span0". See startNamedSpan for // startSpan starts a span with a name "span0". See startNamedSpan for
// details. // details.
func startSpan(tp *TracerProvider, trName string, args ...trace.SpanOption) trace.Span { func startSpan(tp *TracerProvider, trName string, args ...trace.SpanStartOption) trace.Span {
return startNamedSpan(tp, trName, "span0", args...) return startNamedSpan(tp, trName, "span0", args...)
} }
@ -821,7 +821,7 @@ func startSpan(tp *TracerProvider, trName string, args ...trace.SpanOption) trac
// passed name and with remote span context as parent. The remote span // passed name and with remote span context as parent. The remote span
// context contains TraceFlags with sampled bit set. This allows the // context contains TraceFlags with sampled bit set. This allows the
// span to be automatically sampled. // span to be automatically sampled.
func startNamedSpan(tp *TracerProvider, trName, name string, args ...trace.SpanOption) trace.Span { func startNamedSpan(tp *TracerProvider, trName, name string, args ...trace.SpanStartOption) trace.Span {
_, span := tp.Tracer(trName).Start( _, span := tp.Tracer(trName).Start(
trace.ContextWithRemoteSpanContext(context.Background(), sc), trace.ContextWithRemoteSpanContext(context.Background(), sc),
name, name,
@ -834,7 +834,7 @@ func startNamedSpan(tp *TracerProvider, trName, name string, args ...trace.SpanO
// passed name and with the passed context. The context is returned // passed name and with the passed context. The context is returned
// along with the span so this parent can be used to create child // along with the span so this parent can be used to create child
// spans. // spans.
func startLocalSpan(tp *TracerProvider, ctx context.Context, trName, name string, args ...trace.SpanOption) (context.Context, trace.Span) { func startLocalSpan(tp *TracerProvider, ctx context.Context, trName, name string, args ...trace.SpanStartOption) (context.Context, trace.Span) {
ctx, span := tp.Tracer(trName).Start( ctx, span := tp.Tracer(trName).Start(
ctx, ctx,
name, name,

View File

@ -36,8 +36,8 @@ var _ trace.Tracer = &tracer{}
// span context found in the passed context. The created Span will be // span context found in the passed context. The created Span will be
// configured appropriately by any SpanOption passed. Any Timestamp option // configured appropriately by any SpanOption passed. Any Timestamp option
// passed will be used as the start time of the Span's life-cycle. // passed will be used as the start time of the Span's life-cycle.
func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanOption) (context.Context, trace.Span) { func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) {
config := trace.NewSpanConfig(options...) config := trace.NewSpanStartConfig(options...)
// For local spans created by this SDK, track child span count. // For local spans created by this SDK, track child span count.
if p := trace.SpanFromContext(ctx); p != nil { if p := trace.SpanFromContext(ctx); p != nil {
@ -47,10 +47,10 @@ func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanO
} }
span := startSpanInternal(ctx, tr, name, config) span := startSpanInternal(ctx, tr, name, config)
for _, l := range config.Links { for _, l := range config.Links() {
span.addLink(l) span.addLink(l)
} }
span.SetAttributes(config.Attributes...) span.SetAttributes(config.Attributes()...)
span.tracer = tr span.tracer = tr

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package trace package trace // import "go.opentelemetry.io/otel/trace"
import ( import (
"time" "time"
@ -22,109 +22,171 @@ import (
// TracerConfig is a group of options for a Tracer. // TracerConfig is a group of options for a Tracer.
type TracerConfig struct { type TracerConfig struct {
// InstrumentationVersion is the version of the library providing instrumentationVersion string
// instrumentation. }
InstrumentationVersion string
// InstrumentationVersion returns the version of the library providing instrumentation.
func (t *TracerConfig) InstrumentationVersion() string {
return t.instrumentationVersion
} }
// NewTracerConfig applies all the options to a returned TracerConfig. // NewTracerConfig applies all the options to a returned TracerConfig.
func NewTracerConfig(options ...TracerOption) *TracerConfig { func NewTracerConfig(options ...TracerOption) *TracerConfig {
config := new(TracerConfig) config := new(TracerConfig)
for _, option := range options { for _, option := range options {
option.ApplyTracer(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 {
ApplyTracer(*TracerConfig) apply(*TracerConfig)
}
// A private method to prevent users implementing the type tracerOptionFunc func(*TracerConfig)
// interface and so future additions to it will not
// violate compatibility. func (fn tracerOptionFunc) apply(cfg *TracerConfig) {
private() fn(cfg)
} }
// SpanConfig is a group of options for a Span. // SpanConfig is a group of options for a Span.
type SpanConfig struct { type SpanConfig struct {
// Attributes describe the associated qualities of a Span. attributes []attribute.KeyValue
Attributes []attribute.KeyValue timestamp time.Time
// Timestamp is a time in a Span life-cycle. links []Link
Timestamp time.Time newRoot bool
// Links are the associations a Span has with other Spans. spanKind SpanKind
Links []Link
// NewRoot identifies a Span as the root Span for a new trace. This is
// commonly used when an existing trace crosses trust boundaries and the
// remote parent span context should be ignored for security.
NewRoot bool
// SpanKind is the role a Span has in a trace.
SpanKind SpanKind
} }
// NewSpanConfig applies all the options to a returned SpanConfig. // Attributes describe the associated qualities of a Span.
func (cfg *SpanConfig) Attributes() []attribute.KeyValue {
return cfg.attributes
}
// Timestamp is a time in a Span life-cycle.
func (cfg *SpanConfig) Timestamp() time.Time {
return cfg.timestamp
}
// Links are the associations a Span has with other Spans.
func (cfg *SpanConfig) Links() []Link {
return cfg.links
}
// NewRoot identifies a Span as the root Span for a new trace. This is
// commonly used when an existing trace crosses trust boundaries and the
// remote parent span context should be ignored for security.
func (cfg *SpanConfig) NewRoot() bool {
return cfg.newRoot
}
// SpanKind is the role a Span has in a trace.
func (cfg *SpanConfig) SpanKind() SpanKind {
return cfg.spanKind
}
// NewSpanStartConfig applies all the options to a returned SpanConfig.
// No validation is performed on the returned SpanConfig (e.g. no uniqueness // No validation is performed on the returned SpanConfig (e.g. no uniqueness
// checking or bounding of data), it is left to the SDK to perform this // checking or bounding of data), it is left to the SDK to perform this
// action. // action.
func NewSpanConfig(options ...SpanOption) *SpanConfig { func NewSpanStartConfig(options ...SpanStartOption) *SpanConfig {
c := new(SpanConfig) c := new(SpanConfig)
for _, option := range options { for _, option := range options {
option.ApplySpan(c) option.applySpanStart(c)
} }
return c return c
} }
// SpanOption applies an option to a SpanConfig. // NewSpanEndConfig applies all the options to a returned SpanConfig.
type SpanOption interface { // No validation is performed on the returned SpanConfig (e.g. no uniqueness
ApplySpan(*SpanConfig) // checking or bounding of data), it is left to the SDK to perform this
// action.
func NewSpanEndConfig(options ...SpanEndOption) *SpanConfig {
c := new(SpanConfig)
for _, option := range options {
option.applySpanEnd(c)
}
return c
}
// A private method to prevent users implementing the // SpanStartOption applies an option to a SpanConfig. These options are applicable
// interface and so future additions to it will not // only when the span is created
// violate compatibility. type SpanStartOption interface {
private() applySpanStart(*SpanConfig)
}
type spanOptionFunc func(*SpanConfig)
func (fn spanOptionFunc) applySpanStart(cfg *SpanConfig) {
fn(cfg)
}
// SpanEndOptions applies an option to a SpanConfig. These options are applicable
// only when the span is ended.
type SpanEndOption interface {
applySpanEnd(*SpanConfig)
}
// EventConfig is a group of options for an Event.
type EventConfig struct {
attributes []attribute.KeyValue
timestamp time.Time
}
// Attributes describe the associated qualities of an Event.
func (cfg *EventConfig) Attributes() []attribute.KeyValue {
return cfg.attributes
}
// Timestamp is a time in an Event life-cycle.
func (cfg *EventConfig) Timestamp() time.Time {
return cfg.timestamp
} }
// NewEventConfig applies all the EventOptions to a returned SpanConfig. If no // NewEventConfig applies all the EventOptions to a returned SpanConfig. If no
// timestamp option is passed, the returned SpanConfig will have a Timestamp // timestamp option is passed, the returned SpanConfig will have a Timestamp
// set to the call time, otherwise no validation is performed on the returned // set to the call time, otherwise no validation is performed on the returned
// SpanConfig. // SpanConfig.
func NewEventConfig(options ...EventOption) *SpanConfig { func NewEventConfig(options ...EventOption) *EventConfig {
c := new(SpanConfig) c := new(EventConfig)
for _, option := range options { for _, option := range options {
option.ApplyEvent(c) option.applyEvent(c)
} }
if c.Timestamp.IsZero() { if c.timestamp.IsZero() {
c.Timestamp = time.Now() c.timestamp = time.Now()
} }
return c return c
} }
// EventOption applies span event options to a SpanConfig. // EventOption applies span event options to an EventConfig.
type EventOption interface { type EventOption interface {
ApplyEvent(*SpanConfig) applyEvent(*EventConfig)
// A private method to prevent users implementing the
// interface and so future additions to it will not
// violate compatibility.
private()
} }
// LifeCycleOption applies span life-cycle options to a SpanConfig. These // SpanOption are options that can be used at both the beginning and end of a span.
// options set values releated to events in a spans life-cycle like starting, type SpanOption interface {
// ending, experiencing an error and other user defined notable events. SpanStartOption
type LifeCycleOption interface { SpanEndOption
SpanOption }
// SpanStartEventOption are options that can be used at the start of a span, or with an event.
type SpanStartEventOption interface {
SpanStartOption
EventOption EventOption
} }
type attributeSpanOption []attribute.KeyValue type attributeOption []attribute.KeyValue
func (o attributeSpanOption) ApplySpan(c *SpanConfig) { o.apply(c) } func (o attributeOption) applySpan(c *SpanConfig) {
func (o attributeSpanOption) ApplyEvent(c *SpanConfig) { o.apply(c) } c.attributes = append(c.attributes, []attribute.KeyValue(o)...)
func (attributeSpanOption) private() {}
func (o attributeSpanOption) apply(c *SpanConfig) {
c.Attributes = append(c.Attributes, []attribute.KeyValue(o)...)
} }
func (o attributeOption) applySpanStart(c *SpanConfig) { o.applySpan(c) }
func (o attributeOption) applyEvent(c *EventConfig) {
c.attributes = append(c.attributes, []attribute.KeyValue(o)...)
}
var _ SpanStartEventOption = attributeOption{}
// WithAttributes adds the attributes related to a span life-cycle event. // WithAttributes adds the attributes related to a span life-cycle event.
// These attributes are used to describe the work a Span represents when this // These attributes are used to describe the work a Span represents when this
@ -135,71 +197,58 @@ func (o attributeSpanOption) apply(c *SpanConfig) {
// If multiple of these options are passed the attributes of each successive // If multiple of these options are passed the attributes of each successive
// option will extend the attributes instead of overwriting. There is no // option will extend the attributes instead of overwriting. There is no
// guarantee of uniqueness in the resulting attributes. // guarantee of uniqueness in the resulting attributes.
func WithAttributes(attributes ...attribute.KeyValue) LifeCycleOption { func WithAttributes(attributes ...attribute.KeyValue) SpanStartEventOption {
return attributeSpanOption(attributes) return attributeOption(attributes)
} }
type timestampSpanOption time.Time // SpanEventOption are options that can be used with an event or a span.
type SpanEventOption interface {
func (o timestampSpanOption) ApplySpan(c *SpanConfig) { o.apply(c) } SpanOption
func (o timestampSpanOption) ApplyEvent(c *SpanConfig) { o.apply(c) } EventOption
func (timestampSpanOption) private() {}
func (o timestampSpanOption) apply(c *SpanConfig) { c.Timestamp = time.Time(o) }
// WithTimestamp sets the time of a Span life-cycle moment (e.g. started,
// stopped, errored).
func WithTimestamp(t time.Time) LifeCycleOption {
return timestampSpanOption(t)
} }
type linksSpanOption []Link type timestampOption time.Time
func (o linksSpanOption) ApplySpan(c *SpanConfig) { c.Links = append(c.Links, []Link(o)...) } func (o timestampOption) applySpan(c *SpanConfig) { c.timestamp = time.Time(o) }
func (linksSpanOption) private() {} 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) }
var _ SpanEventOption = timestampOption{}
// WithTimestamp sets the time of a Span or Event life-cycle moment (e.g.
// started, stopped, errored).
func WithTimestamp(t time.Time) SpanEventOption {
return timestampOption(t)
}
// 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, i.e. this does not overwrite.
func WithLinks(links ...Link) SpanOption { func WithLinks(links ...Link) SpanStartOption {
return linksSpanOption(links) return spanOptionFunc(func(cfg *SpanConfig) {
cfg.links = append(cfg.links, links...)
})
} }
type newRootSpanOption bool
func (o newRootSpanOption) ApplySpan(c *SpanConfig) { c.NewRoot = bool(o) }
func (newRootSpanOption) private() {}
// WithNewRoot specifies that the Span should be treated as a root Span. Any // WithNewRoot specifies that the Span should be treated as a root Span. Any
// 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() SpanOption { func WithNewRoot() SpanStartOption {
return newRootSpanOption(true) return spanOptionFunc(func(cfg *SpanConfig) {
cfg.newRoot = true
})
} }
type spanKindSpanOption SpanKind
func (o spanKindSpanOption) ApplySpan(c *SpanConfig) { c.SpanKind = SpanKind(o) }
func (o spanKindSpanOption) private() {}
// WithSpanKind sets the SpanKind of a Span. // WithSpanKind sets the SpanKind of a Span.
func WithSpanKind(kind SpanKind) SpanOption { func WithSpanKind(kind SpanKind) SpanStartOption {
return spanKindSpanOption(kind) return spanOptionFunc(func(cfg *SpanConfig) {
} cfg.spanKind = kind
})
// InstrumentationOption is an interface for applying instrumentation specific
// options.
type InstrumentationOption interface {
TracerOption
} }
// WithInstrumentationVersion sets the instrumentation version. // WithInstrumentationVersion sets the instrumentation version.
func WithInstrumentationVersion(version string) InstrumentationOption { func WithInstrumentationVersion(version string) TracerOption {
return instrumentationVersionOption(version) return tracerOptionFunc(func(cfg *TracerConfig) {
cfg.instrumentationVersion = version
})
} }
type instrumentationVersionOption string
func (i instrumentationVersionOption) ApplyTracer(config *TracerConfig) {
config.InstrumentationVersion = string(i)
}
func (instrumentationVersionOption) private() {}

View File

@ -41,119 +41,119 @@ func TestNewSpanConfig(t *testing.T) {
} }
tests := []struct { tests := []struct {
options []SpanOption options []SpanStartOption
expected *SpanConfig expected *SpanConfig
}{ }{
{ {
// No non-zero-values should be set. // No non-zero-values should be set.
[]SpanOption{}, []SpanStartOption{},
new(SpanConfig), new(SpanConfig),
}, },
{ {
[]SpanOption{ []SpanStartOption{
WithAttributes(k1v1), WithAttributes(k1v1),
}, },
&SpanConfig{ &SpanConfig{
Attributes: []attribute.KeyValue{k1v1}, attributes: []attribute.KeyValue{k1v1},
}, },
}, },
{ {
// Multiple calls should append not overwrite. // Multiple calls should append not overwrite.
[]SpanOption{ []SpanStartOption{
WithAttributes(k1v1), WithAttributes(k1v1),
WithAttributes(k1v2), WithAttributes(k1v2),
WithAttributes(k2v2), WithAttributes(k2v2),
}, },
&SpanConfig{ &SpanConfig{
// No uniqueness is guaranteed by the API. // No uniqueness is guaranteed by the API.
Attributes: []attribute.KeyValue{k1v1, k1v2, k2v2}, attributes: []attribute.KeyValue{k1v1, k1v2, k2v2},
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
WithAttributes(k1v1, k1v2, k2v2), WithAttributes(k1v1, k1v2, k2v2),
}, },
&SpanConfig{ &SpanConfig{
// No uniqueness is guaranteed by the API. // No uniqueness is guaranteed by the API.
Attributes: []attribute.KeyValue{k1v1, k1v2, k2v2}, attributes: []attribute.KeyValue{k1v1, k1v2, k2v2},
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
WithTimestamp(timestamp0), WithTimestamp(timestamp0),
}, },
&SpanConfig{ &SpanConfig{
Timestamp: timestamp0, timestamp: timestamp0,
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
// Multiple calls overwrites with last-one-wins. // Multiple calls overwrites with last-one-wins.
WithTimestamp(timestamp0), WithTimestamp(timestamp0),
WithTimestamp(timestamp1), WithTimestamp(timestamp1),
}, },
&SpanConfig{ &SpanConfig{
Timestamp: timestamp1, timestamp: timestamp1,
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
WithLinks(link1), WithLinks(link1),
}, },
&SpanConfig{ &SpanConfig{
Links: []Link{link1}, links: []Link{link1},
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
// Multiple calls should append not overwrite. // Multiple calls should append not overwrite.
WithLinks(link1), WithLinks(link1),
WithLinks(link1, link2), WithLinks(link1, link2),
}, },
&SpanConfig{ &SpanConfig{
// No uniqueness is guaranteed by the API. // No uniqueness is guaranteed by the API.
Links: []Link{link1, link1, link2}, links: []Link{link1, link1, link2},
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
WithNewRoot(), WithNewRoot(),
}, },
&SpanConfig{ &SpanConfig{
NewRoot: true, newRoot: true,
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
// Multiple calls should not change NewRoot state. // Multiple calls should not change NewRoot state.
WithNewRoot(), WithNewRoot(),
WithNewRoot(), WithNewRoot(),
}, },
&SpanConfig{ &SpanConfig{
NewRoot: true, newRoot: true,
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
WithSpanKind(SpanKindConsumer), WithSpanKind(SpanKindConsumer),
}, },
&SpanConfig{ &SpanConfig{
SpanKind: SpanKindConsumer, spanKind: SpanKindConsumer,
}, },
}, },
{ {
[]SpanOption{ []SpanStartOption{
// Multiple calls overwrites with last-one-wins. // Multiple calls overwrites with last-one-wins.
WithSpanKind(SpanKindClient), WithSpanKind(SpanKindClient),
WithSpanKind(SpanKindConsumer), WithSpanKind(SpanKindConsumer),
}, },
&SpanConfig{ &SpanConfig{
SpanKind: SpanKindConsumer, spanKind: SpanKindConsumer,
}, },
}, },
{ {
// Everything should work together. // Everything should work together.
[]SpanOption{ []SpanStartOption{
WithAttributes(k1v1), WithAttributes(k1v1),
WithTimestamp(timestamp0), WithTimestamp(timestamp0),
WithLinks(link1, link2), WithLinks(link1, link2),
@ -161,16 +161,16 @@ func TestNewSpanConfig(t *testing.T) {
WithSpanKind(SpanKindConsumer), WithSpanKind(SpanKindConsumer),
}, },
&SpanConfig{ &SpanConfig{
Attributes: []attribute.KeyValue{k1v1}, attributes: []attribute.KeyValue{k1v1},
Timestamp: timestamp0, timestamp: timestamp0,
Links: []Link{link1, link2}, links: []Link{link1, link2},
NewRoot: true, newRoot: true,
SpanKind: SpanKindConsumer, spanKind: SpanKindConsumer,
}, },
}, },
} }
for _, test := range tests { for _, test := range tests {
assert.Equal(t, test.expected, NewSpanConfig(test.options...)) assert.Equal(t, test.expected, NewSpanStartConfig(test.options...))
} }
} }
@ -191,7 +191,7 @@ func TestTracerConfig(t *testing.T) {
WithInstrumentationVersion(v1), WithInstrumentationVersion(v1),
}, },
&TracerConfig{ &TracerConfig{
InstrumentationVersion: v1, instrumentationVersion: v1,
}, },
}, },
{ {
@ -201,7 +201,7 @@ func TestTracerConfig(t *testing.T) {
WithInstrumentationVersion(v2), WithInstrumentationVersion(v2),
}, },
&TracerConfig{ &TracerConfig{
InstrumentationVersion: v2, instrumentationVersion: v2,
}, },
}, },
} }

View File

@ -43,7 +43,7 @@ type noopTracer struct{}
var _ Tracer = noopTracer{} var _ Tracer = noopTracer{}
// Start starts a noop span. // Start starts a noop span.
func (t noopTracer) Start(ctx context.Context, name string, _ ...SpanOption) (context.Context, Span) { func (t noopTracer) Start(ctx context.Context, name string, _ ...SpanStartOption) (context.Context, Span) {
span := noopSpan{} span := noopSpan{}
return ContextWithSpan(ctx, span), span return ContextWithSpan(ctx, span), span
} }
@ -69,7 +69,7 @@ func (noopSpan) SetError(bool) {}
func (noopSpan) SetAttributes(...attribute.KeyValue) {} func (noopSpan) SetAttributes(...attribute.KeyValue) {}
// End does nothing. // End does nothing.
func (noopSpan) End(...SpanOption) {} func (noopSpan) End(...SpanEndOption) {}
// RecordError does nothing. // RecordError does nothing.
func (noopSpan) RecordError(error, ...EventOption) {} func (noopSpan) RecordError(error, ...EventOption) {}

View File

@ -342,7 +342,7 @@ type Span interface {
// delivered through the rest of the telemetry pipeline after this method // delivered through the rest of the telemetry pipeline after this method
// is called. Therefore, updates to the Span are not allowed after this // is called. Therefore, updates to the Span are not allowed after this
// method has been called. // method has been called.
End(options ...SpanOption) End(options ...SpanEndOption)
// AddEvent adds an event with the provided name and options. // AddEvent adds an event with the provided name and options.
AddEvent(name string, options ...EventOption) AddEvent(name string, options ...EventOption)
@ -488,7 +488,7 @@ type Tracer interface {
// //
// Any Span that is created MUST also be ended. This is the responsibility of the user. // Any Span that is created MUST also be ended. This is the responsibility of the user.
// Implementations of this API may leak memory or other resources if Spans are not ended. // Implementations of this API may leak memory or other resources if Spans are not ended.
Start(ctx context.Context, spanName string, opts ...SpanOption) (context.Context, Span) Start(ctx context.Context, spanName string, opts ...SpanStartOption) (context.Context, Span)
} }
// TracerProvider provides access to instrumentation Tracers. // TracerProvider provides access to instrumentation Tracers.