You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
189 lines
4.2 KiB
189 lines
4.2 KiB
package trace
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
_maxLevel = 64
|
|
// hard code reset probability at 0.00025, 1/4000
|
|
_probability = 0.00025
|
|
)
|
|
|
|
// NewTracer new a tracer.
|
|
func NewTracer(serviceName string, report reporter, disableSample bool, probability float32) Tracer {
|
|
sampler := newSampler(probability)
|
|
|
|
// default internal tags
|
|
tags := extendTag()
|
|
stdlog := log.New(os.Stderr, "trace", log.LstdFlags)
|
|
return &dapper{
|
|
serviceName: serviceName,
|
|
disableSample: disableSample,
|
|
propagators: map[interface{}]propagator{
|
|
HTTPFormat: httpPropagator{},
|
|
GRPCFormat: grpcPropagator{},
|
|
},
|
|
reporter: report,
|
|
sampler: sampler,
|
|
tags: tags,
|
|
pool: &sync.Pool{New: func() interface{} { return new(Span) }},
|
|
stdlog: stdlog,
|
|
}
|
|
}
|
|
|
|
type dapper struct {
|
|
serviceName string
|
|
disableSample bool
|
|
tags []Tag
|
|
reporter reporter
|
|
propagators map[interface{}]propagator
|
|
pool *sync.Pool
|
|
stdlog *log.Logger
|
|
sampler sampler
|
|
}
|
|
|
|
func (d *dapper) New(operationName string, opts ...Option) Trace {
|
|
opt := defaultOption
|
|
for _, fn := range opts {
|
|
fn(&opt)
|
|
}
|
|
traceID := genID()
|
|
var sampled bool
|
|
var probability float32
|
|
if d.disableSample {
|
|
sampled = true
|
|
probability = 1
|
|
} else {
|
|
sampled, probability = d.sampler.IsSampled(traceID, operationName)
|
|
}
|
|
pctx := spanContext{TraceID: traceID}
|
|
if sampled {
|
|
pctx.Flags = flagSampled
|
|
pctx.Probability = probability
|
|
}
|
|
if opt.Debug {
|
|
pctx.Flags |= flagDebug
|
|
return d.newSpanWithContext(operationName, pctx).SetTag(TagString(TagSpanKind, "server")).SetTag(TagBool("debug", true))
|
|
}
|
|
// 为了兼容临时为 New 的 Span 设置 span.kind
|
|
return d.newSpanWithContext(operationName, pctx).SetTag(TagString(TagSpanKind, "server"))
|
|
}
|
|
|
|
func (d *dapper) newSpanWithContext(operationName string, pctx spanContext) Trace {
|
|
sp := d.getSpan()
|
|
// is span is not sampled just return a span with this context, no need clear it
|
|
//if !pctx.isSampled() {
|
|
// sp.context = pctx
|
|
// return sp
|
|
//}
|
|
if pctx.Level > _maxLevel {
|
|
// if span reach max limit level return noopspan
|
|
return noopspan{}
|
|
}
|
|
level := pctx.Level + 1
|
|
nctx := spanContext{
|
|
TraceID: pctx.TraceID,
|
|
ParentID: pctx.SpanID,
|
|
Flags: pctx.Flags,
|
|
Level: level,
|
|
}
|
|
if pctx.SpanID == 0 {
|
|
nctx.SpanID = pctx.TraceID
|
|
} else {
|
|
nctx.SpanID = genID()
|
|
}
|
|
sp.operationName = operationName
|
|
sp.context = nctx
|
|
sp.startTime = time.Now()
|
|
sp.tags = append(sp.tags, d.tags...)
|
|
return sp
|
|
}
|
|
|
|
func (d *dapper) Inject(t Trace, format interface{}, carrier interface{}) error {
|
|
// if carrier implement Carrier use direct, ignore format
|
|
carr, ok := carrier.(Carrier)
|
|
if ok {
|
|
t.Visit(carr.Set)
|
|
return nil
|
|
}
|
|
// use Built-in propagators
|
|
pp, ok := d.propagators[format]
|
|
if !ok {
|
|
return ErrUnsupportedFormat
|
|
}
|
|
carr, err := pp.Inject(carrier)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if t != nil {
|
|
t.Visit(carr.Set)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *dapper) Extract(format interface{}, carrier interface{}) (Trace, error) {
|
|
sp, err := d.extract(format, carrier)
|
|
if err != nil {
|
|
return sp, err
|
|
}
|
|
// 为了兼容临时为 New 的 Span 设置 span.kind
|
|
return sp.SetTag(TagString(TagSpanKind, "server")), nil
|
|
}
|
|
|
|
func (d *dapper) extract(format interface{}, carrier interface{}) (Trace, error) {
|
|
// if carrier implement Carrier use direct, ignore format
|
|
carr, ok := carrier.(Carrier)
|
|
if !ok {
|
|
// use Built-in propagators
|
|
pp, ok := d.propagators[format]
|
|
if !ok {
|
|
return nil, ErrUnsupportedFormat
|
|
}
|
|
var err error
|
|
if carr, err = pp.Extract(carrier); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
pctx, err := contextFromString(carr.Get(KratosTraceID))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// NOTE: call SetTitle after extract trace
|
|
return d.newSpanWithContext("", pctx), nil
|
|
}
|
|
|
|
func (d *dapper) Close() error {
|
|
return d.reporter.Close()
|
|
}
|
|
|
|
func (d *dapper) report(sp *Span) {
|
|
if sp.context.isSampled() {
|
|
if err := d.reporter.WriteSpan(sp); err != nil {
|
|
d.stdlog.Printf("marshal trace span error: %s", err)
|
|
}
|
|
}
|
|
d.putSpan(sp)
|
|
}
|
|
|
|
func (d *dapper) putSpan(sp *Span) {
|
|
if len(sp.tags) > 32 {
|
|
sp.tags = nil
|
|
}
|
|
if len(sp.logs) > 32 {
|
|
sp.logs = nil
|
|
}
|
|
d.pool.Put(sp)
|
|
}
|
|
|
|
func (d *dapper) getSpan() *Span {
|
|
sp := d.pool.Get().(*Span)
|
|
sp.dapper = d
|
|
sp.childs = 0
|
|
sp.tags = sp.tags[:0]
|
|
sp.logs = sp.logs[:0]
|
|
return sp
|
|
}
|
|
|