13937: Export stats as prometheus metrics. (WIP)
[arvados.git] / services / keepstore / stats_ticker.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "fmt"
9         "sync"
10         "sync/atomic"
11
12         "github.com/prometheus/client_golang/prometheus"
13 )
14
15 type statsTicker struct {
16         Errors   uint64
17         InBytes  uint64
18         OutBytes uint64
19
20         ErrorCodes map[string]uint64 `json:",omitempty"`
21         lock       sync.Mutex
22 }
23
24 func (s *statsTicker) setupPrometheus(drv string, reg *prometheus.Registry, lbl prometheus.Labels) {
25         metrics := map[string][]interface{}{
26                 "errors":    []interface{}{string("errors"), s.Errors},
27                 "in_bytes":  []interface{}{string("input bytes"), s.InBytes},
28                 "out_bytes": []interface{}{string("output bytes"), s.OutBytes},
29         }
30         for mName, data := range metrics {
31                 mHelp := data[0].(string)
32                 mVal := data[1].(uint64)
33                 reg.Register(prometheus.NewGaugeFunc(
34                         prometheus.GaugeOpts{
35                                 Namespace:   "arvados",
36                                 Subsystem:   "keepstore",
37                                 Name:        fmt.Sprintf("%s_%s", drv, mName),
38                                 Help:        fmt.Sprintf("Number of %s backend %s", drv, mHelp),
39                                 ConstLabels: lbl,
40                         },
41                         func() float64 { return float64(mVal) },
42                 ))
43         }
44 }
45
46 // Tick increments each of the given counters by 1 using
47 // atomic.AddUint64.
48 func (s *statsTicker) Tick(counters ...*uint64) {
49         for _, counter := range counters {
50                 atomic.AddUint64(counter, 1)
51         }
52 }
53
54 // TickErr increments the overall error counter, as well as the
55 // ErrorCodes entry for the given errType. If err is nil, TickErr is a
56 // no-op.
57 func (s *statsTicker) TickErr(err error, errType string) {
58         if err == nil {
59                 return
60         }
61         s.Tick(&s.Errors)
62
63         s.lock.Lock()
64         if s.ErrorCodes == nil {
65                 s.ErrorCodes = make(map[string]uint64)
66         }
67         s.ErrorCodes[errType]++
68         s.lock.Unlock()
69 }
70
71 // TickInBytes increments the incoming byte counter by n.
72 func (s *statsTicker) TickInBytes(n uint64) {
73         atomic.AddUint64(&s.InBytes, n)
74 }
75
76 // TickOutBytes increments the outgoing byte counter by n.
77 func (s *statsTicker) TickOutBytes(n uint64) {
78         atomic.AddUint64(&s.OutBytes, n)
79 }