// 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 event provides support for event based telemetry. package event import ( "fmt" "time" ) 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 ) // sTags is used to hold a small number of tags inside an event whichout // requiring a separate allocation. // As tags are often on the stack, this avoids an allocation at all for // the very common cases of simple events. // The length needs to be large enough to cope with the majority of events // but no so large as to cause undue stack pressure. // A log message with two values will use 3 tags (one for each value and // one for the message itself). type sTags [3]Tag // Event holds the information about an event of note that ocurred. type Event struct { At time.Time typ eventType static sTags // inline storage for the first few tags dynamic []Tag // dynamically sized storage for remaining tags } // eventTagMap implements TagMap for a the tags of an Event. type eventTagMap struct { event Event } 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) { tagMap := TagMap(ev) if !ev.At.IsZero() { fmt.Fprint(f, ev.At.Format("2006/01/02 15:04:05 ")) } msg := Msg.Get(tagMap) err := Err.Get(tagMap) fmt.Fprint(f, msg) if err != nil { if f.Flag('+') { fmt.Fprintf(f, ": %+v", err) } else { fmt.Fprintf(f, ": %v", err) } } for index := 0; ev.Valid(index); index++ { tag := ev.Tag(index) // msg and err were both already printed above, so we skip them to avoid // double printing if !tag.Valid() || tag.Key() == Msg || tag.Key() == Err { continue } fmt.Fprintf(f, "\n\t%v", tag) } } func (ev Event) Valid(index int) bool { return index >= 0 && index < len(ev.static)+len(ev.dynamic) } func (ev Event) Tag(index int) Tag { if index < len(ev.static) { return ev.static[index] } return ev.dynamic[index-len(ev.static)] } func (ev Event) Find(key Key) Tag { for _, tag := range ev.static { if tag.Key() == key { return tag } } for _, tag := range ev.dynamic { if tag.Key() == key { return tag } } return Tag{} } func makeEvent(typ eventType, static sTags, tags []Tag) Event { return Event{ typ: typ, static: static, dynamic: tags, } }