master
go 185 lines 4.76 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package metrix
4
5 // snapshotStateSetInstrument writes sampled full-state points.
6 type snapshotStateSetInstrument struct {
7 backend meterBackend
8 desc *instrumentDescriptor
9 scope HostScope
10 base []LabelSet
11 }
12
13 // statefulStateSetInstrument writes maintained full-state points.
14 type statefulStateSetInstrument struct {
15 backend meterBackend
16 desc *instrumentDescriptor
17 scope HostScope
18 base []LabelSet
19 }
20
21 // stagedStateSet holds one in-cycle stateset sample for a single series identity.
22 type stagedStateSet struct {
23 key string
24 name string
25 hostScopeKey string
26 hostScope HostScope
27 labels []Label
28 labelsKey string
29 desc *instrumentDescriptor
30 states map[string]bool
31 }
32
33 // StateSet declares or reuses a snapshot stateset under this meter.
34 func (m *snapshotMeter) StateSet(name string, opts ...InstrumentOption) StateSetInstrument {
35 desc, err := m.backend.registerInstrument(metricName(m.prefix, name), kindStateSet, modeSnapshot, opts...)
36 if err != nil {
37 panic(err)
38 }
39 return &snapshotStateSetInstrument{
40 backend: m.backend,
41 desc: desc,
42 scope: m.scope,
43 base: appendLabelSets(m.sets, nil),
44 }
45 }
46
47 // StateSet declares or reuses a stateful stateset under this meter.
48 func (m *statefulMeter) StateSet(name string, opts ...InstrumentOption) StateSetInstrument {
49 desc, err := m.backend.registerInstrument(metricName(m.prefix, name), kindStateSet, modeStateful, opts...)
50 if err != nil {
51 panic(err)
52 }
53 return &statefulStateSetInstrument{
54 backend: m.backend,
55 desc: desc,
56 scope: m.scope,
57 base: appendLabelSets(m.sets, nil),
58 }
59 }
60
61 // ObserveStateSet writes a full-state sample for this collect cycle.
62 func (s *snapshotStateSetInstrument) ObserveStateSet(p StateSetPoint, labels ...LabelSet) {
63 s.backend.recordStateSetObserve(s.desc, s.scope, p, appendLabelSets(s.base, labels))
64 }
65
66 // Enable writes enum/bitset convenience sample with listed active states.
67 func (s *snapshotStateSetInstrument) Enable(actives ...string) {
68 s.ObserveStateSet(stateSetPointFromActives(s.desc, actives...))
69 }
70
71 // ObserveStateSet writes a full-state sample for this collect cycle.
72 func (s *statefulStateSetInstrument) ObserveStateSet(p StateSetPoint, labels ...LabelSet) {
73 s.backend.recordStateSetObserve(s.desc, s.scope, p, appendLabelSets(s.base, labels))
74 }
75
76 // Enable writes enum/bitset convenience sample with listed active states.
77 func (s *statefulStateSetInstrument) Enable(actives ...string) {
78 s.ObserveStateSet(stateSetPointFromActives(s.desc, actives...))
79 }
80
81 func stateSetPointFromActives(desc *instrumentDescriptor, actives ...string) StateSetPoint {
82 schema := desc.stateSet
83 if schema == nil {
84 panic(errStateSetSchema)
85 }
86
87 if schema.mode == ModeEnum {
88 if len(actives) != 1 {
89 panic(errStateSetEnumCount)
90 }
91 }
92
93 states := make(map[string]bool, len(schema.states))
94 for _, st := range schema.states {
95 states[st] = false
96 }
97 for _, active := range actives {
98 if _, ok := schema.index[active]; !ok {
99 panic(errStateSetUnknownState)
100 }
101 states[active] = true
102 }
103 return StateSetPoint{States: states}
104 }
105
106 // recordStateSetObserve writes one full-state stateset sample into the active frame.
107 func (c *storeCore) recordStateSetObserve(desc *instrumentDescriptor, scope HostScope, point StateSetPoint, sets []LabelSet) {
108 c.mu.Lock()
109 defer c.mu.Unlock()
110
111 if c.active == nil {
112 panic(errCycleInactive)
113 }
114
115 schema := desc.stateSet
116 if schema == nil {
117 panic(errStateSetSchema)
118 }
119
120 labels, labelsKey, err := labelsFromSet(sets, c)
121 if err != nil {
122 panic(err)
123 }
124 if labelsContainKey(labels, desc.name) {
125 panic(errStateSetLabelKey)
126 }
127 scope, ok := c.prepareHostScopeForWriteLocked(scope)
128 if !ok {
129 return
130 }
131
132 states := normalizeStateSetPoint(point, schema)
133
134 key := makeSeriesKey(scope.ScopeKey, desc.name, labelsKey)
135 entry, ok := c.active.stateSet[key]
136 if !ok {
137 entry = &stagedStateSet{
138 key: key,
139 name: desc.name,
140 hostScopeKey: scope.ScopeKey,
141 hostScope: scope,
142 labels: labels,
143 labelsKey: labelsKey,
144 desc: desc,
145 }
146 c.active.stateSet[key] = entry
147 }
148 entry.states = states
149 }
150
151 func normalizeStateSetPoint(point StateSetPoint, schema *stateSetSchema) map[string]bool {
152 states := make(map[string]bool, len(schema.states))
153 for _, st := range schema.states {
154 states[st] = false
155 }
156 for st, val := range point.States {
157 if _, ok := schema.index[st]; !ok {
158 panic(errStateSetUnknownState)
159 }
160 states[st] = val
161 }
162
163 if schema.mode == ModeEnum {
164 active := 0
165 for _, st := range schema.states {
166 if states[st] {
167 active++
168 }
169 }
170 if active != 1 {
171 panic(errStateSetEnumCount)
172 }
173 }
174
175 return states
176 }
177
178 func labelsContainKey(labels []Label, key string) bool {
179 for _, lbl := range labels {
180 if lbl.Key == key {
181 return true
182 }
183 }
184 return false
185 }