event: add Event.Clone
Add a method for cloning an Event.
This lets users who plan to deliver many events for the same context
to amortize the context lookup.
Change-Id: I71624a8f42508fb70142c85dd9d390a903f2eb6a
Reviewed-on: https://go-review.googlesource.com/c/exp/+/329789
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/event/adapter/logr/logr.go b/event/adapter/logr/logr.go
index 6877591..2684547 100644
--- a/event/adapter/logr/logr.go
+++ b/event/adapter/logr/logr.go
@@ -14,7 +14,7 @@
)
type logger struct {
- ctx context.Context
+ ev *event.Event // cloned, never delivered
labels []event.Label
nameSep string
name string
@@ -25,7 +25,7 @@
func NewLogger(ctx context.Context, nameSep string) logr.Logger {
return &logger{
- ctx: ctx,
+ ev: event.New(ctx, event.LogKind),
nameSep: nameSep,
}
}
@@ -69,10 +69,10 @@
// variable information. The key/value pairs should alternate string
// keys and arbitrary values.
func (l *logger) Info(msg string, keysAndValues ...interface{}) {
- ev := event.New(l.ctx, event.LogKind)
- if ev != nil {
- l.log(ev, msg, keysAndValues)
+ if l.ev == nil {
+ return
}
+ l.log(l.ev.Clone(), msg, keysAndValues)
}
// Error logs an error, with the given message and key/value pairs as context.
@@ -84,11 +84,12 @@
// while the err field should be used to attach the actual error that
// triggered this log line, if present.
func (l *logger) Error(err error, msg string, keysAndValues ...interface{}) {
- ev := event.New(l.ctx, event.LogKind)
- if ev != nil {
- ev.Labels = append(ev.Labels, event.Value("error", err))
- l.log(ev, msg, keysAndValues)
+ if l.ev == nil {
+ return
}
+ ev := l.ev.Clone()
+ ev.Labels = append(ev.Labels, event.Value("error", err))
+ l.log(ev, msg, keysAndValues)
}
func (l *logger) log(ev *event.Event, msg string, keysAndValues []interface{}) {
diff --git a/event/adapter/zap/zap.go b/event/adapter/zap/zap.go
index 013a987..df46bcc 100644
--- a/event/adapter/zap/zap.go
+++ b/event/adapter/zap/zap.go
@@ -26,14 +26,14 @@
)
type core struct {
- ctx context.Context
+ ev *event.Event // cloned but never delivered
labels []event.Label
}
var _ zapcore.Core = (*core)(nil)
func NewCore(ctx context.Context) zapcore.Core {
- return &core{ctx: ctx}
+ return &core{ev: event.New(ctx, event.LogKind)}
}
func (c *core) Enabled(level zapcore.Level) bool {
@@ -53,15 +53,19 @@
}
func (c *core) Write(e zapcore.Entry, fs []zapcore.Field) error {
- ev := event.New(c.ctx, event.LogKind)
+ if c.ev == nil {
+ return nil
+ }
+ ev := c.ev.Clone()
if ev == nil {
return nil
}
ev.At = e.Time
+ // TODO: Add labels more efficiently: compare cap(ev.Labels) to the total number to add,
+ // and allocate a []Label once.
ev.Labels = append(ev.Labels, c.labels...)
ev.Labels = append(ev.Labels, convertLevel(e.Level).Label())
ev.Labels = append(ev.Labels, event.String("name", e.LoggerName))
- // TODO: add these additional labels more efficiently.
if e.Stack != "" {
ev.Labels = append(ev.Labels, event.String("stack", e.Stack))
}
@@ -89,8 +93,7 @@
zapcore.ErrorType:
return event.Value(f.Key, f.Interface)
case zapcore.DurationType:
- // TODO: avoid this allocation?
- return event.Value(f.Key, time.Duration(f.Integer))
+ return event.Duration(f.Key, time.Duration(f.Integer))
case zapcore.Float64Type:
return event.Float64(f.Key, math.Float64frombits(uint64(f.Integer)))
case zapcore.Float32Type:
diff --git a/event/event.go b/event/event.go
index 7c3aed6..485e706 100644
--- a/event/event.go
+++ b/event/event.go
@@ -95,16 +95,26 @@
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
+}
+
// 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 {
+ e := ev.target.exporter
// get the event ready to send
- ev.target.exporter.prepare(ev)
+ e.prepare(ev)
// now hold the lock while we deliver the event
- ev.target.exporter.mu.Lock()
- defer ev.target.exporter.mu.Unlock()
- ctx := ev.target.exporter.handler.Event(ev.ctx, ev)
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ ctx := e.handler.Event(ev.ctx, ev)
eventPool.Put(ev)
return ctx
}