Skip to content

Commit

Permalink
Add a Shutdown method to api TraceProvider
Browse files Browse the repository at this point in the history
- sdktraceprovider shutdown span processors
- In examples, replace processosr shutdown with
  traceprovider's shutdown

Signed-off-by: Hui Kang <[email protected]>
  • Loading branch information
Hui Kang committed Oct 16, 2020
1 parent ec300b2 commit 886cf2e
Show file tree
Hide file tree
Showing 13 changed files with 378 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- OTLP Metric exporter supports Histogram aggregation. (#1209)
- The `Code` struct from the `go.opentelemetry.io/otel/codes` package now supports JSON marshaling and unmarshaling as well as implements the `Stringer` interface. (#1214)
- A Baggage API to implement the OpenTelemetry specification. (#1217)
- Add Shutdown method to apitrace.TracerProvider (#1206)

### Changed

Expand Down
7 changes: 6 additions & 1 deletion api/global/internal/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,12 @@ func (p *tracerProvider) Tracer(name string, opts ...otel.TracerOption) otel.Tra
return t
}

// tracer is a placeholder for a otel.Tracer.
// Shutdown implements TracerProvider.
func (p *tracerProvider) Shutdown() {
p.delegate.Shutdown()
}

// tracer is a placeholder for a trace.Tracer.
//
// All Tracer functionality is forwarded to a delegate once configured.
// Otherwise, all functionality is forwarded to a NoopTracer.
Expand Down
1 change: 1 addition & 0 deletions api/global/internal/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func TestTraceWithSDK(t *testing.T) {

ctx := context.Background()
gtp := global.TracerProvider()
defer gtp.Shutdown()
tracer1 := gtp.Tracer("pre")
// This is started before an SDK was registered and should be dropped.
_, span1 := tracer1.Start(ctx, "span1")
Expand Down
22 changes: 18 additions & 4 deletions api/global/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,39 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/api/global"
"go.opentelemetry.io/otel/internal/trace/noop"
"go.opentelemetry.io/otel/sdk/trace"
)

type testTracerProvider struct{}
type testTracerProvider struct {
running bool
}

var _ otel.TracerProvider = &testTracerProvider{}

func (*testTracerProvider) Tracer(_ string, _ ...otel.TracerOption) otel.Tracer {
return noop.Tracer
}

func (tp *testTracerProvider) Shutdown() {
tp.running = false
}

func TestMultipleGlobalTracerProvider(t *testing.T) {
p1 := testTracerProvider{}
p2 := otel.NewNoopTracerProvider()
global.SetTracerProvider(&p1)
p2 := trace.NoopTracerProvider()

global.SetTracerProvider(p2)
global.SetTracerProvider(&p1)

got := global.TracerProvider()
want := p2
p1.running = true

want := &p1
if got != want {
t.Fatalf("TracerProvider: got %p, want %p\n", got, want)
}
got.Shutdown()
if p1.running {
t.Fatalf("Trace should be shutdown")
}
}
317 changes: 317 additions & 0 deletions api/trace/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package trace

import (
"context"
"time"

"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/label"
)

// TracerProvider provides access to instrumentation Tracers.
type TracerProvider interface {
// Tracer creates an implementation of the Tracer interface.
// The instrumentationName must be the name of the library providing
// instrumentation. This name may be the same as the instrumented code
// only if that code provides built-in instrumentation. If the
// instrumentationName is empty, then a implementation defined default
// name will be used instead.
Tracer(instrumentationName string, opts ...TracerOption) Tracer

// Shutdown closes the provider's resources such as the span processors.
Shutdown()
}

// TracerConfig is a group of options for a Tracer.
//
// Most users will use the tracer options instead.
type TracerConfig struct {
// InstrumentationVersion is the version of the instrumentation library.
InstrumentationVersion string
}

// NewTracerConfig applies all the options to a returned TracerConfig.
// The default value for all the fields of the returned TracerConfig are the
// default zero value of the type. Also, this does not perform any validation
// on the returned TracerConfig (e.g. no uniqueness checking or bounding of
// data), instead it is left to the implementations of the SDK to perform this
// action.
func NewTracerConfig(opts ...TracerOption) *TracerConfig {
config := new(TracerConfig)
for _, option := range opts {
option.Apply(config)
}
return config
}

// TracerOption applies an options to a TracerConfig.
type TracerOption interface {
Apply(*TracerConfig)
}

type instVersionTracerOption string

func (o instVersionTracerOption) Apply(c *TracerConfig) { c.InstrumentationVersion = string(o) }

// WithInstrumentationVersion sets the instrumentation version for a Tracer.
func WithInstrumentationVersion(version string) TracerOption {
return instVersionTracerOption(version)
}

type Tracer interface {
// Start a span.
Start(ctx context.Context, spanName string, opts ...SpanOption) (context.Context, Span)
}

// ErrorConfig provides options to set properties of an error
// event at the time it is recorded.
//
// Most users will use the error options instead.
type ErrorConfig struct {
Timestamp time.Time
StatusCode codes.Code
}

// ErrorOption applies changes to ErrorConfig that sets options when an error event is recorded.
type ErrorOption func(*ErrorConfig)

// WithErrorTime sets the time at which the error event should be recorded.
func WithErrorTime(t time.Time) ErrorOption {
return func(c *ErrorConfig) {
c.Timestamp = t
}
}

// WithErrorStatus indicates the span status that should be set when recording an error event.
func WithErrorStatus(s codes.Code) ErrorOption {
return func(c *ErrorConfig) {
c.StatusCode = s
}
}

type Span interface {
// Tracer returns tracer used to create this span. Tracer cannot be nil.
Tracer() Tracer

// End completes the span. No updates are allowed to span after it
// ends. The only exception is setting status of the span.
End(options ...SpanOption)

// AddEvent adds an event to the span.
AddEvent(ctx context.Context, name string, attrs ...label.KeyValue)
// AddEventWithTimestamp adds an event with a custom timestamp
// to the span.
AddEventWithTimestamp(ctx context.Context, timestamp time.Time, name string, attrs ...label.KeyValue)

// IsRecording returns true if the span is active and recording events is enabled.
IsRecording() bool

// RecordError records an error as a span event.
RecordError(ctx context.Context, err error, opts ...ErrorOption)

// SpanContext returns span context of the span. Returned SpanContext is usable
// even after the span ends.
SpanContext() SpanContext

// SetStatus sets the status of the span in the form of a code
// and a message. SetStatus overrides the value of previous
// calls to SetStatus on the Span.
//
// The default span status is OK, so it is not necessary to
// explicitly set an OK status on successful Spans unless it
// is to add an OK message or to override a previous status on the Span.
SetStatus(code codes.Code, msg string)

// SetName sets the name of the span.
SetName(name string)

// Set span attributes
SetAttributes(kv ...label.KeyValue)
}

// SpanConfig is a group of options for a Span.
//
// Most users will use span options instead.
type SpanConfig struct {
// Attributes describe the associated qualities of a Span.
Attributes []label.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
// Record is the recording state of a Span.
Record bool
// 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.
// The default value for all the fields of the returned SpanConfig are the
// default zero value of the type. Also, this does not perform any validation
// on the returned SpanConfig (e.g. no uniqueness checking or bounding of
// data). Instead, it is left to the implementations of the SDK to perform this
// action.
func NewSpanConfig(opts ...SpanOption) *SpanConfig {
c := new(SpanConfig)
for _, option := range opts {
option.Apply(c)
}
return c
}

// SpanOption applies an option to a SpanConfig.
type SpanOption interface {
Apply(*SpanConfig)
}

type attributeSpanOption []label.KeyValue

func (o attributeSpanOption) Apply(c *SpanConfig) {
c.Attributes = append(c.Attributes, []label.KeyValue(o)...)
}

// WithAttributes adds the attributes to a span. These attributes are meant to
// provide additional information about the work the Span represents. The
// attributes are added to the existing Span attributes, i.e. this does not
// overwrite.
func WithAttributes(attributes ...label.KeyValue) SpanOption {
return attributeSpanOption(attributes)
}

type timestampSpanOption time.Time

func (o timestampSpanOption) Apply(c *SpanConfig) { c.Timestamp = time.Time(o) }

// WithTimestamp sets the time of a Span life-cycle moment (e.g. started or
// stopped).
func WithTimestamp(t time.Time) SpanOption {
return timestampSpanOption(t)
}

type linksSpanOption []Link

func (o linksSpanOption) Apply(c *SpanConfig) { c.Links = append(c.Links, []Link(o)...) }

// 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)
}

type recordSpanOption bool

func (o recordSpanOption) Apply(c *SpanConfig) { c.Record = bool(o) }

// WithRecord specifies that the span should be recorded. It is important to
// note that implementations may override this option, i.e. if the span is a
// child of an un-sampled trace.
func WithRecord() SpanOption {
return recordSpanOption(true)
}

type newRootSpanOption bool

func (o newRootSpanOption) Apply(c *SpanConfig) { c.NewRoot = bool(o) }

// 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)
}

type spanKindSpanOption SpanKind

func (o spanKindSpanOption) Apply(c *SpanConfig) { c.SpanKind = SpanKind(o) }

// WithSpanKind sets the SpanKind of a Span.
func WithSpanKind(kind SpanKind) SpanOption {
return spanKindSpanOption(kind)
}

// Link is used to establish relationship between two spans within the same Trace or
// across different Traces. Few examples of Link usage.
// 1. Batch Processing: A batch of elements may contain elements associated with one
// or more traces/spans. Since there can only be one parent SpanContext, Link is
// used to keep reference to SpanContext of all elements in the batch.
// 2. Public Endpoint: A SpanContext in incoming client request on a public endpoint
// is untrusted from service provider perspective. In such case it is advisable to
// start a new trace with appropriate sampling decision.
// However, it is desirable to associate incoming SpanContext to new trace initiated
// on service provider side so two traces (from Client and from Service Provider) can
// be correlated.
type Link struct {
SpanContext
Attributes []label.KeyValue
}

// SpanKind represents the role of a Span inside a Trace. Often, this defines how a Span
// will be processed and visualized by various backends.
type SpanKind int

const (
// As a convenience, these match the proto definition, see
// opentelemetry/proto/trace/v1/trace.proto
//
// The unspecified value is not a valid `SpanKind`. Use
// `ValidateSpanKind()` to coerce a span kind to a valid
// value.
SpanKindUnspecified SpanKind = 0
SpanKindInternal SpanKind = 1
SpanKindServer SpanKind = 2
SpanKindClient SpanKind = 3
SpanKindProducer SpanKind = 4
SpanKindConsumer SpanKind = 5
)

// ValidateSpanKind returns a valid span kind value. This will coerce
// invalid values into the default value, SpanKindInternal.
func ValidateSpanKind(spanKind SpanKind) SpanKind {
switch spanKind {
case SpanKindInternal,
SpanKindServer,
SpanKindClient,
SpanKindProducer,
SpanKindConsumer:
// valid
return spanKind
default:
return SpanKindInternal
}
}

// String returns the specified name of the SpanKind in lower-case.
func (sk SpanKind) String() string {
switch sk {
case SpanKindInternal:
return "internal"
case SpanKindServer:
return "server"
case SpanKindClient:
return "client"
case SpanKindProducer:
return "producer"
case SpanKindConsumer:
return "consumer"
default:
return "unspecified"
}
}
Loading

0 comments on commit 886cf2e

Please sign in to comment.