blob: 1ccaf92cbb3cc44589be7f79f0fc3624acdbec37 [file] [log] [blame]
Jiri Simsad7616c92015-03-24 23:44:30 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Robin Thellendac59e972014-08-19 18:26:11 -07005// Package stats implements a global repository of stats objects. Each object
6// has a name and a value.
7// Example:
8// bar1 := stats.NewInteger("foo/bar1")
9// bar2 := stats.NewFloat("foo/bar2")
10// bar3 := stats.NewCounter("foo/bar3")
11// bar1.Set(1)
12// bar2.Set(2)
13// bar3.Set(3)
14// The values can be retrieved with:
15// v, err := stats.Value("foo/bar1")
16package stats
17
18import (
Robin Thellendac59e972014-08-19 18:26:11 -070019 "strings"
20 "sync"
21 "time"
Mike Burrows2c989372015-03-31 18:06:39 -070022
Todd Wang94c9d0b2015-04-01 14:27:00 -070023 "v.io/v23/services/stats"
Mike Burrows2c989372015-03-31 18:06:39 -070024 "v.io/v23/verror"
Robin Thellendac59e972014-08-19 18:26:11 -070025)
26
27// StatsObject is the interface for objects stored in the stats repository.
28type StatsObject interface {
29 // LastUpdate is used by WatchGlob to decide which updates to send.
30 LastUpdate() time.Time
31 // Value returns the current value of the object.
32 Value() interface{}
33}
34
35type node struct {
36 object StatsObject
37 children map[string]*node
38}
39
40var (
Mike Burrows2c989372015-03-31 18:06:39 -070041 lock sync.RWMutex
42 repository *node // GUARDED_BY(lock)
Robin Thellendac59e972014-08-19 18:26:11 -070043)
44
45func init() {
46 repository = newNode()
47}
48
49// GetStatsObject returns the object with that given name, or an error if the
50// object doesn't exist.
51func GetStatsObject(name string) (StatsObject, error) {
52 lock.RLock()
53 defer lock.RUnlock()
54 node := findNodeLocked(name, false)
55 if node == nil || node.object == nil {
Mike Burrows2c989372015-03-31 18:06:39 -070056 return nil, verror.New(verror.ErrNoExist, nil, name)
Robin Thellendac59e972014-08-19 18:26:11 -070057 }
58 return node.object, nil
59}
60
61// Value returns the value of an object, or an error if the object doesn't
62// exist.
63func Value(name string) (interface{}, error) {
64 obj, err := GetStatsObject(name)
65 if err != nil {
66 return 0, err
67 }
68 if obj == nil {
Todd Wang94c9d0b2015-04-01 14:27:00 -070069 return nil, verror.New(stats.ErrNoValue, nil, name)
Robin Thellendac59e972014-08-19 18:26:11 -070070 }
71 return obj.Value(), nil
72}
73
Robin Thellenddf428232014-10-06 12:50:44 -070074// Delete deletes a StatsObject and all its children, if any.
75func Delete(name string) error {
76 if name == "" {
Mike Burrows2c989372015-03-31 18:06:39 -070077 return verror.New(verror.ErrNoExist, nil, name)
Robin Thellenddf428232014-10-06 12:50:44 -070078 }
79 elems := strings.Split(name, "/")
80 last := len(elems) - 1
81 dirname, basename := strings.Join(elems[:last], "/"), elems[last]
82 lock.Lock()
83 defer lock.Unlock()
84 parent := findNodeLocked(dirname, false)
85 if parent == nil {
Mike Burrows2c989372015-03-31 18:06:39 -070086 return verror.New(verror.ErrNoExist, nil, name)
Robin Thellenddf428232014-10-06 12:50:44 -070087 }
88 delete(parent.children, basename)
89 return nil
90}
91
Robin Thellendac59e972014-08-19 18:26:11 -070092func newNode() *node {
93 return &node{children: make(map[string]*node)}
94}
95
96// findNodeLocked finds a node, and optionally creates it if it doesn't already
97// exist.
98func findNodeLocked(name string, create bool) *node {
99 elems := strings.Split(name, "/")
100 node := repository
101 for {
102 if len(elems) == 0 {
103 return node
104 }
105 if len(elems[0]) == 0 {
106 elems = elems[1:]
107 continue
108 }
109 if next, ok := node.children[elems[0]]; ok {
110 node = next
111 elems = elems[1:]
112 continue
113 }
114 if create {
115 node.children[elems[0]] = newNode()
116 node = node.children[elems[0]]
117 elems = elems[1:]
118 continue
119 }
120 return nil
121 }
122}