blob: e5746b0c3ddb660c8d799ce74462ae6b7bc4c521 [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"
"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{}
}