-
Notifications
You must be signed in to change notification settings - Fork 28
/
stats.go
127 lines (115 loc) · 2.92 KB
/
stats.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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2015 The Vanadium 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 stats implements a global repository of stats objects. Each object
// has a name and a value.
// Example:
//
// bar1 := stats.NewInteger("foo/bar1")
// bar2 := stats.NewFloat("foo/bar2")
// bar3 := stats.NewCounter("foo/bar3")
// bar1.Set(1)
// bar2.Set(2)
// bar3.Set(3)
//
// The values can be retrieved with:
//
// v, err := stats.Value("foo/bar1")
package stats
import (
"strings"
"sync"
"time"
"v.io/v23/services/stats"
"v.io/v23/verror"
)
// StatsObject is the interface for objects stored in the stats repository.
//
//nolint:revive // API change required.
type StatsObject interface {
// LastUpdate is used by WatchGlob to decide which updates to send.
LastUpdate() time.Time
// Value returns the current value of the object.
Value() interface{}
}
type node struct {
object StatsObject
children map[string]*node
}
var (
lock sync.RWMutex
repository *node // GUARDED_BY(lock)
)
func init() {
repository = newNode()
}
// GetStatsObject returns the object with that given name, or an error if the
// object doesn't exist.
func GetStatsObject(name string) (StatsObject, error) {
lock.RLock()
defer lock.RUnlock()
node := findNodeLocked(name, false)
if node == nil || node.object == nil {
return nil, verror.ErrNoExist.Errorf(nil, "does not exist: %s", name)
}
return node.object, nil
}
// Value returns the value of an object, or an error if the object doesn't
// exist.
func Value(name string) (interface{}, error) {
obj, err := GetStatsObject(name)
if err != nil {
return 0, err
}
if obj == nil {
return nil, stats.ErrorfNoValue(nil, "object has no value, suffix: %v", name)
}
return obj.Value(), nil
}
// Delete deletes a StatsObject and all its children, if any.
func Delete(name string) error {
if name == "" {
return verror.ErrNoExist.Errorf(nil, "does not exist: %s", name)
}
elems := strings.Split(name, "/")
last := len(elems) - 1
dirname, basename := strings.Join(elems[:last], "/"), elems[last]
lock.Lock()
defer lock.Unlock()
parent := findNodeLocked(dirname, false)
if parent == nil {
return verror.ErrNoExist.Errorf(nil, "does not exist: %s", name)
}
delete(parent.children, basename)
return nil
}
func newNode() *node {
return &node{children: make(map[string]*node)}
}
// findNodeLocked finds a node, and optionally creates it if it doesn't already
// exist.
func findNodeLocked(name string, create bool) *node {
elems := strings.Split(name, "/")
node := repository
for {
if len(elems) == 0 {
return node
}
if len(elems[0]) == 0 {
elems = elems[1:]
continue
}
if next, ok := node.children[elems[0]]; ok {
node = next
elems = elems[1:]
continue
}
if create {
node.children[elems[0]] = newNode()
node = node.children[elems[0]]
elems = elems[1:]
continue
}
return nil
}
}