72 lines
1.3 KiB
Go
72 lines
1.3 KiB
Go
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, " ", "-")
|
||
|
|
}
|