blob: 06839dc2d73be120020a0cde73a41ce71deea2de [file] [log] [blame]
// 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 (
"reflect"
"runtime/debug"
"github.com/pkg/errors"
"github.com/apache/skywalking-go/plugins/core/reporter"
"github.com/apache/skywalking-go/plugins/core/tracing"
)
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()
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() {
tracingSpan.SetOperationName(operationName)
return tracingSpan, nil
}
var ref = &SpanContext{}
if err := ref.Decode(extractor.(tracing.ExtractorWrapper).Fun()); err != nil {
return nil, err
}
if !ref.Valid {
ref = nil
}
return t.createSpan0(tracingSpan, opts, withRef(ref), withSpanType(SpanTypeEntry), withOperationName(operationName))
}
func (t *Tracer) CreateLocalSpan(operationName string, opts ...interface{}) (s interface{}, err error) {
ctx, tracingSpan, noop := t.createNoop()
if noop {
return tracingSpan, nil
}
defer func() {
saveSpanToActiveIfNotError(ctx, s, err)
}()
return t.createSpan0(tracingSpan, opts, withSpanType(SpanTypeLocal), withOperationName(operationName))
}
func (t *Tracer) CreateExitSpan(operationName, peer string, injector interface{}, opts ...interface{}) (s interface{}, err error) {
ctx, tracingSpan, noop := t.createNoop()
if noop {
return tracingSpan, nil
}
defer func() {
saveSpanToActiveIfNotError(ctx, s, err)
}()
span, err := t.createSpan0(tracingSpan, opts, withSpanType(SpanTypeExit), withOperationName(operationName), withPeer(peer))
if err != nil {
return nil, err
}
spanContext := &SpanContext{}
reportedSpan, ok := span.(SegmentSpan)
if !ok {
return nil, errors.New("span type is wrong")
}
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) createNoop() (*TracingContext, TracingSpan, bool) {
if !t.InitSuccess() || t.Reporter.ConnectionStatus() == reporter.ConnectionStatusDisconnect {
return nil, &NoopSpan{}, true
}
ctx := getTracingContext()
if ctx != nil {
span := ctx.ActiveSpan()
_, ok := span.(*NoopSpan)
return ctx, span, ok
}
return nil, nil, false
}
func (t *Tracer) createSpan0(parent TracingSpan, pluginOpts []interface{}, coreOpts ...interface{}) (s TracingSpan, 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 {
// Force sample
sampled := t.Sampler.IsSampled(ds.OperationName)
if !sampled {
// Filter by sample just return noop span
s = &NoopSpan{}
return s, nil
}
}
// process the opts from agent core for prepare building segment span
for _, opt := range coreOpts {
opt.(tracing.SpanOption).Apply(ds)
}
s, err = NewSegmentSpan(ds, parentSpan)
if err != nil {
return nil, 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)
}
return s, 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
}
if ctx == nil {
ctx = NewTracingContext()
}
ctx.SaveActiveSpan(span.(TracingSpan))
SetGLS(ctx)
}