--- /dev/null
+// 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,
+ }
+}