From c949e3422ba56fe57c8db0d05c2cf13756648984 Mon Sep 17 00:00:00 2001 From: Kamil Samigullin Date: Sun, 23 Jun 2019 15:37:34 +0300 Subject: [PATCH 1/2] change checkpoint storage --- README.md | 6 +++--- example_test.go | 4 ++-- helper_test.go | 12 ++++++------ tracer.go | 22 ++++++++-------------- tracer_test.go | 11 +++-------- 5 files changed, 22 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 58eb859..b7d1c08 100644 --- a/README.md +++ b/README.md @@ -49,15 +49,15 @@ func Handle(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), time.Second) defer cancel() - call := tracer.Fetch(req.Context()).Start().Mark(req.Header.Get("X-Request-Id")) + call := tracer.Fetch(req.Context()).Start(req.Header.Get("X-Request-Id")) defer call.Stop() ... - call.Checkpoint().Mark("serialize") + call.Checkpoint("serialize") data := FetchData(ctx, req.Body) - call.Checkpoint().Mark("store") + call.Checkpoint("store") if err := StoreIntoDatabase(ctx, data); err != nil { http.Error(rw, http.StatusText(http.StatusInternalServerError), diff --git a/example_test.go b/example_test.go index 111eef7..f52f311 100644 --- a/example_test.go +++ b/example_test.go @@ -69,7 +69,7 @@ func Handle(rw http.ResponseWriter, req *http.Request) { time.Sleep(time.Millisecond) - call.Checkpoint().Mark("serialize") + call.Checkpoint("serialize") data, err := FetchData(ctx, req.Body) if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) @@ -78,7 +78,7 @@ func Handle(rw http.ResponseWriter, req *http.Request) { time.Sleep(time.Millisecond) - call.Checkpoint().Mark("store") + call.Checkpoint("store") if err := StoreIntoDatabase(ctx, data); err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return diff --git a/helper_test.go b/helper_test.go index 099ba08..e7492e5 100644 --- a/helper_test.go +++ b/helper_test.go @@ -25,10 +25,10 @@ func traceRoot(ctx context.Context) { call := Fetch(ctx).Start().Mark("root") defer call.Stop() - call.Checkpoint().Mark("checkpointA") + call.Checkpoint("checkpointA") traceA(ctx) - call.Checkpoint().Mark("checkpointB") + call.Checkpoint("checkpointB") traceB(ctx) } @@ -36,10 +36,10 @@ func traceA(ctx context.Context) { call := Fetch(ctx).Start().Mark("A") defer call.Stop() - call.Checkpoint().Mark("checkpointA1") + call.Checkpoint("checkpointA1") traceA1(ctx) - call.Checkpoint().Mark("checkpointA2") + call.Checkpoint("checkpointA2") traceA2(ctx) } @@ -58,10 +58,10 @@ func traceB(ctx context.Context) { call := Fetch(ctx).Start().Mark("B") defer call.Stop() - call.Checkpoint().Mark("checkpointB1") + call.Checkpoint("checkpointB1") traceB1(ctx) - call.Checkpoint().Mark("checkpointB2") + call.Checkpoint("checkpointB2") func(ctx context.Context) { defer Fetch(ctx).Start().Stop() }(ctx) diff --git a/tracer.go b/tracer.go index d849306..27985fe 100644 --- a/tracer.go +++ b/tracer.go @@ -71,22 +71,24 @@ type Call struct { caller CallerInfo start, stop time.Time id string - checkpoints []*Checkpoint + checkpoints []Checkpoint allocates int } -func (call *Call) Checkpoint() *Checkpoint { +func (call *Call) Checkpoint(labels ...string) { if call == nil { - return nil + return } - checkpoint := &Checkpoint{timestamp: time.Now()} + var id string + if len(labels) > 0 { + id = labels[0] + } + checkpoint := Checkpoint{id: id, timestamp: time.Now()} if len(call.checkpoints) == cap(call.checkpoints) { call.allocates++ } call.checkpoints = append(call.checkpoints, checkpoint) - - return checkpoint } func (call *Call) Mark(id string) *Call { @@ -110,11 +112,3 @@ type Checkpoint struct { id string timestamp time.Time } - -func (checkpoint *Checkpoint) Mark(id string) { - if checkpoint == nil { - return - } - - checkpoint.id = id -} diff --git a/tracer_test.go b/tracer_test.go index 8a09a6f..831dc1c 100644 --- a/tracer_test.go +++ b/tracer_test.go @@ -27,7 +27,7 @@ func TestTrace_String(t *testing.T) { } call := trace.Start().Mark("fn call") - call.Checkpoint().Mark("checkpoint") + call.Checkpoint("checkpoint") call.Stop() if expected, obtained := "allocates at call stack: 1", trace.String(); !strings.Contains(obtained, expected) { t.Errorf("\n expected: %+#v \n obtained: %+#v", expected, obtained) @@ -36,8 +36,8 @@ func TestTrace_String(t *testing.T) { } func TestCall_Checkpoint(t *testing.T) { - (*Call)(nil).Checkpoint().Mark("no panic") - (&Call{}).Checkpoint().Mark("one allocation") + (*Call)(nil).Checkpoint("no panic") + (&Call{}).Checkpoint("one allocation") } func TestCall_Mark(t *testing.T) { @@ -50,11 +50,6 @@ func TestCall_Stop(t *testing.T) { (&Call{}).Mark("success").Stop() } -func TestCheckpoint_Mark(t *testing.T) { - (*Checkpoint)(nil).Mark("no panic") - (&Checkpoint{}).Mark("by id") -} - // BenchmarkTracing/silent-12 200000 7755 ns/op 1816 B/op 24 allocs/op // BenchmarkTracing/full-12 200000 8880 ns/op 3944 B/op 45 allocs/op func BenchmarkTracing(b *testing.B) { From bc244f73e6591ada986bd6786da8c3192fbcc18a Mon Sep 17 00:00:00 2001 From: Kamil Samigullin Date: Sun, 23 Jun 2019 18:35:38 +0300 Subject: [PATCH 2/2] replace Call pointer to ptr type --- context.go | 2 +- context_test.go | 4 +-- example_test.go | 4 +-- helper_test.go | 6 ++--- tracer.go | 68 +++++++++++++++++++++++++++---------------------- tracer_test.go | 29 +++++---------------- 6 files changed, 52 insertions(+), 61 deletions(-) diff --git a/context.go b/context.go index 9b56d28..b1808ce 100644 --- a/context.go +++ b/context.go @@ -9,6 +9,6 @@ func Fetch(ctx context.Context) *Trace { return trace } -func Inject(ctx context.Context, stack []*Call) context.Context { +func Inject(ctx context.Context, stack []Call) context.Context { return context.WithValue(ctx, key{}, &Trace{stack: stack}) } diff --git a/context_test.go b/context_test.go index d867d7b..0cc7c83 100644 --- a/context_test.go +++ b/context_test.go @@ -12,14 +12,14 @@ func TestContext(t *testing.T) { if Fetch(context.Background()) != nil { t.Error("expected nil") } - Fetch(context.Background()).Start().Mark("no panic").Stop() + Fetch(context.Background()).Start("no panic").Stop() }) t.Run("fetch injected", func(t *testing.T) { ctx := Inject(context.Background(), nil) if Fetch(ctx) == nil { t.Error("unexpected nil") } - Fetch(ctx).Start().Mark("allocation").Stop() + Fetch(ctx).Start("allocation").Stop() }) } diff --git a/example_test.go b/example_test.go index f52f311..83d12ae 100644 --- a/example_test.go +++ b/example_test.go @@ -48,7 +48,7 @@ func Example() { func InjectTracer(handler http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - req = req.WithContext(tracer.Inject(req.Context(), make([]*tracer.Call, 0, 2))) + req = req.WithContext(tracer.Inject(req.Context(), make([]tracer.Call, 0, 2))) handler.ServeHTTP(rw, req) }) } @@ -64,7 +64,7 @@ func Handle(rw http.ResponseWriter, req *http.Request) { ctx, cancel := context.WithTimeout(req.Context(), time.Second) defer cancel() - call := tracer.Fetch(req.Context()).Start().Mark(req.Header.Get("X-Request-Id")) + call := tracer.Fetch(req.Context()).Start(req.Header.Get("X-Request-Id")) defer call.Stop() time.Sleep(time.Millisecond) diff --git a/helper_test.go b/helper_test.go index e7492e5..16caab9 100644 --- a/helper_test.go +++ b/helper_test.go @@ -22,7 +22,7 @@ func callerC() CallerInfo { } func traceRoot(ctx context.Context) { - call := Fetch(ctx).Start().Mark("root") + call := Fetch(ctx).Start("root") defer call.Stop() call.Checkpoint("checkpointA") @@ -33,7 +33,7 @@ func traceRoot(ctx context.Context) { } func traceA(ctx context.Context) { - call := Fetch(ctx).Start().Mark("A") + call := Fetch(ctx).Start("A") defer call.Stop() call.Checkpoint("checkpointA1") @@ -55,7 +55,7 @@ func traceA2(ctx context.Context) { } func traceB(ctx context.Context) { - call := Fetch(ctx).Start().Mark("B") + call := Fetch(ctx).Start("B") defer call.Stop() call.Checkpoint("checkpointB1") diff --git a/tracer.go b/tracer.go index 27985fe..7c7551a 100644 --- a/tracer.go +++ b/tracer.go @@ -8,21 +8,29 @@ import ( ) type Trace struct { - stack []*Call + stack []Call allocates int } -func (trace *Trace) Start() *Call { +func (trace *Trace) Start(labels ...string) ptr { if trace == nil { - return nil + return ptr{} } + var ( + id string + tags []string + ) + if len(labels) > 0 { + id = labels[0] + tags = labels[1:] + } + call := Call{caller: Caller(3), start: time.Now(), id: id, tags: tags} if len(trace.stack) == cap(trace.stack) { trace.allocates++ } - call := &Call{caller: Caller(3), start: time.Now()} trace.stack = append(trace.stack, call) - return call + return ptr{trace, len(trace.stack) - 1} } func (trace *Trace) String() string { @@ -71,44 +79,42 @@ type Call struct { caller CallerInfo start, stop time.Time id string + tags []string checkpoints []Checkpoint allocates int } -func (call *Call) Checkpoint(labels ...string) { - if call == nil { - return - } +type Checkpoint struct { + id string + tags []string + timestamp time.Time +} + +type ptr struct { + *Trace + int +} - var id string +func (call ptr) Checkpoint(labels ...string) { + var ( + id string + tags []string + ) if len(labels) > 0 { id = labels[0] + tags = labels[1:] } - checkpoint := Checkpoint{id: id, timestamp: time.Now()} - if len(call.checkpoints) == cap(call.checkpoints) { - call.allocates++ + checkpoint := Checkpoint{id: id, tags: tags, timestamp: time.Now()} + if len(call.stack[call.int].checkpoints) == cap(call.stack[call.int].checkpoints) { + call.stack[call.int].allocates++ } - call.checkpoints = append(call.checkpoints, checkpoint) -} - -func (call *Call) Mark(id string) *Call { - if call == nil { - return nil - } - - call.id = id - return call + call.stack[call.int].checkpoints = append(call.stack[call.int].checkpoints, checkpoint) } -func (call *Call) Stop() { - if call == nil { +func (call ptr) Stop() { + if call.Trace == nil { return } - call.stop = time.Now() -} - -type Checkpoint struct { - id string - timestamp time.Time + call.stack[call.int].stop = time.Now() } diff --git a/tracer_test.go b/tracer_test.go index 831dc1c..525df04 100644 --- a/tracer_test.go +++ b/tracer_test.go @@ -9,8 +9,8 @@ import ( ) func TestTrace_Start(t *testing.T) { - (*Trace)(nil).Start().Mark("no panic") - (&Trace{}).Start().Mark("one allocation") + (*Trace)(nil).Start("no panic") + (&Trace{}).Start("one allocation") } func TestTrace_String(t *testing.T) { @@ -26,7 +26,7 @@ func TestTrace_String(t *testing.T) { t.Errorf("\n expected: %+#v \n obtained: %+#v", expected, obtained) } - call := trace.Start().Mark("fn call") + call := trace.Start("fn call") call.Checkpoint("checkpoint") call.Stop() if expected, obtained := "allocates at call stack: 1", trace.String(); !strings.Contains(obtained, expected) { @@ -35,34 +35,19 @@ func TestTrace_String(t *testing.T) { }) } -func TestCall_Checkpoint(t *testing.T) { - (*Call)(nil).Checkpoint("no panic") - (&Call{}).Checkpoint("one allocation") -} - -func TestCall_Mark(t *testing.T) { - (*Call)(nil).Mark("no panic") - (&Call{}).Mark("by id") -} - -func TestCall_Stop(t *testing.T) { - (*Call)(nil).Mark("no panic").Stop() - (&Call{}).Mark("success").Stop() -} - -// BenchmarkTracing/silent-12 200000 7755 ns/op 1816 B/op 24 allocs/op -// BenchmarkTracing/full-12 200000 8880 ns/op 3944 B/op 45 allocs/op +// BenchmarkTracing/silent-12 200000 7298 ns/op 2336 B/op 18 allocs/op +// BenchmarkTracing/full-12 200000 8918 ns/op 4464 B/op 39 allocs/op func BenchmarkTracing(b *testing.B) { b.Run("silent", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - traceRoot(Inject(context.Background(), make([]*Call, 0, 9))) + traceRoot(Inject(context.Background(), make([]Call, 0, 9))) } }) b.Run("full", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - ctx := Inject(context.Background(), make([]*Call, 0, 9)) + ctx := Inject(context.Background(), make([]Call, 0, 9)) traceRoot(ctx) _ = Fetch(ctx).String() }