139 строки
3.7 KiB
Go
139 строки
3.7 KiB
Go
// 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"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Event holds the information about an event that occurred.
|
|
// It combines the event metadata with the user supplied labels.
|
|
type Event struct {
|
|
ID uint64
|
|
Parent uint64 // id of the parent event for this event
|
|
Source Source // source of event; if empty, set by exporter to import path
|
|
At time.Time // time at which the event is delivered to the exporter.
|
|
Kind Kind
|
|
Labels []Label
|
|
|
|
ctx context.Context
|
|
target *target
|
|
labels [preallocateLabels]Label
|
|
}
|
|
|
|
// Handler is a the type for something that handles events as they occur.
|
|
type Handler interface {
|
|
// Event is called with each event.
|
|
Event(context.Context, *Event) context.Context
|
|
}
|
|
|
|
// preallocateLabels controls the space reserved for labels in a builder.
|
|
// Storing the first few labels directly in builders can avoid 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.
|
|
const preallocateLabels = 6
|
|
|
|
var eventPool = sync.Pool{New: func() interface{} { return &Event{} }}
|
|
|
|
// WithExporter returns a context with the exporter attached.
|
|
// The exporter is called synchronously from the event call site, so it should
|
|
// return quickly so as not to hold up user code.
|
|
func WithExporter(ctx context.Context, e *Exporter) context.Context {
|
|
return newContext(ctx, e, 0, time.Time{})
|
|
}
|
|
|
|
// SetDefaultExporter sets an exporter that is used if no exporter can be
|
|
// found on the context.
|
|
func SetDefaultExporter(e *Exporter) {
|
|
setDefaultExporter(e)
|
|
}
|
|
|
|
// New prepares a new event.
|
|
// This is intended to avoid allocations in the steady state case, to do this
|
|
// it uses a pool of events.
|
|
// Events are returned to the pool when Deliver is called. Failure to call
|
|
// Deliver will exhaust the pool and cause allocations.
|
|
// It returns nil if there is no active exporter for this kind of event.
|
|
func New(ctx context.Context, kind Kind) *Event {
|
|
var t *target
|
|
if v, ok := ctx.Value(contextKey).(*target); ok {
|
|
t = v
|
|
} else {
|
|
t = getDefaultTarget()
|
|
}
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
//TODO: we can change this to a much faster test
|
|
switch kind {
|
|
case LogKind:
|
|
if !t.exporter.loggingEnabled() {
|
|
return nil
|
|
}
|
|
case MetricKind:
|
|
if !t.exporter.metricsEnabled() {
|
|
return nil
|
|
}
|
|
case StartKind, EndKind:
|
|
if !t.exporter.tracingEnabled() {
|
|
return nil
|
|
}
|
|
}
|
|
ev := eventPool.Get().(*Event)
|
|
*ev = Event{
|
|
ctx: ctx,
|
|
target: t,
|
|
Kind: kind,
|
|
Parent: t.parent,
|
|
}
|
|
ev.Labels = ev.labels[:0]
|
|
return ev
|
|
}
|
|
|
|
// Clone makes a deep copy of the Event.
|
|
// Deliver can be called on both Events independently.
|
|
func (ev *Event) Clone() *Event {
|
|
ev2 := eventPool.Get().(*Event)
|
|
*ev2 = *ev
|
|
ev2.Labels = append(ev2.labels[:0], ev.Labels...)
|
|
return ev2
|
|
}
|
|
|
|
func (ev *Event) Trace() {
|
|
ev.prepare()
|
|
ev.ctx = newContext(ev.ctx, ev.target.exporter, ev.ID, ev.At)
|
|
}
|
|
|
|
// Deliver the event to the exporter that was found in New.
|
|
// This also returns the event to the pool, it is an error to do anything
|
|
// with the event after it is delivered.
|
|
func (ev *Event) Deliver() context.Context {
|
|
// get the event ready to send
|
|
ev.prepare()
|
|
ctx := ev.deliver()
|
|
eventPool.Put(ev)
|
|
return ctx
|
|
}
|
|
|
|
func (ev *Event) deliver() context.Context {
|
|
// hold the lock while we deliver the event
|
|
e := ev.target.exporter
|
|
e.mu.Lock()
|
|
defer e.mu.Unlock()
|
|
return e.handler.Event(ev.ctx, ev)
|
|
}
|
|
|
|
func (ev *Event) Find(name string) Label {
|
|
for _, l := range ev.Labels {
|
|
if l.Name == name {
|
|
return l
|
|
}
|
|
}
|
|
return Label{}
|
|
}
|