1
0
mirror of https://github.com/golang/go synced 2024-11-18 08:34:44 -07:00

internal/event: remove the event.eventType type

Instead of tagging events with their type, instead we infer the type from
the label pattern.
The standard event creators all have a matching test that returns true
if the the labels pattern matches the ones that would be built by the
creator.
Spans and logs already have a unique label pattern, other event types
required a special label marker.
This makes the system much more extensible, and also cleans up some
the API.

Change-Id: I1fbc9ec07aa84ead6c12bbd5ca65b13b605bfa4a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/229242
Run-TryBot: Ian Cottrell <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
This commit is contained in:
Ian Cottrell 2020-04-21 09:41:57 -04:00
parent bed5418863
commit a90b7300be
17 changed files with 194 additions and 95 deletions

View File

@ -12,24 +12,10 @@ import (
"golang.org/x/tools/internal/event/label"
)
type eventType uint8
const (
invalidType = eventType(iota)
LogType // an event that should be recorded in a log
StartSpanType // the start of a span of time
EndSpanType // the end of a span of time
LabelType // some values that should be noted for later events
DetachType // an event that causes a context to detach
RecordType // a value that should be tracked
)
// Event holds the information about an event of note that ocurred.
type Event struct {
at time.Time
typ eventType
// As events are often on the stack, storing the first few labels directly
// in the event can avoid an allocation at all for the very common cases of
// simple events.
@ -49,13 +35,6 @@ type eventLabelMap struct {
func (ev Event) At() time.Time { return ev.at }
func (ev Event) IsLog() bool { return ev.typ == LogType }
func (ev Event) IsEndSpan() bool { return ev.typ == EndSpanType }
func (ev Event) IsStartSpan() bool { return ev.typ == StartSpanType }
func (ev Event) IsLabel() bool { return ev.typ == LabelType }
func (ev Event) IsDetach() bool { return ev.typ == DetachType }
func (ev Event) IsRecord() bool { return ev.typ == RecordType }
func (ev Event) Format(f fmt.State, r rune) {
if !ev.at.IsZero() {
fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 "))
@ -92,9 +71,8 @@ func (ev Event) Find(key label.Key) label.Label {
return label.Label{}
}
func MakeEvent(typ eventType, static [3]label.Label, labels []label.Label) Event {
func MakeEvent(static [3]label.Label, labels []label.Label) Event {
return Event{
typ: typ,
static: static,
dynamic: labels,
}

View File

@ -14,28 +14,37 @@ import (
// Log1 takes a message and one label delivers a log event to the exporter.
// It is a customized version of Print that is faster and does no allocation.
func Log1(ctx context.Context, message string, t1 label.Label) {
Export(ctx, MakeEvent(LogType, [3]label.Label{keys.Msg.Of(message), t1}, nil))
Export(ctx, MakeEvent([3]label.Label{
keys.Msg.Of(message),
t1,
}, nil))
}
// Log2 takes a message and two labels and delivers a log event to the exporter.
// It is a customized version of Print that is faster and does no allocation.
func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) {
Export(ctx, MakeEvent(LogType, [3]label.Label{keys.Msg.Of(message), t1, t2}, nil))
Export(ctx, MakeEvent([3]label.Label{
keys.Msg.Of(message),
t1,
t2,
}, nil))
}
// Metric1 sends a label event to the exporter with the supplied labels.
func Metric1(ctx context.Context, t1 label.Label) context.Context {
return Export(ctx, MakeEvent(RecordType, [3]label.Label{t1}, nil))
return Export(ctx, MakeEvent([3]label.Label{
keys.Metric.New(),
t1,
}, nil))
}
// Metric2 sends a label event to the exporter with the supplied labels.
func Metric2(ctx context.Context, t1, t2 label.Label) context.Context {
return Export(ctx, MakeEvent(RecordType, [3]label.Label{t1, t2}, nil))
}
// Metric3 sends a label event to the exporter with the supplied labels.
func Metric3(ctx context.Context, t1, t2, t3 label.Label) context.Context {
return Export(ctx, MakeEvent(RecordType, [3]label.Label{t1, t2, t3}, nil))
return Export(ctx, MakeEvent([3]label.Label{
keys.Metric.New(),
t1,
t2,
}, nil))
}
// Start1 sends a span start event with the supplied label list to the exporter.
@ -43,8 +52,13 @@ func Metric3(ctx context.Context, t1, t2, t3 label.Label) context.Context {
// deferred.
func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) {
return ExportPair(ctx,
MakeEvent(StartSpanType, [3]label.Label{keys.Name.Of(name), t1}, nil),
MakeEvent(EndSpanType, [3]label.Label{}, nil))
MakeEvent([3]label.Label{
keys.Start.Of(name),
t1,
}, nil),
MakeEvent([3]label.Label{
keys.End.New(),
}, nil))
}
// Start2 sends a span start event with the supplied label list to the exporter.
@ -52,6 +66,12 @@ func Start1(ctx context.Context, name string, t1 label.Label) (context.Context,
// deferred.
func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) {
return ExportPair(ctx,
MakeEvent(StartSpanType, [3]label.Label{keys.Name.Of(name), t1, t2}, nil),
MakeEvent(EndSpanType, [3]label.Label{}, nil))
MakeEvent([3]label.Label{
keys.Start.Of(name),
t1,
t2,
}, nil),
MakeEvent([3]label.Label{
keys.End.New(),
}, nil))
}

View File

@ -26,29 +26,62 @@ func SetExporter(e Exporter) {
// Log takes a message and a label list and combines them into a single event
// before delivering them to the exporter.
func Log(ctx context.Context, message string, labels ...label.Label) {
core.Export(ctx, core.MakeEvent(core.LogType, [3]label.Label{
core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Msg.Of(message),
}, labels))
}
// IsLog returns true if the event was built by the Log function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsLog(ev core.Event) bool {
return ev.Label(0).Key() == keys.Msg
}
// Error takes a message and a label list and combines them into a single event
// before delivering them to the exporter. It captures the error in the
// delivered event.
func Error(ctx context.Context, message string, err error, labels ...label.Label) {
core.Export(ctx, core.MakeEvent(core.LogType, [3]label.Label{
core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Msg.Of(message),
keys.Err.Of(err),
}, labels))
}
// IsError returns true if the event was built by the Error function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsError(ev core.Event) bool {
return ev.Label(0).Key() == keys.Msg &&
ev.Label(1).Key() == keys.Err
}
// Metric sends a label event to the exporter with the supplied labels.
func Metric(ctx context.Context, labels ...label.Label) {
core.Export(ctx, core.MakeEvent(core.RecordType, [3]label.Label{}, labels))
core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Metric.New(),
}, labels))
}
// IsMetric returns true if the event was built by the Metric function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsMetric(ev core.Event) bool {
return ev.Label(0).Key() == keys.Metric
}
// Label sends a label event to the exporter with the supplied labels.
func Label(ctx context.Context, labels ...label.Label) context.Context {
return core.Export(ctx, core.MakeEvent(core.LabelType, [3]label.Label{}, labels))
return core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Label.New(),
}, labels))
}
// IsLabel returns true if the event was built by the Label function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsLabel(ev core.Event) bool {
return ev.Label(0).Key() == keys.Label
}
// Start sends a span start event with the supplied label list to the exporter.
@ -56,14 +89,39 @@ func Label(ctx context.Context, labels ...label.Label) context.Context {
// deferred.
func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) {
return core.ExportPair(ctx,
core.MakeEvent(core.StartSpanType, [3]label.Label{
keys.Name.Of(name),
core.MakeEvent([3]label.Label{
keys.Start.Of(name),
}, labels),
core.MakeEvent(core.EndSpanType, [3]label.Label{}, nil))
core.MakeEvent([3]label.Label{
keys.End.New(),
}, nil))
}
// IsStart returns true if the event was built by the Start function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsStart(ev core.Event) bool {
return ev.Label(0).Key() == keys.Start
}
// IsEnd returns true if the event was built by the End function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsEnd(ev core.Event) bool {
return ev.Label(0).Key() == keys.End
}
// 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 core.Export(ctx, core.MakeEvent(core.DetachType, [3]label.Label{}, nil))
return core.Export(ctx, core.MakeEvent([3]label.Label{
keys.Detach.New(),
}, nil))
}
// IsDetach returns true if the event was built by the Detach function.
// It is intended to be used in exporters to identify the semantics of the
// event when deciding what to do with it.
func IsDetach(ev core.Event) bool {
return ev.Label(0).Key() == keys.Detach
}

