blob: f12b84a5839ac94f4a4be35bc9ab525df448b657 [file] [log] [blame]
// 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
}