|  | // 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 | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "fmt" | 
|  | "sync" | 
|  | ) | 
|  |  | 
|  | const ( | 
|  | MetricKey      = interfaceKey("metric") | 
|  | MetricVal      = "metricValue" | 
|  | DurationMetric = interfaceKey("durationMetric") | 
|  | ) | 
|  |  | 
|  | type Kind int | 
|  |  | 
|  | const ( | 
|  | unknownKind = Kind(iota) | 
|  |  | 
|  | LogKind | 
|  | MetricKind | 
|  | StartKind | 
|  | EndKind | 
|  |  | 
|  | dynamicKindStart | 
|  | ) | 
|  |  | 
|  | type ( | 
|  | valueKey     string | 
|  | interfaceKey string | 
|  | ) | 
|  |  | 
|  | var ( | 
|  | dynamicKindMu    sync.Mutex | 
|  | nextDynamicKind  = dynamicKindStart | 
|  | dynamicKindNames map[Kind]string | 
|  | ) | 
|  |  | 
|  | func NewKind(name string) Kind { | 
|  | dynamicKindMu.Lock() | 
|  | defer dynamicKindMu.Unlock() | 
|  | for _, n := range dynamicKindNames { | 
|  | if n == name { | 
|  | panic(fmt.Errorf("kind %s is already registered", name)) | 
|  | } | 
|  | } | 
|  | k := nextDynamicKind | 
|  | nextDynamicKind++ | 
|  | dynamicKindNames[k] = name | 
|  | return k | 
|  | } | 
|  |  | 
|  | func (k Kind) String() string { | 
|  | switch k { | 
|  | case unknownKind: | 
|  | return "unknown" | 
|  | case LogKind: | 
|  | return "log" | 
|  | case MetricKind: | 
|  | return "metric" | 
|  | case StartKind: | 
|  | return "start" | 
|  | case EndKind: | 
|  | return "end" | 
|  | default: | 
|  | dynamicKindMu.Lock() | 
|  | defer dynamicKindMu.Unlock() | 
|  | name, ok := dynamicKindNames[k] | 
|  | if !ok { | 
|  | return fmt.Sprintf("?unknownKind:%d?", k) | 
|  | } | 
|  | return name | 
|  | } | 
|  | } | 
|  |  | 
|  | func Log(ctx context.Context, msg string, labels ...Label) { | 
|  | ev := New(ctx, LogKind) | 
|  | if ev != nil { | 
|  | ev.Labels = append(ev.Labels, labels...) | 
|  | ev.Labels = append(ev.Labels, String("msg", msg)) | 
|  | ev.Deliver() | 
|  | } | 
|  | } | 
|  |  | 
|  | func Logf(ctx context.Context, msg string, args ...interface{}) { | 
|  | ev := New(ctx, LogKind) | 
|  | if ev != nil { | 
|  | ev.Labels = append(ev.Labels, String("msg", fmt.Sprintf(msg, args...))) | 
|  | ev.Deliver() | 
|  | } | 
|  | } | 
|  |  | 
|  | func Error(ctx context.Context, msg string, err error, labels ...Label) { | 
|  | ev := New(ctx, LogKind) | 
|  | if ev != nil { | 
|  | ev.Labels = append(ev.Labels, labels...) | 
|  | ev.Labels = append(ev.Labels, String("msg", msg), Value("error", err)) | 
|  | ev.Deliver() | 
|  | } | 
|  | } | 
|  |  | 
|  | func Annotate(ctx context.Context, labels ...Label) { | 
|  | ev := New(ctx, 0) | 
|  | if ev != nil { | 
|  | ev.Labels = append(ev.Labels, labels...) | 
|  | ev.Deliver() | 
|  | } | 
|  | } | 
|  |  | 
|  | func Start(ctx context.Context, name string, labels ...Label) context.Context { | 
|  | ev := New(ctx, StartKind) | 
|  | if ev != nil { | 
|  | ev.Labels = append(ev.Labels, String("name", name)) | 
|  | ev.Labels = append(ev.Labels, labels...) | 
|  | ev.Trace() | 
|  | ctx = ev.Deliver() | 
|  | } | 
|  | return ctx | 
|  | } | 
|  |  | 
|  | func End(ctx context.Context, labels ...Label) { | 
|  | ev := New(ctx, EndKind) | 
|  | if ev != nil { | 
|  | ev.Labels = append(ev.Labels, labels...) | 
|  | ev.prepare() | 
|  | // this was an end event, do we need to send a duration? | 
|  | if v, ok := DurationMetric.Find(ev); ok { | 
|  | //TODO: do we want the rest of the values from the end event? | 
|  | v.(*DurationDistribution).Record(ctx, ev.At.Sub(ev.target.startTime)) | 
|  | } | 
|  | ev.Deliver() | 
|  | } | 
|  | } | 
|  |  | 
|  | func (k interfaceKey) Of(v interface{}) Label { | 
|  | return Value(string(k), v) | 
|  | } | 
|  |  | 
|  | func (k interfaceKey) Find(ev *Event) (interface{}, bool) { | 
|  | v, ok := lookupValue(string(k), ev.Labels) | 
|  | if !ok { | 
|  | return nil, false | 
|  | } | 
|  | return v.Interface(), true | 
|  |  | 
|  | } | 
|  |  | 
|  | func lookupValue(name string, labels []Label) (Label, bool) { | 
|  | for i := len(labels) - 1; i >= 0; i-- { | 
|  | if labels[i].Name == name { | 
|  | return labels[i], true | 
|  | } | 
|  | } | 
|  | return Label{}, false | 
|  | } |