finclip-app-manager/vendor/github.com/SkyAPM/go2sky/trace.go

200 lines
5.0 KiB
Go
Raw Permalink Normal View History

2023-11-02 18:36:36 +08:00
// Licensed to SkyAPM org under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. SkyAPM org 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 go2sky
import (
"context"
"github.com/SkyAPM/go2sky/internal/idgen"
"github.com/pkg/errors"
"github.com/SkyAPM/go2sky/internal/tool"
"github.com/SkyAPM/go2sky/propagation"
)
const (
errParameter = tool.Error("parameter are nil")
EmptyTraceID = "N/A"
NoopTraceID = "[Ignored Trace]"
)
// Tracer is go2sky tracer implementation.
type Tracer struct {
service string
instance string
reporter Reporter
// 0 not init 1 init
initFlag int32
}
// TracerOption allows for functional options to adjust behaviour
// of a Tracer to be created by NewTracer
type TracerOption func(t *Tracer)
// NewTracer return a new go2sky Tracer
func NewTracer(service string, opts ...TracerOption) (tracer *Tracer, err error) {
if service == "" {
return nil, errParameter
}
t := &Tracer{
service: service,
initFlag: 0,
}
for _, opt := range opts {
opt(t)
}
if t.reporter != nil {
if t.instance == "" {
id, err := idgen.UUID()
if err != nil {
return nil, err
}
t.instance = id + "@" + tool.IPV4()
}
t.reporter.Boot(t.service, t.instance)
t.initFlag = 1
}
return t, nil
}
// CreateEntrySpan creates and starts an entry span for incoming request
func (t *Tracer) CreateEntrySpan(ctx context.Context, operationName string, extractor propagation.Extractor) (s Span, nCtx context.Context, err error) {
if ctx == nil || operationName == "" || extractor == nil {
return nil, nil, errParameter
}
if s, nCtx = t.createNoop(ctx); s != nil {
return
}
header, err := extractor()
if err != nil {
return
}
var refSc *propagation.SpanContext
if header != "" {
refSc = &propagation.SpanContext{}
err = refSc.DecodeSW8(header)
if err != nil {
return
}
}
s, nCtx, err = t.CreateLocalSpan(ctx, WithContext(refSc), WithSpanType(SpanTypeEntry))
if err != nil {
return
}
s.SetOperationName(operationName)
return
}
// CreateLocalSpan creates and starts a span for local usage
func (t *Tracer) CreateLocalSpan(ctx context.Context, opts ...SpanOption) (s Span, c context.Context, err error) {
if ctx == nil {
return nil, nil, errParameter
}
if s, c = t.createNoop(ctx); s != nil {
return
}
ds := newLocalSpan(t)
for _, opt := range opts {
opt(ds)
}
parentSpan, ok := ctx.Value(ctxKeyInstance).(segmentSpan)
if !ok {
parentSpan = nil
}
s, err = newSegmentSpan(ds, parentSpan)
if err != nil {
return nil, nil, err
}
return s, context.WithValue(ctx, ctxKeyInstance, s), nil
}
// CreateExitSpan creates and starts an exit span for client
func (t *Tracer) CreateExitSpan(ctx context.Context, operationName string, peer string, injector propagation.Injector) (Span, error) {
if ctx == nil || operationName == "" || peer == "" || injector == nil {
return nil, errParameter
}
if s, _ := t.createNoop(ctx); s != nil {
return s, nil
}
s, _, err := t.CreateLocalSpan(ctx, WithSpanType(SpanTypeExit))
if err != nil {
return nil, err
}
s.SetOperationName(operationName)
s.SetPeer(peer)
spanContext := &propagation.SpanContext{}
span, ok := s.(ReportedSpan)
if !ok {
return nil, errors.New("span type is wrong")
}
firstSpan := span.Context().FirstSpan
spanContext.Sample = 1
spanContext.TraceID = span.Context().TraceID
spanContext.ParentSegmentID = span.Context().SegmentID
spanContext.ParentSpanID = span.Context().SpanID
spanContext.ParentService = t.service
spanContext.ParentServiceInstance = t.instance
spanContext.ParentEndpoint = firstSpan.GetOperationName()
spanContext.AddressUsedAtClient = peer
err = injector(spanContext.EncodeSW8())
if err != nil {
return nil, err
}
return s, nil
}
func (t *Tracer) createNoop(ctx context.Context) (s Span, nCtx context.Context) {
if ns, ok := ctx.Value(ctxKeyInstance).(*NoopSpan); ok {
nCtx = ctx
s = ns
return
}
if t.initFlag == 0 {
s = &NoopSpan{}
nCtx = context.WithValue(ctx, ctxKeyInstance, s)
return
}
return
}
type ctxKey struct{}
var ctxKeyInstance = ctxKey{}
//Reporter is a data transit specification
type Reporter interface {
Boot(service string, serviceInstance string)
Send(spans []ReportedSpan)
Close()
}
func TraceID(ctx context.Context) string {
activeSpan := ctx.Value(ctxKeyInstance)
if activeSpan == nil {
return EmptyTraceID
}
span, ok := activeSpan.(segmentSpan)
if ok {
return span.context().TraceID
}
return NoopTraceID
}