Log slow API requests with server spans and client timing
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type span struct {
|
||||
name string
|
||||
dur time.Duration
|
||||
}
|
||||
|
||||
type Trace struct {
|
||||
mu sync.Mutex
|
||||
spans []span
|
||||
}
|
||||
|
||||
type ctxKey struct{}
|
||||
|
||||
func New(ctx context.Context) (context.Context, *Trace) {
|
||||
t := &Trace{}
|
||||
return context.WithValue(ctx, ctxKey{}, t), t
|
||||
}
|
||||
|
||||
func Start(ctx context.Context, name string) func() {
|
||||
start := time.Now()
|
||||
return func() {
|
||||
Add(ctx, name, time.Since(start))
|
||||
}
|
||||
}
|
||||
|
||||
func Add(ctx context.Context, name string, dur time.Duration) {
|
||||
t, _ := ctx.Value(ctxKey{}).(*Trace)
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
t.mu.Lock()
|
||||
t.spans = append(t.spans, span{name: name, dur: dur})
|
||||
t.mu.Unlock()
|
||||
}
|
||||
|
||||
func (t *Trace) Header(total time.Duration) string {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
parts := []string{fmt.Sprintf("total;dur=%.1f", ms(total))}
|
||||
for _, s := range t.spans {
|
||||
parts = append(parts, fmt.Sprintf("%s;dur=%.1f", token(s.name), ms(s.dur)))
|
||||
}
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
|
||||
func (t *Trace) String() string {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
parts := []string{}
|
||||
for _, s := range t.spans {
|
||||
parts = append(parts, fmt.Sprintf("%s %s", s.name, s.dur.Round(time.Millisecond)))
|
||||
}
|
||||
return strings.Join(parts, ", ")
|
||||
}
|
||||
|
||||
func ms(d time.Duration) float64 {
|
||||
return float64(d.Microseconds()) / 1000
|
||||
}
|
||||
|
||||
func token(name string) string {
|
||||
return strings.ReplaceAll(name, " ", "-")
|
||||
}
|
||||
Reference in New Issue
Block a user