View File

@ -34,8 +34,8 @@ type logWriter struct {
func (w *logWriter) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) context.Context {
switch {
case ev.IsLog():
if w.onlyErrors && keys.Err.Get(lm) == nil {
case event.IsLog(ev):
if w.onlyErrors && !event.IsError(ev) {
return ctx
}
w.mu.Lock()
@ -63,14 +63,14 @@ func (w *logWriter) ProcessEvent(ctx context.Context, ev core.Event, lm label.Ma
}
io.WriteString(w.writer, "\n")
case ev.IsStartSpan():
case event.IsStart(ev):
if span := GetSpan(ctx); span != nil {
fmt.Fprintf(w.writer, "start: %v %v", span.Name, span.ID)
if span.ParentID.IsValid() {
fmt.Fprintf(w.writer, "[%v]", span.ParentID)
}
}
case ev.IsEndSpan():
case event.IsEnd(ev):
if span := GetSpan(ctx); span != nil {
fmt.Fprintf(w.writer, "finish: %v %v", span.Name, span.ID)
}

View File

@ -34,7 +34,7 @@ func (e *Config) subscribe(key label.Key, s subscriber) {
func (e *Config) Exporter(output event.Exporter) event.Exporter {
var mu sync.Mutex
return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
if !ev.IsRecord() {
if !event.IsMetric(ev) {
return output(ctx, ev, lm)
}
mu.Lock()

View File

@ -18,6 +18,7 @@ import (
"sync"
"time"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/core"
"golang.org/x/tools/internal/event/export"
"golang.org/x/tools/internal/event/export/metric"
@ -89,14 +90,14 @@ func Connect(config *Config) *Exporter {
func (e *Exporter) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) context.Context {
switch {
case ev.IsEndSpan():
case event.IsEnd(ev):
e.mu.Lock()
defer e.mu.Unlock()
span := export.GetSpan(ctx)
if span != nil {
e.spans = append(e.spans, span)
}
case ev.IsRecord():
case event.IsMetric(ev):
e.mu.Lock()
defer e.mu.Unlock()
data := metric.Entries.Get(lm).([]metric.Data)
@ -202,7 +203,7 @@ func convertSpan(span *export.Span) *wire.Span {
Kind: wire.UnspecifiedSpanKind,
StartTime: convertTimestamp(span.Start().At()),
EndTime: convertTimestamp(span.Finish().At()),
Attributes: convertAttributes(label.Filter(span.Start(), keys.Name)),
Attributes: convertAttributes(span.Start(), 1),
TimeEvents: convertEvents(span.Events()),
SameProcessAsParentSpan: true,
//TODO: StackTrace?
@ -229,18 +230,20 @@ func convertMetric(data metric.Data, start time.Time) *wire.Metric {
}
}
func skipToValidLabel(list label.List) (int, label.Label) {
func skipToValidLabel(list label.List, index int) (int, label.Label) {
// skip to the first valid label
for index := 0; list.Valid(index); index++ {
if l := list.Label(index); l.Valid() {
return index, l
for ; list.Valid(index); index++ {
l := list.Label(index)
if !l.Valid() || l.Key() == keys.Label {
continue
}
return index, l
}
return -1, label.Label{}
}
func convertAttributes(list label.List) *wire.Attributes {
index, l := skipToValidLabel(list)
func convertAttributes(list label.List, index int) *wire.Attributes {
index, l := skipToValidLabel(list, index)
if !l.Valid() {
return nil
}
@ -312,22 +315,31 @@ func convertEvent(ev core.Event) wire.TimeEvent {
}
}
func convertAnnotation(ev core.Event) *wire.Annotation {
if _, l := skipToValidLabel(ev); !l.Valid() {
return nil
func getAnnotationDescription(ev core.Event) (string, int) {
l := ev.Label(0)
if l.Key() != keys.Msg {
return "", 0
}
lm := label.Map(ev)
description := keys.Msg.Get(lm)
labels := label.Filter(ev, keys.Msg)
if description == "" {
err := keys.Err.Get(lm)
labels = label.Filter(labels, keys.Err)
if err != nil {
description = err.Error()
}
if msg := keys.Msg.From(l); msg != "" {
return msg, 1
}
l = ev.Label(1)
if l.Key() != keys.Err {
return "", 1
}
if err := keys.Err.From(l); err != nil {
return err.Error(), 2
}
return "", 2
}
func convertAnnotation(ev core.Event) *wire.Annotation {
description, index := getAnnotationDescription(ev)
if _, l := skipToValidLabel(ev, index); !l.Valid() && description == "" {
return nil
}
return &wire.Annotation{
Description: toTruncatableString(description),
Attributes: convertAttributes(labels),
Attributes: convertAttributes(ev, index),
}
}

View File

@ -129,9 +129,9 @@ func timeFixer(output event.Exporter) event.Exporter {
end, _ := time.Parse(time.RFC3339Nano, "1970-01-01T00:00:50Z")
return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
switch {
case ev.IsStartSpan():
case event.IsStart(ev):
ev = core.CloneEvent(ev, start)
case ev.IsEndSpan():
case event.IsEnd(ev):
ev = core.CloneEvent(ev, end)
default:
ev = core.CloneEvent(ev, at)
@ -142,7 +142,7 @@ func timeFixer(output event.Exporter) event.Exporter {
func spanFixer(output event.Exporter) event.Exporter {
return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
if ev.IsStartSpan() {
if event.IsStart(ev) {
span := export.GetSpan(ctx)
span.ID = export.SpanContext{}
}

View File

@ -10,8 +10,6 @@ import (
"testing"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/core"
"golang.org/x/tools/internal/event/label"
)
func TestTrace(t *testing.T) {
@ -40,7 +38,7 @@ func TestTrace(t *testing.T) {
{
name: "no labels",
run: func(ctx context.Context) {
core.Export(ctx, core.MakeEvent(core.LogType, [3]label.Label{}, nil))
event.Label(ctx)
},
want: prefix + `
"timeEvent":[{"time":"1970-01-01T00:00:40Z"}]

View File

@ -12,6 +12,7 @@ import (
"sort"
"sync"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/core"
"golang.org/x/tools/internal/event/export/metric"
"golang.org/x/tools/internal/event/label"
@ -27,7 +28,7 @@ type Exporter struct {
}
func (e *Exporter) ProcessEvent(ctx context.Context, ev core.Event, ln label.Map) context.Context {
if !ev.IsRecord() {
if !event.IsMetric(ev) {
return ctx
}
e.mu.Lock()

View File

@ -20,7 +20,7 @@ import (
func Labels(output event.Exporter) event.Exporter {
return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
stored, _ := ctx.Value(labelContextKey).(label.Map)
if ev.IsLabel() || ev.IsStartSpan() {
if event.IsLabel(ev) || event.IsStart(ev) {
// update the label map stored in the context
fromEvent := label.Map(ev)
if stored == nil {

View File

@ -53,15 +53,15 @@ func GetSpan(ctx context.Context) *Span {
func Spans(output event.Exporter) event.Exporter {
return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
switch {
case ev.IsLog(), ev.IsLabel():
case event.IsLog(ev), event.IsLabel(ev):
if span := GetSpan(ctx); span != nil {
span.mu.Lock()
span.events = append(span.events, ev)
span.mu.Unlock()
}
case ev.IsStartSpan():
case event.IsStart(ev):
span := &Span{
Name: keys.Name.Get(lm),
Name: keys.Start.Get(lm),
start: ev,
}
if parent := GetSpan(ctx); parent != nil {
@ -72,13 +72,13 @@ func Spans(output event.Exporter) event.Exporter {
}
span.ID.SpanID = newSpanID()
ctx = context.WithValue(ctx, spanContextKey, span)
case ev.IsEndSpan():
case event.IsEnd(ev):
if span := GetSpan(ctx); span != nil {
span.mu.Lock()
span.finish = ev
span.mu.Unlock()
}
case ev.IsDetach():
case event.IsDetach(ev):
ctx = context.WithValue(ctx, spanContextKey, nil)
}
return output(ctx, ev, lm)

View File

@ -45,6 +45,28 @@ func (k *Value) From(t label.Label) interface{} { return t.UnpackValue() }
// Of creates a new Label with this key and the supplied value.
func (k *Value) Of(value interface{}) label.Label { return label.OfValue(k, value) }
// Tag represents a key for tagging labels that have no value.
// These are used when the existence of the label is the entire information it
// carries, such as marking events to be of a specific kind, or from a specific
// package.
type Tag struct {
name string
description string
}
// NewTag creates a new Key for tagging labels.
func NewTag(name, description string) *Tag {
return &Tag{name: name, description: description}
}
func (k *Tag) Name() string { return k.name }
func (k *Tag) Description() string { return k.description }
func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {}
// New creates a new Label with this key.
func (k *Tag) New() label.Label { return label.OfValue(k, nil) }
// Int represents a key
type Int struct {
name string

View File

@ -7,8 +7,16 @@ package keys
var (
// Msg is a key used to add message strings to label lists.
Msg = NewString("message", "a readable message")
// Name is used for things like traces that have a name.
Name = NewString("name", "an entity name")
// Label is a key used to indicate an event adds labels to the context.
Label = NewTag("label", "a label context marker")
// Start is used for things like traces that have a name.
Start = NewString("start", "span start")
// Metric is a key used to indicate an event records metrics.
End = NewTag("end", "a span end marker")
// Metric is a key used to indicate an event records metrics.
Detach = NewTag("detach", "a span detach marker")
// Err is a key used to add error values to label lists.
Err = NewError("error", "an error that occurred")
// Metric is a key used to indicate an event records metrics.
Metric = NewTag("metric", "a metric event marker")
)

View File

@ -13,6 +13,7 @@ import (
"sync"
"time"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/core"
"golang.org/x/tools/internal/event/export"
"golang.org/x/tools/internal/event/label"
@ -82,16 +83,16 @@ func (r *rpcs) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map) co
r.mu.Lock()
defer r.mu.Unlock()
switch {
case ev.IsStartSpan():
case event.IsStart(ev):
if _, stats := r.getRPCSpan(ctx, ev); stats != nil {
stats.Started++
}
case ev.IsEndSpan():
case event.IsEnd(ev):
span, stats := r.getRPCSpan(ctx, ev)
if stats != nil {
endRPC(ctx, ev, span, stats)
}
case ev.IsRecord():
case event.IsMetric(ev):
sent := byteUnits(tag.SentBytes.Get(lm))
rec := byteUnits(tag.ReceivedBytes.Get(lm))
if sent != 0 || rec != 0 {

View File

@ -548,7 +548,7 @@ func makeGlobalExporter(stderr io.Writer) event.Exporter {
return func(ctx context.Context, ev core.Event, lm label.Map) context.Context {
i := GetInstance(ctx)
if ev.IsLog() {
if event.IsLog(ev) {
// Don't log context cancellation errors.
if err := keys.Err.Get(ev); xerrors.Is(err, context.Canceled) {
return ctx

View File

@ -15,6 +15,7 @@ import (
"sync"
"time"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/core"
"golang.org/x/tools/internal/event/export"
"golang.org/x/tools/internal/event/label"
@ -83,7 +84,7 @@ func (t *traces) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map)
}
switch {
case ev.IsStartSpan():
case event.IsStart(ev):
if t.sets == nil {
t.sets = make(map[string]*traceSet)
t.unfinished = make(map[export.SpanContext]*traceData)
@ -110,7 +111,7 @@ func (t *traces) ProcessEvent(ctx context.Context, ev core.Event, lm label.Map)
}
parent.Children = append(parent.Children, td)
case ev.IsEndSpan():
case event.IsEnd(ev):
// finishing, must be already in the map
td, found := t.unfinished[span.ID]
if !found {

View File

@ -4,8 +4,8 @@ import (
"context"
"fmt"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/event/core"
"golang.org/x/tools/internal/event/keys"
"golang.org/x/tools/internal/event/label"
"golang.org/x/tools/internal/xcontext"
)
@ -21,7 +21,7 @@ func WithClient(ctx context.Context, client Client) context.Context {
}
func LogEvent(ctx context.Context, ev core.Event, tags label.Map) context.Context {
if !ev.IsLog() {
if !event.IsLog(ev) {
return ctx
}
client, ok := ctx.Value(clientKey).(Client)
@ -29,7 +29,7 @@ func LogEvent(ctx context.Context, ev core.Event, tags label.Map) context.Contex
return ctx
}
msg := &LogMessageParams{Type: Info, Message: fmt.Sprint(ev)}
if keys.Err.Get(tags) != nil {
if event.IsError(ev) {
msg.Type = Error
}
go client.LogMessage(xcontext.Detach(ctx), msg)