plugins/core/tracing.go (293 lines of code) (raw):

// Licensed to Apache Software Foundation (ASF) under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Apache Software Foundation (ASF) licenses this file to you 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 core import ( "fmt" "reflect" "runtime/debug" "github.com/pkg/errors" "github.com/apache/skywalking-go/plugins/core/reporter" "github.com/apache/skywalking-go/plugins/core/tracing" ) var snapshotType = reflect.TypeOf(&SnapshotSpan{}) func (t *Tracer) Tracing() interface{} { return t } func (t *Tracer) Logger() interface{} { return t.Log } func (t *Tracer) DebugStack() []byte { return debug.Stack() } func (t *Tracer) CreateEntrySpan(operationName string, extractor interface{}, opts ...interface{}) (s interface{}, err error) { ctx, tracingSpan, noop := t.createNoop(operationName) if noop { return tracingSpan, nil } defer func() { saveSpanToActiveIfNotError(ctx, s, err) }() // if parent span is entry span, then use parent span as result if tracingSpan != nil && tracingSpan.IsEntry() && reflect.ValueOf(tracingSpan).Type() != snapshotType { tracingSpan.SetOperationName(operationName) return tracingSpan, nil } var ref = &SpanContext{} if err1 := ref.Decode(extractor.(tracing.ExtractorWrapper).Fun()); err1 != nil { return nil, err1 } if !ref.Valid { ref = nil } span, _, err := t.createSpan0(ctx, tracingSpan, opts, withRef(ref), withSpanType(SpanTypeEntry), withOperationName(operationName)) return span, err } func (t *Tracer) CreateLocalSpan(operationName string, opts ...interface{}) (s interface{}, err error) { ctx, tracingSpan, noop := t.createNoop(operationName) if noop { return tracingSpan, nil } defer func() { saveSpanToActiveIfNotError(ctx, s, err) }() span, _, err := t.createSpan0(ctx, tracingSpan, opts, withSpanType(SpanTypeLocal), withOperationName(operationName)) return span, err } func (t *Tracer) CreateExitSpan(operationName, peer string, injector interface{}, opts ...interface{}) (s interface{}, err error) { ctx, tracingSpan, noop := t.createNoop(operationName) if noop { return tracingSpan, nil } defer func() { saveSpanToActiveIfNotError(ctx, s, err) }() // if parent span is exit span, then use parent span as result if tracingSpan != nil && tracingSpan.IsExit() && reflect.ValueOf(tracingSpan).Type() != snapshotType { return tracingSpan, nil } span, noop, err := t.createSpan0(ctx, tracingSpan, opts, withSpanType(SpanTypeExit), withOperationName(operationName), withPeer(peer)) if err != nil { return nil, err } if noop { return span, nil } spanContext := &SpanContext{} reportedSpan, ok := span.(SegmentSpan) if !ok { return nil, errors.New(fmt.Sprintf("span type is wrong: %T", span)) } firstSpan := reportedSpan.GetSegmentContext().FirstSpan spanContext.Sample = 1 spanContext.TraceID = reportedSpan.GetSegmentContext().TraceID spanContext.ParentSegmentID = reportedSpan.GetSegmentContext().SegmentID spanContext.ParentSpanID = reportedSpan.GetSegmentContext().SpanID spanContext.ParentService = t.ServiceEntity.ServiceName spanContext.ParentServiceInstance = t.ServiceEntity.ServiceInstanceName spanContext.ParentEndpoint = firstSpan.GetOperationName() spanContext.AddressUsedAtClient = peer spanContext.CorrelationContext = reportedSpan.GetSegmentContext().CorrelationContext err = spanContext.Encode(injector.(tracing.InjectorWrapper).Fun()) if err != nil { return nil, err } return span, nil } func (t *Tracer) ActiveSpan() interface{} { ctx := getTracingContext() if ctx == nil || ctx.ActiveSpan() == nil { return nil } span := ctx.ActiveSpan() return span } func (t *Tracer) GetRuntimeContextValue(key string) interface{} { context := getTracingContext() if context == nil { return nil } return context.Runtime.Get(key) } func (t *Tracer) SetRuntimeContextValue(key string, value interface{}) { context := getTracingContext() if context == nil { context = NewTracingContext() SetGLS(context) } context.Runtime.Set(key, value) } func (t *Tracer) CaptureContext() interface{} { ctx := getTracingContext() if ctx == nil { return nil } snapshot := &ContextSnapshot{ activeSpan: newSnapshotSpan(ctx.ActiveSpan()), runtime: ctx.Runtime.clone(), } return snapshot } func (t *Tracer) ContinueContext(snapshot interface{}) { if snapshot == nil { return } if snap, ok := snapshot.(*ContextSnapshot); ok { ctx := getTracingContext() if ctx == nil { ctx = NewTracingContext() SetGLS(ctx) } ctx.activeSpan = snap.activeSpan ctx.Runtime = snap.runtime } } func (t *Tracer) CleanContext() { SetGLS(nil) } func (t *Tracer) GetCorrelationContextValue(key string) string { span := t.ActiveSpan() if span == nil { return "" } switch reportedSpan := span.(type) { case *SegmentSpanImpl: return reportedSpan.Context().GetCorrelationContextValue(key) case *RootSegmentSpan: return reportedSpan.Context().GetCorrelationContextValue(key) default: return "" } } func (t *Tracer) SetCorrelationContextValue(key, value string) { span := t.ActiveSpan() if span == nil { return } switch reportedSpan := span.(type) { case *SegmentSpanImpl: if len(value) > t.correlation.MaxValueSize { return } if len(reportedSpan.GetSegmentContext().CorrelationContext) >= t.correlation.MaxKeyCount { return } reportedSpan.Context().SetCorrelationContextValue(key, value) case *RootSegmentSpan: if len(value) > t.correlation.MaxValueSize { return } if len(reportedSpan.GetSegmentContext().CorrelationContext) >= t.correlation.MaxKeyCount { return } reportedSpan.Context().SetCorrelationContextValue(key, value) default: } } type ContextSnapshot struct { activeSpan TracingSpan runtime *RuntimeContext } func (s *ContextSnapshot) IsValid() bool { return s.activeSpan != nil && s.runtime != nil } func (t *Tracer) createNoop(operationName string) (*TracingContext, TracingSpan, bool) { if !t.InitSuccess() || t.Reporter.ConnectionStatus() == reporter.ConnectionStatusDisconnect { GetSo11y(t).MeasureTracingContextCreation(false, true) return nil, newNoopSpan(t), true } if tracerIgnore(operationName, t.ignoreSuffix, t.traceIgnorePath) { GetSo11y(t).MeasureTracingContextCreation(false, true) return nil, newNoopSpan(t), true } ctx := getTracingContext() if ctx != nil { span := ctx.ActiveSpan() noop, ok := span.(*NoopSpan) if ok { // increase the stack count for ensure the noop span can be clear in the context noop.enterNoSpan() } return ctx, span, ok } ctx = NewTracingContext() return ctx, nil, false } func (t *Tracer) createSpan0(ctx *TracingContext, parent TracingSpan, pluginOpts []interface{}, coreOpts ...interface{}) (s TracingSpan, noop bool, err error) { ds := NewDefaultSpan(t, parent) var parentSpan SegmentSpan if parent != nil { tmpSpan, ok := parent.(SegmentSpan) if ok { parentSpan = tmpSpan } } isForceSample := len(ds.Refs) > 0 // Try to sample when it is not force sample if parentSpan == nil && !isForceSample { isSampled := t.Sampler.IsSampled(ds.OperationName) if !isSampled { GetSo11y(t).MeasureTracingContextCreation(false, true) GetSo11y(t).MeasureLeakedTracingContext(true) // Filter by sample just return noop span return newNoopSpan(t), true, nil } } // process the opts from agent core for prepare building segment span for _, opt := range coreOpts { opt.(tracing.SpanOption).Apply(ds) } s, err = NewSegmentSpan(ctx, ds, parentSpan) if err != nil { return nil, false, err } // process the opts from plugin, split opts because the DefaultSpan not contains the tracing context information(AdaptSpan) for _, opt := range pluginOpts { opt.(tracing.SpanOption).Apply(s) } GetSo11y(t).MeasureTracingContextCreation(isForceSample, false) return s, false, nil } func withSpanType(spanType SpanType) tracing.SpanOption { return buildSpanOption(func(span *DefaultSpan) { span.SpanType = spanType }) } func withOperationName(opName string) tracing.SpanOption { return buildSpanOption(func(span *DefaultSpan) { span.OperationName = opName }) } func withRef(sc reporter.SpanContext) tracing.SpanOption { return buildSpanOption(func(span *DefaultSpan) { if sc == nil { return } v := reflect.ValueOf(sc) if v.Interface() == reflect.Zero(v.Type()).Interface() { return } span.Refs = append(span.Refs, sc) }) } func withPeer(peer string) tracing.SpanOption { return buildSpanOption(func(span *DefaultSpan) { span.Peer = peer }) } type spanOpImpl struct { exe func(s *DefaultSpan) } func (s *spanOpImpl) Apply(span interface{}) { if segmentSpan, ok := span.(*DefaultSpan); ok { s.exe(segmentSpan) } } func buildSpanOption(e func(s *DefaultSpan)) tracing.SpanOption { return &spanOpImpl{exe: e} } func getTracingContext() *TracingContext { gls := GetGLS() if gls == nil { return nil } return gls.(*TracingContext) } func saveSpanToActiveIfNotError(ctx *TracingContext, span interface{}, err error) { if err != nil || span == nil { return } ctx.SaveActiveSpan(span.(TracingSpan)) SetGLS(ctx) }