-
Notifications
You must be signed in to change notification settings - Fork 462
/
call.go
74 lines (66 loc) · 2.07 KB
/
call.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package telemetry
import (
"sync"
"time"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// CallCounter is used to track timing and other information about a "call". It
// is intended to be scoped to a function with a defer and a named error value,
// if applicable, like so:
//
// func Foo() (err error) {
// call := StartCall(metrics, "foo")
// defer call.Done(&err)
//
// call.AddLabel("food", "burgers")
// }
//
// See `Done` doc for labels automatically added.
//
// Instances of this struct should only be created directly by this package
// and its subpackages, which define the specific metrics that are emitted.
// It is left exported for testing purposes.
type CallCounter struct {
metrics Metrics
key []string
labels []Label
start time.Time
done bool
mu sync.Mutex
}
// StartCall starts a "call", which when finished via Done() will emit timing
// and error related metrics.
func StartCall(metrics Metrics, key string, keyn ...string) *CallCounter {
return &CallCounter{
metrics: metrics,
key: append([]string{key}, keyn...),
start: time.Now(),
}
}
// AddLabel adds a label to be emitted with the call counter. It is safe to call
// from multiple goroutines.
func (c *CallCounter) AddLabel(name, value string) {
c.mu.Lock()
c.labels = append(c.labels, Label{Name: name, Value: value})
c.mu.Unlock()
}
// Done finishes the "call" and emits metrics. No other calls to the CallCounter
// should be done during or after the call to Done. In other words, it is not
// thread-safe and is intended to be the final call to the CallCounter struct.
// Emits latency and counter metrics, including adding a Status label according
// to gRPC code of the given error. If nil error, the code is OK (success).
func (c *CallCounter) Done(errp *error) {
if c.done {
return
}
c.done = true
key := c.key
code := codes.OK
if errp != nil {
code = status.Code(*errp)
}
c.AddLabel(Status, code.String())
c.metrics.IncrCounterWithLabels(key, 1, c.labels)
c.metrics.MeasureSinceWithLabels(append(key, ElapsedTime), c.start, c.labels)
}