-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from charlievieth/cev/update
update imports from golang.org/x/tools d8aeb16b
- Loading branch information
Showing
65 changed files
with
19,102 additions
and
11,046 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
// Copyright 2020 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_test | ||
|
||
import ( | ||
"context" | ||
"io/ioutil" | ||
"log" | ||
"testing" | ||
|
||
"github.com/charlievieth/imports/event" | ||
"github.com/charlievieth/imports/event/core" | ||
"github.com/charlievieth/imports/event/export" | ||
"github.com/charlievieth/imports/event/keys" | ||
"github.com/charlievieth/imports/event/label" | ||
) | ||
|
||
type Hooks struct { | ||
A func(ctx context.Context, a int) (context.Context, func()) | ||
B func(ctx context.Context, b string) (context.Context, func()) | ||
} | ||
|
||
var ( | ||
aValue = keys.NewInt("a", "") | ||
bValue = keys.NewString("b", "") | ||
aCount = keys.NewInt64("aCount", "Count of time A is called.") | ||
aStat = keys.NewInt("aValue", "A value.") | ||
bCount = keys.NewInt64("B", "Count of time B is called.") | ||
bLength = keys.NewInt("BLen", "B length.") | ||
|
||
Baseline = Hooks{ | ||
A: func(ctx context.Context, a int) (context.Context, func()) { | ||
return ctx, func() {} | ||
}, | ||
B: func(ctx context.Context, b string) (context.Context, func()) { | ||
return ctx, func() {} | ||
}, | ||
} | ||
|
||
StdLog = Hooks{ | ||
A: func(ctx context.Context, a int) (context.Context, func()) { | ||
log.Printf("A where a=%d", a) | ||
return ctx, func() {} | ||
}, | ||
B: func(ctx context.Context, b string) (context.Context, func()) { | ||
log.Printf("B where b=%q", b) | ||
return ctx, func() {} | ||
}, | ||
} | ||
|
||
Log = Hooks{ | ||
A: func(ctx context.Context, a int) (context.Context, func()) { | ||
core.Log1(ctx, "A", aValue.Of(a)) | ||
return ctx, func() {} | ||
}, | ||
B: func(ctx context.Context, b string) (context.Context, func()) { | ||
core.Log1(ctx, "B", bValue.Of(b)) | ||
return ctx, func() {} | ||
}, | ||
} | ||
|
||
Trace = Hooks{ | ||
A: func(ctx context.Context, a int) (context.Context, func()) { | ||
return core.Start1(ctx, "A", aValue.Of(a)) | ||
}, | ||
B: func(ctx context.Context, b string) (context.Context, func()) { | ||
return core.Start1(ctx, "B", bValue.Of(b)) | ||
}, | ||
} | ||
|
||
Stats = Hooks{ | ||
A: func(ctx context.Context, a int) (context.Context, func()) { | ||
core.Metric1(ctx, aStat.Of(a)) | ||
core.Metric1(ctx, aCount.Of(1)) | ||
return ctx, func() {} | ||
}, | ||
B: func(ctx context.Context, b string) (context.Context, func()) { | ||
core.Metric1(ctx, bLength.Of(len(b))) | ||
core.Metric1(ctx, bCount.Of(1)) | ||
return ctx, func() {} | ||
}, | ||
} | ||
|
||
initialList = []int{0, 1, 22, 333, 4444, 55555, 666666, 7777777} | ||
stringList = []string{ | ||
"A value", | ||
"Some other value", | ||
"A nice longer value but not too long", | ||
"V", | ||
"", | ||
"ı", | ||
"prime count of values", | ||
} | ||
) | ||
|
||
type namedBenchmark struct { | ||
name string | ||
test func(*testing.B) | ||
} | ||
|
||
func Benchmark(b *testing.B) { | ||
b.Run("Baseline", Baseline.runBenchmark) | ||
b.Run("StdLog", StdLog.runBenchmark) | ||
benchmarks := []namedBenchmark{ | ||
{"Log", Log.runBenchmark}, | ||
{"Trace", Trace.runBenchmark}, | ||
{"Stats", Stats.runBenchmark}, | ||
} | ||
|
||
event.SetExporter(nil) | ||
for _, t := range benchmarks { | ||
b.Run(t.name+"NoExporter", t.test) | ||
} | ||
|
||
event.SetExporter(noopExporter) | ||
for _, t := range benchmarks { | ||
b.Run(t.name+"Noop", t.test) | ||
} | ||
|
||
event.SetExporter(export.Spans(export.LogWriter(ioutil.Discard, false))) | ||
for _, t := range benchmarks { | ||
b.Run(t.name, t.test) | ||
} | ||
} | ||
|
||
func A(ctx context.Context, hooks Hooks, a int) int { | ||
ctx, done := hooks.A(ctx, a) | ||
defer done() | ||
return B(ctx, hooks, a, stringList[a%len(stringList)]) | ||
} | ||
|
||
func B(ctx context.Context, hooks Hooks, a int, b string) int { | ||
_, done := hooks.B(ctx, b) | ||
defer done() | ||
return a + len(b) | ||
} | ||
|
||
func (hooks Hooks) runBenchmark(b *testing.B) { | ||
ctx := context.Background() | ||
b.ReportAllocs() | ||
b.ResetTimer() | ||
var acc int | ||
for i := 0; i < b.N; i++ { | ||
for _, value := range initialList { | ||
acc += A(ctx, hooks, value) | ||
} | ||
} | ||
} | ||
|
||
func init() { | ||
log.SetOutput(ioutil.Discard) | ||
} | ||
|
||
func noopExporter(ctx context.Context, ev core.Event, lm label.Map) context.Context { | ||
return ctx | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// 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 core provides support for event based telemetry. | ||
package core | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
"github.com/charlievieth/imports/event/label" | ||
) | ||
|
||
// Event holds the information about an event of note that occurred. | ||
type Event struct { | ||
at time.Time | ||
|
||
// As events are often on the stack, storing the first few labels directly | ||
// in the event 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. | ||
// A log message with two values will use 3 labels (one for each value and | ||
// one for the message itself). | ||
|
||
static [3]label.Label // inline storage for the first few labels | ||
dynamic []label.Label // dynamically sized storage for remaining labels | ||
} | ||
|
||
// eventLabelMap implements label.Map for a the labels of an Event. | ||
type eventLabelMap struct { | ||
event Event | ||
} | ||
|
||
func (ev Event) At() time.Time { return ev.at } | ||
|
||
func (ev Event) Format(f fmt.State, r rune) { | ||
if !ev.at.IsZero() { | ||
fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 ")) | ||
} | ||
for index := 0; ev.Valid(index); index++ { | ||
if l := ev.Label(index); l.Valid() { | ||
fmt.Fprintf(f, "\n\t%v", l) | ||
} | ||
} | ||
} | ||
|
||
func (ev Event) Valid(index int) bool { | ||
return index >= 0 && index < len(ev.static)+len(ev.dynamic) | ||
} | ||
|
||
func (ev Event) Label(index int) label.Label { | ||
if index < len(ev.static) { | ||
return ev.static[index] | ||
} | ||
return ev.dynamic[index-len(ev.static)] | ||
} | ||
|
||
func (ev Event) Find(key label.Key) label.Label { | ||
for _, l := range ev.static { | ||
if l.Key() == key { | ||
return l | ||
} | ||
} | ||
for _, l := range ev.dynamic { | ||
if l.Key() == key { | ||
return l | ||
} | ||
} | ||
return label.Label{} | ||
} | ||
|
||
func MakeEvent(static [3]label.Label, labels []label.Label) Event { | ||
return Event{ | ||
static: static, | ||
dynamic: labels, | ||
} | ||
} | ||
|
||
// CloneEvent event returns a copy of the event with the time adjusted to at. | ||
func CloneEvent(ev Event, at time.Time) Event { | ||
ev.at = at | ||
return ev | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// 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 core | ||
|
||
import ( | ||
"context" | ||
"sync/atomic" | ||
"time" | ||
"unsafe" | ||
|
||
"github.com/charlievieth/imports/event/label" | ||
) | ||
|
||
// Exporter is a function that handles events. | ||
// It may return a modified context and event. | ||
type Exporter func(context.Context, Event, label.Map) context.Context | ||
|
||
var ( | ||
exporter unsafe.Pointer | ||
) | ||
|
||
// SetExporter sets the global exporter function that handles all events. | ||
// The exporter is called synchronously from the event call site, so it should | ||
// return quickly so as not to hold up user code. | ||
func SetExporter(e Exporter) { | ||
p := unsafe.Pointer(&e) | ||
if e == nil { | ||
// &e is always valid, and so p is always valid, but for the early abort | ||
// of ProcessEvent to be efficient it needs to make the nil check on the | ||
// pointer without having to dereference it, so we make the nil function | ||
// also a nil pointer | ||
p = nil | ||
} | ||
atomic.StorePointer(&exporter, p) | ||
} | ||
|
||
// deliver is called to deliver an event to the supplied exporter. | ||
// it will fill in the time. | ||
func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context { | ||
// add the current time to the event | ||
ev.at = time.Now() | ||
// hand the event off to the current exporter | ||
return exporter(ctx, ev, ev) | ||
} | ||
|
||
// Export is called to deliver an event to the global exporter if set. | ||
func Export(ctx context.Context, ev Event) context.Context { | ||
// get the global exporter and abort early if there is not one | ||
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) | ||
if exporterPtr == nil { | ||
return ctx | ||
} | ||
return deliver(ctx, *exporterPtr, ev) | ||
} | ||
|
||
// ExportPair is called to deliver a start event to the supplied exporter. | ||
// It also returns a function that will deliver the end event to the same | ||
// exporter. | ||
// It will fill in the time. | ||
func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) { | ||
// get the global exporter and abort early if there is not one | ||
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) | ||
if exporterPtr == nil { | ||
return ctx, func() {} | ||
} | ||
ctx = deliver(ctx, *exporterPtr, begin) | ||
return ctx, func() { deliver(ctx, *exporterPtr, end) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// 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 core | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/charlievieth/imports/event/keys" | ||
"github.com/charlievieth/imports/event/label" | ||
) | ||
|
||
// Log1 takes a message and one label delivers a log event to the exporter. | ||
// It is a customized version of Print that is faster and does no allocation. | ||
func Log1(ctx context.Context, message string, t1 label.Label) { | ||
Export(ctx, MakeEvent([3]label.Label{ | ||
keys.Msg.Of(message), | ||
t1, | ||
}, nil)) | ||
} | ||
|
||
// Log2 takes a message and two labels and delivers a log event to the exporter. | ||
// It is a customized version of Print that is faster and does no allocation. | ||
func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) { | ||
Export(ctx, MakeEvent([3]label.Label{ | ||
keys.Msg.Of(message), | ||
t1, | ||
t2, | ||
}, nil)) | ||
} | ||
|
||
// Metric1 sends a label event to the exporter with the supplied labels. | ||
func Metric1(ctx context.Context, t1 label.Label) context.Context { | ||
return Export(ctx, MakeEvent([3]label.Label{ | ||
keys.Metric.New(), | ||
t1, | ||
}, nil)) | ||
} | ||
|
||
// Metric2 sends a label event to the exporter with the supplied labels. | ||
func Metric2(ctx context.Context, t1, t2 label.Label) context.Context { | ||
return Export(ctx, MakeEvent([3]label.Label{ | ||
keys.Metric.New(), | ||
t1, | ||
t2, | ||
}, nil)) | ||
} | ||
|
||
// Start1 sends a span start event with the supplied label list to the exporter. | ||
// It also returns a function that will end the span, which should normally be | ||
// deferred. | ||
func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) { | ||
return ExportPair(ctx, | ||
MakeEvent([3]label.Label{ | ||
keys.Start.Of(name), | ||
t1, | ||
}, nil), | ||
MakeEvent([3]label.Label{ | ||
keys.End.New(), | ||
}, nil)) | ||
} | ||
|
||
// Start2 sends a span start event with the supplied label list to the exporter. | ||
// It also returns a function that will end the span, which should normally be | ||
// deferred. | ||
func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) { | ||
return ExportPair(ctx, | ||
MakeEvent([3]label.Label{ | ||
keys.Start.Of(name), | ||
t1, | ||
t2, | ||
}, nil), | ||
MakeEvent([3]label.Label{ | ||
keys.End.New(), | ||
}, nil)) | ||
} |
Oops, something went wrong.