mirror of
https://github.com/golang/go
synced 2024-11-19 04:04:47 -07:00
38daa6564b
Change-Id: Id6e6b121e8c69d6324a15cb46b3b9937dfe7a1dd Reviewed-on: https://go-review.googlesource.com/c/tools/+/186201 Run-TryBot: Ian Cottrell <iancottrell@google.com> Reviewed-by: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
141 lines
2.7 KiB
Go
141 lines
2.7 KiB
Go
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package trace adds support for telemetry tracing.
|
|
package trace
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"golang.org/x/tools/internal/lsp/telemetry/log"
|
|
"golang.org/x/tools/internal/lsp/telemetry/tag"
|
|
"golang.org/x/tools/internal/lsp/telemetry/worker"
|
|
)
|
|
|
|
type Span struct {
|
|
Name string
|
|
TraceID TraceID
|
|
SpanID SpanID
|
|
ParentID SpanID
|
|
Start time.Time
|
|
Finish time.Time
|
|
Tags tag.List
|
|
Events []Event
|
|
|
|
ready bool
|
|
}
|
|
|
|
type Event struct {
|
|
Time time.Time
|
|
Tags tag.List
|
|
}
|
|
|
|
type Observer func(*Span)
|
|
|
|
func RegisterObservers(o ...Observer) {
|
|
worker.Do(func() {
|
|
if !registered {
|
|
registered = true
|
|
tag.Observe(tagObserver)
|
|
log.AddLogger(logger)
|
|
}
|
|
observers = append(observers, o...)
|
|
})
|
|
}
|
|
|
|
func StartSpan(ctx context.Context, name string, tags ...tag.Tag) (context.Context, func()) {
|
|
span := &Span{
|
|
Name: name,
|
|
Start: time.Now(),
|
|
}
|
|
if parent := fromContext(ctx); parent != nil {
|
|
span.TraceID = parent.TraceID
|
|
span.ParentID = parent.SpanID
|
|
} else {
|
|
span.TraceID = newTraceID()
|
|
}
|
|
span.SpanID = newSpanID()
|
|
ctx = context.WithValue(ctx, contextKey, span)
|
|
if len(tags) > 0 {
|
|
ctx = tag.With(ctx, tags...)
|
|
}
|
|
worker.Do(func() {
|
|
span.ready = true
|
|
for _, o := range observers {
|
|
o(span)
|
|
}
|
|
})
|
|
return ctx, span.close
|
|
}
|
|
|
|
func (s *Span) close() {
|
|
now := time.Now()
|
|
worker.Do(func() {
|
|
s.Finish = now
|
|
for _, o := range observers {
|
|
o(s)
|
|
}
|
|
})
|
|
}
|
|
|
|
func (s *Span) Format(f fmt.State, r rune) {
|
|
fmt.Fprintf(f, "%v %v:%v", s.Name, s.TraceID, s.SpanID)
|
|
if s.ParentID.IsValid() {
|
|
fmt.Fprintf(f, "[%v]", s.ParentID)
|
|
}
|
|
fmt.Fprintf(f, " %v->%v", s.Start, s.Finish)
|
|
}
|
|
|
|
type contextKeyType int
|
|
|
|
var contextKey contextKeyType
|
|
|
|
func fromContext(ctx context.Context) *Span {
|
|
v := ctx.Value(contextKey)
|
|
if v == nil {
|
|
return nil
|
|
}
|
|
return v.(*Span)
|
|
}
|
|
|
|
var (
|
|
observers []Observer
|
|
registered bool
|
|
)
|
|
|
|
func tagObserver(ctx context.Context, at time.Time, tags tag.List) {
|
|
span := fromContext(ctx)
|
|
if span == nil {
|
|
return
|
|
}
|
|
if !span.ready {
|
|
span.Tags = append(span.Tags, tags...)
|
|
return
|
|
}
|
|
span.Events = append(span.Events, Event{
|
|
Time: at,
|
|
Tags: tags,
|
|
})
|
|
}
|
|
|
|
func logger(ctx context.Context, at time.Time, tags tag.List) bool {
|
|
span := fromContext(ctx)
|
|
if span == nil {
|
|
return false
|
|
}
|
|
span.Events = append(span.Events, Event{
|
|
Time: at,
|
|
Tags: tags,
|
|
})
|
|
return false
|
|
}
|
|
|
|
// Detach returns a context without an associated span.
|
|
// This allows the creation of spans that are not children of the current span.
|
|
func Detach(ctx context.Context) context.Context {
|
|
return context.WithValue(ctx, contextKey, nil)
|
|
}
|