1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-03-21 21:17:35 +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)
- 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)
- 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)
- 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.

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`
how the user can extend the configuration.
It is important that `config` are not shared across package boundaries.
Meaning a `config` from one package should not be directly used by another.
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. 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
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})
}
func convertStartOptions(optFns []octrace.StartOption, name string) []trace.SpanOption {
func convertStartOptions(optFns []octrace.StartOption, name string) []trace.SpanStartOption {
var ocOpts octrace.StartOptions
for _, fn := range optFns {
fn(&ocOpts)
}
otOpts := []trace.SpanOption{}
otOpts := []trace.SpanStartOption{}
switch ocOpts.SpanKind {
case octrace.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) {
var otelOpts []trace.SpanOption
var otelOpts []trace.SpanEndOption
if !opts.FinishTime.IsZero() {
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) {
config := trace.NewSpanConfig(opts...)
startTime := config.Timestamp
func (t *MockTracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
config := trace.NewSpanStartConfig(opts...)
startTime := config.Timestamp()
if startTime.IsZero() {
startTime = time.Now()
}
@ -86,13 +86,13 @@ func (t *MockTracer) Start(ctx context.Context, name string, opts ...trace.SpanO
officialTracer: t,
spanContext: spanContext,
Attributes: baggage.NewMap(baggage.MapUpdate{
MultiKV: config.Attributes,
MultiKV: config.Attributes(),
}),
StartTime: startTime,
EndTime: time.Time{},
ParentSpanID: t.getParentSpanID(ctx, config),
Events: nil,
SpanKind: trace.ValidateSpanKind(config.SpanKind),
SpanKind: trace.ValidateSpanKind(config.SpanKind()),
}
if !migration.SkipContextSetup(ctx) {
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 {
if !config.NewRoot {
if !config.NewRoot() {
return trace.SpanContextFromContext(ctx)
}
return trace.SpanContext{}
@ -232,12 +232,12 @@ func (s *MockSpan) applyUpdate(update baggage.MapUpdate) {
s.Attributes = s.Attributes.Apply(update)
}
func (s *MockSpan) End(options ...trace.SpanOption) {
func (s *MockSpan) End(options ...trace.SpanEndOption) {
if !s.EndTime.IsZero() {
return // already finished
}
config := trace.NewSpanConfig(options...)
endTime := config.Timestamp
config := trace.NewSpanEndConfig(options...)
endTime := config.Timestamp()
if endTime.IsZero() {
endTime = time.Now()
}
@ -269,10 +269,10 @@ func (s *MockSpan) Tracer() trace.Tracer {
func (s *MockSpan) AddEvent(name string, o ...trace.EventOption) {
c := trace.NewEventConfig(o...)
s.Events = append(s.Events, MockEvent{
Timestamp: c.Timestamp,
Timestamp: c.Timestamp(),
Name: name,
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
// override the tracer of the returned span if the span implements the
// 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...)
if spanWithExtension, ok := span.(migration.OverrideTracerSpanExtension); ok {
spanWithExtension.OverrideTracer(t)

View File

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

View File

@ -92,7 +92,7 @@ func (p *tracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
key := il{
name: name,
version: trace.NewTracerConfig(opts...).InstrumentationVersion,
version: trace.NewTracerConfig(opts...).InstrumentationVersion(),
}
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
// 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()
if delegate != nil {
return delegate.(trace.Tracer).Start(ctx, name, opts...)
@ -175,7 +175,7 @@ func (nonRecordingSpan) SetError(bool) {}
func (nonRecordingSpan) SetAttributes(...attribute.KeyValue) {}
// End does nothing.
func (nonRecordingSpan) End(...trace.SpanOption) {}
func (nonRecordingSpan) End(...trace.SpanEndOption) {}
// RecordError does nothing.
func (nonRecordingSpan) RecordError(error, ...trace.EventOption) {}

View File

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

View File

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

View File

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

View File

@ -148,25 +148,31 @@ func checkSyncBatches(ctx context.Context, t *testing.T, labels []attribute.KeyV
func TestOptions(t *testing.T) {
type testcase struct {
name string
opts []metric.InstrumentOption
desc string
unit unit.Unit
name string
opts []metric.InstrumentOption
desc string
unit unit.Unit
iName string
iVer string
}
testcases := []testcase{
{
name: "no opts",
opts: nil,
desc: "",
unit: "",
name: "no opts",
opts: nil,
desc: "",
unit: "",
iName: "",
iVer: "",
},
{
name: "description",
opts: []metric.InstrumentOption{
metric.WithDescription("stuff"),
},
desc: "stuff",
unit: "",
desc: "stuff",
unit: "",
iName: "",
iVer: "",
},
{
name: "description override",
@ -174,34 +180,126 @@ func TestOptions(t *testing.T) {
metric.WithDescription("stuff"),
metric.WithDescription("things"),
},
desc: "things",
unit: "",
desc: "things",
unit: "",
iName: "",
iVer: "",
},
{
name: "unit",
opts: []metric.InstrumentOption{
metric.WithUnit("s"),
},
desc: "",
unit: "s",
desc: "",
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",
opts: []metric.InstrumentOption{
metric.WithUnit("s"),
metric.WithUnit("h"),
},
desc: "",
unit: "h",
desc: "",
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 {
t.Logf("Testing counter case %s (%d)", tt.name, idx)
if diff := cmp.Diff(metric.NewInstrumentConfig(tt.opts...), metric.InstrumentConfig{
Description: tt.desc,
Unit: tt.unit,
}); diff != "" {
t.Errorf("Compare options: -got +want %s", diff)
cfg := metric.NewInstrumentConfig(tt.opts...)
if diff := cmp.Diff(cfg.Description(), tt.desc); diff != "" {
t.Errorf("Compare Description: -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{
Name: instName,
Version: conf.InstrumentationVersion,
Version: conf.InstrumentationVersion(),
}
p.tracersMu.Lock()
defer p.tracersMu.Unlock()
@ -59,7 +59,7 @@ func (p *TracerProvider) Tracer(instName string, opts ...trace.TracerOption) tra
if !ok {
t = &Tracer{
Name: instName,
Version: conf.InstrumentationVersion,
Version: conf.InstrumentationVersion(),
config: &p.config,
}
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
// SpanRecorder, that recorder's OnEnd method is called as the final part of
// this method.
func (s *Span) End(opts ...trace.SpanOption) {
func (s *Span) End(opts ...trace.SpanEndOption) {
s.lock.Lock()
defer s.lock.Unlock()
@ -57,9 +57,9 @@ func (s *Span) End(opts ...trace.SpanOption) {
return
}
c := trace.NewSpanConfig(opts...)
c := trace.NewSpanEndConfig(opts...)
s.endTime = time.Now()
if endTime := c.Timestamp; !endTime.IsZero() {
if endTime := c.Timestamp(); !endTime.IsZero() {
s.endTime = endTime
}
@ -101,15 +101,15 @@ func (s *Span) AddEvent(name string, o ...trace.EventOption) {
c := trace.NewEventConfig(o...)
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)
for _, attr := range c.Attributes {
for _, attr := range c.Attributes() {
attributes[attr.Key] = attr.Value
}
}
s.events = append(s.events, Event{
Timestamp: c.Timestamp,
Timestamp: c.Timestamp(),
Name: name,
Attributes: attributes,
})

View File

@ -36,10 +36,10 @@ type Tracer struct {
// Start creates a span. If t is configured with a SpanRecorder its OnStart
// 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) {
c := trace.NewSpanConfig(opts...)
func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
c := trace.NewSpanStartConfig(opts...)
startTime := time.Now()
if st := c.Timestamp; !st.IsZero() {
if st := c.Timestamp(); !st.IsZero() {
startTime = st
}
@ -48,10 +48,10 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...trace.SpanOptio
startTime: startTime,
attributes: make(map[attribute.Key]attribute.Value),
links: []trace.Link{},
spanKind: c.SpanKind,
spanKind: c.SpanKind(),
}
if c.NewRoot {
if c.NewRoot() {
span.spanContext = trace.SpanContext{}
} else {
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 {
if sl.SpanContext.SpanID() == link.SpanContext.SpanID() &&
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.SetAttributes(c.Attributes...)
span.SetAttributes(c.Attributes()...)
if t.config.SpanRecorder != nil {
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{
Name: name,
Version: c.InstrumentationVersion,
Version: c.InstrumentationVersion(),
}
t, ok := p.namedTracer[il]
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
// 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
// acquiring a lock. Make a minimal check that the span is not nil.
if s == nil {
@ -247,14 +247,14 @@ func (s *span) End(options ...trace.SpanOption) {
s.executionTracerTaskEnd()
}
config := trace.NewSpanConfig(options...)
config := trace.NewSpanEndConfig(options...)
s.mu.Lock()
// Setting endTime to non-zero marks the span as ended and not recording.
if config.Timestamp.IsZero() {
if config.Timestamp().IsZero() {
s.endTime = et
} else {
s.endTime = config.Timestamp
s.endTime = config.Timestamp()
}
s.mu.Unlock()
@ -305,19 +305,19 @@ func (s *span) addEvent(name string, o ...trace.EventOption) {
c := trace.NewEventConfig(o...)
// Discard over limited attributes
attributes := c.Attributes()
var discarded int
if len(c.Attributes) > s.spanLimits.AttributePerEventCountLimit {
discarded = len(c.Attributes) - s.spanLimits.AttributePerEventCountLimit
c.Attributes = c.Attributes[:s.spanLimits.AttributePerEventCountLimit]
if len(attributes) > s.spanLimits.AttributePerEventCountLimit {
discarded = len(attributes) - s.spanLimits.AttributePerEventCountLimit
attributes = attributes[:s.spanLimits.AttributePerEventCountLimit]
}
s.mu.Lock()
defer s.mu.Unlock()
s.events.add(Event{
Name: name,
Attributes: c.Attributes,
Attributes: attributes,
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
// as a parent which contains an invalid trace ID and is not remote.
var psc trace.SpanContext
if !o.NewRoot {
if !o.NewRoot() {
psc = trace.SpanContextFromContext(ctx)
}
@ -575,9 +575,9 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.Sp
ParentContext: ctx,
TraceID: tid,
Name: name,
Kind: o.SpanKind,
Attributes: o.Attributes,
Links: o.Links,
Kind: o.SpanKind(),
Attributes: o.Attributes(),
Links: o.Links(),
})
scc := trace.SpanContextConfig{
@ -596,13 +596,13 @@ func startSpanInternal(ctx context.Context, tr *tracer, name string, o *trace.Sp
return span
}
startTime := o.Timestamp
startTime := o.Timestamp()
if startTime.IsZero() {
startTime = time.Now()
}
span.startTime = startTime
span.spanKind = trace.ValidateSpanKind(o.SpanKind)
span.spanKind = trace.ValidateSpanKind(o.SpanKind())
span.name = name
span.parent = psc
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
// 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...)
}
@ -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
// context contains TraceFlags with sampled bit set. This allows the
// 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(
trace.ContextWithRemoteSpanContext(context.Background(), sc),
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
// along with the span so this parent can be used to create child
// 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,
name,

View File

@ -36,8 +36,8 @@ var _ trace.Tracer = &tracer{}
// span context found in the passed context. The created Span will be
// configured appropriately by any SpanOption passed. Any Timestamp option
// 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) {
config := trace.NewSpanConfig(options...)
func (tr *tracer) Start(ctx context.Context, name string, options ...trace.SpanStartOption) (context.Context, trace.Span) {
config := trace.NewSpanStartConfig(options...)
// For local spans created by this SDK, track child span count.
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)
for _, l := range config.Links {
for _, l := range config.Links() {
span.addLink(l)
}
span.SetAttributes(config.Attributes...)
span.SetAttributes(config.Attributes()...)
span.tracer = tr

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package trace
package trace // import "go.opentelemetry.io/otel/trace"
import (
"time"
@ -22,109 +22,171 @@ import (
// TracerConfig is a group of options for a Tracer.
type TracerConfig struct {
// InstrumentationVersion is the version of the library providing
// instrumentation.
InstrumentationVersion string
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.
func NewTracerConfig(options ...TracerOption) *TracerConfig {
config := new(TracerConfig)
for _, option := range options {
option.ApplyTracer(config)
option.apply(config)
}
return config
}
// TracerOption applies an option to a TracerConfig.
type TracerOption interface {
ApplyTracer(*TracerConfig)
apply(*TracerConfig)
}
// A private method to prevent users implementing the
// interface and so future additions to it will not
// violate compatibility.
private()
type tracerOptionFunc func(*TracerConfig)
func (fn tracerOptionFunc) apply(cfg *TracerConfig) {
fn(cfg)
}
// SpanConfig is a group of options for a Span.
type SpanConfig struct {
// Attributes describe the associated qualities of a Span.
Attributes []attribute.KeyValue
// Timestamp is a time in a Span life-cycle.
Timestamp time.Time
// Links are the associations a Span has with other Spans.
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
attributes []attribute.KeyValue
timestamp time.Time
links []Link
newRoot bool
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
// checking or bounding of data), it is left to the SDK to perform this
// action.
func NewSpanConfig(options ...SpanOption) *SpanConfig {
func NewSpanStartConfig(options ...SpanStartOption) *SpanConfig {
c := new(SpanConfig)
for _, option := range options {
option.ApplySpan(c)
option.applySpanStart(c)
}
return c
}
// SpanOption applies an option to a SpanConfig.
type SpanOption interface {
ApplySpan(*SpanConfig)
// NewSpanEndConfig applies all the options to a returned SpanConfig.
// 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
// 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
// interface and so future additions to it will not
// violate compatibility.
private()
// SpanStartOption applies an option to a SpanConfig. These options are applicable
// only when the span is created
type SpanStartOption interface {
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
// timestamp option is passed, the returned SpanConfig will have a Timestamp
// set to the call time, otherwise no validation is performed on the returned
// SpanConfig.
func NewEventConfig(options ...EventOption) *SpanConfig {
c := new(SpanConfig)
func NewEventConfig(options ...EventOption) *EventConfig {
c := new(EventConfig)
for _, option := range options {
option.ApplyEvent(c)
option.applyEvent(c)
}
if c.Timestamp.IsZero() {
c.Timestamp = time.Now()
if c.timestamp.IsZero() {
c.timestamp = time.Now()
}
return c
}
// EventOption applies span event options to a SpanConfig.
// EventOption applies span event options to an EventConfig.
type EventOption interface {
ApplyEvent(*SpanConfig)
// A private method to prevent users implementing the
// interface and so future additions to it will not
// violate compatibility.
private()
applyEvent(*EventConfig)
}
// LifeCycleOption applies span life-cycle options to a SpanConfig. These
// options set values releated to events in a spans life-cycle like starting,
// ending, experiencing an error and other user defined notable events.
type LifeCycleOption interface {
SpanOption
// SpanOption are options that can be used at both the beginning and end of a span.
type SpanOption interface {
SpanStartOption
SpanEndOption
}
// SpanStartEventOption are options that can be used at the start of a span, or with an event.
type SpanStartEventOption interface {
SpanStartOption
EventOption
}
type attributeSpanOption []attribute.KeyValue
type attributeOption []attribute.KeyValue
func (o attributeSpanOption) ApplySpan(c *SpanConfig) { o.apply(c) }
func (o attributeSpanOption) ApplyEvent(c *SpanConfig) { o.apply(c) }
func (attributeSpanOption) private() {}
func (o attributeSpanOption) apply(c *SpanConfig) {
c.Attributes = append(c.Attributes, []attribute.KeyValue(o)...)
func (o attributeOption) applySpan(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.
// 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
// option will extend the attributes instead of overwriting. There is no
// guarantee of uniqueness in the resulting attributes.
func WithAttributes(attributes ...attribute.KeyValue) LifeCycleOption {
return attributeSpanOption(attributes)
func WithAttributes(attributes ...attribute.KeyValue) SpanStartEventOption {
return attributeOption(attributes)
}
type timestampSpanOption time.Time
func (o timestampSpanOption) ApplySpan(c *SpanConfig) { o.apply(c) }
func (o timestampSpanOption) ApplyEvent(c *SpanConfig) { o.apply(c) }
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)
// SpanEventOption are options that can be used with an event or a span.
type SpanEventOption interface {
SpanOption
EventOption
}
type linksSpanOption []Link
type timestampOption time.Time
func (o linksSpanOption) ApplySpan(c *SpanConfig) { c.Links = append(c.Links, []Link(o)...) }
func (linksSpanOption) private() {}
func (o timestampOption) applySpan(c *SpanConfig) { c.timestamp = time.Time(o) }
func (o timestampOption) applySpanStart(c *SpanConfig) { o.applySpan(c) }
func (o timestampOption) applySpanEnd(c *SpanConfig) { o.applySpan(c) }
func (o timestampOption) applyEvent(c *EventConfig) { c.timestamp = time.Time(o) }
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
// links, i.e. this does not overwrite.
func WithLinks(links ...Link) SpanOption {
return linksSpanOption(links)
func WithLinks(links ...Link) SpanStartOption {
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
// existing parent span context will be ignored when defining the Span's trace
// identifiers.
func WithNewRoot() SpanOption {
return newRootSpanOption(true)
func WithNewRoot() SpanStartOption {
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.
func WithSpanKind(kind SpanKind) SpanOption {
return spanKindSpanOption(kind)
}
// InstrumentationOption is an interface for applying instrumentation specific
// options.
type InstrumentationOption interface {
TracerOption
func WithSpanKind(kind SpanKind) SpanStartOption {
return spanOptionFunc(func(cfg *SpanConfig) {
cfg.spanKind = kind
})
}
// WithInstrumentationVersion sets the instrumentation version.
func WithInstrumentationVersion(version string) InstrumentationOption {
return instrumentationVersionOption(version)
func WithInstrumentationVersion(version string) TracerOption {
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 {
options []SpanOption
options []SpanStartOption
expected *SpanConfig
}{
{
// No non-zero-values should be set.
[]SpanOption{},
[]SpanStartOption{},
new(SpanConfig),
},
{
[]SpanOption{
[]SpanStartOption{
WithAttributes(k1v1),
},
&SpanConfig{
Attributes: []attribute.KeyValue{k1v1},
attributes: []attribute.KeyValue{k1v1},
},
},
{
// Multiple calls should append not overwrite.
[]SpanOption{
[]SpanStartOption{
WithAttributes(k1v1),
WithAttributes(k1v2),
WithAttributes(k2v2),
},
&SpanConfig{
// 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),
},
&SpanConfig{
// No uniqueness is guaranteed by the API.
Attributes: []attribute.KeyValue{k1v1, k1v2, k2v2},
attributes: []attribute.KeyValue{k1v1, k1v2, k2v2},
},
},
{
[]SpanOption{
[]SpanStartOption{
WithTimestamp(timestamp0),
},
&SpanConfig{
Timestamp: timestamp0,
timestamp: timestamp0,
},
},
{
[]SpanOption{
[]SpanStartOption{
// Multiple calls overwrites with last-one-wins.
WithTimestamp(timestamp0),
WithTimestamp(timestamp1),
},
&SpanConfig{
Timestamp: timestamp1,
timestamp: timestamp1,
},
},
{
[]SpanOption{
[]SpanStartOption{
WithLinks(link1),
},
&SpanConfig{
Links: []Link{link1},
links: []Link{link1},
},
},
{
[]SpanOption{
[]SpanStartOption{
// Multiple calls should append not overwrite.
WithLinks(link1),
WithLinks(link1, link2),
},
&SpanConfig{
// No uniqueness is guaranteed by the API.
Links: []Link{link1, link1, link2},
links: []Link{link1, link1, link2},
},
},
{
[]SpanOption{
[]SpanStartOption{
WithNewRoot(),
},
&SpanConfig{
NewRoot: true,
newRoot: true,
},
},
{
[]SpanOption{
[]SpanStartOption{
// Multiple calls should not change NewRoot state.
WithNewRoot(),
WithNewRoot(),
},
&SpanConfig{
NewRoot: true,
newRoot: true,
},
},
{
[]SpanOption{
[]SpanStartOption{
WithSpanKind(SpanKindConsumer),
},
&SpanConfig{
SpanKind: SpanKindConsumer,
spanKind: SpanKindConsumer,
},
},
{
[]SpanOption{
[]SpanStartOption{
// Multiple calls overwrites with last-one-wins.
WithSpanKind(SpanKindClient),
WithSpanKind(SpanKindConsumer),
},
&SpanConfig{
SpanKind: SpanKindConsumer,
spanKind: SpanKindConsumer,
},
},
{
// Everything should work together.
[]SpanOption{
[]SpanStartOption{
WithAttributes(k1v1),
WithTimestamp(timestamp0),
WithLinks(link1, link2),
@ -161,16 +161,16 @@ func TestNewSpanConfig(t *testing.T) {
WithSpanKind(SpanKindConsumer),
},
&SpanConfig{
Attributes: []attribute.KeyValue{k1v1},
Timestamp: timestamp0,
Links: []Link{link1, link2},
NewRoot: true,
SpanKind: SpanKindConsumer,
attributes: []attribute.KeyValue{k1v1},
timestamp: timestamp0,
links: []Link{link1, link2},
newRoot: true,
spanKind: SpanKindConsumer,
},
},
}
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),
},
&TracerConfig{
InstrumentationVersion: v1,
instrumentationVersion: v1,
},
},
{
@ -201,7 +201,7 @@ func TestTracerConfig(t *testing.T) {
WithInstrumentationVersion(v2),
},
&TracerConfig{
InstrumentationVersion: v2,
instrumentationVersion: v2,
},
},
}

View File

@ -43,7 +43,7 @@ type noopTracer struct{}
var _ Tracer = noopTracer{}
// 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{}
return ContextWithSpan(ctx, span), span
}
@ -69,7 +69,7 @@ func (noopSpan) SetError(bool) {}
func (noopSpan) SetAttributes(...attribute.KeyValue) {}
// End does nothing.
func (noopSpan) End(...SpanOption) {}
func (noopSpan) End(...SpanEndOption) {}
// RecordError does nothing.
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
// is called. Therefore, updates to the Span are not allowed after this
// method has been called.
End(options ...SpanOption)
End(options ...SpanEndOption)
// AddEvent adds an event with the provided name and options.
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.
// 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.