| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 1 | // Copyright 2013 The Prometheus Authors |
| 2 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 3 | // you may not use this file except in compliance with the License. |
| 4 | // You may obtain a copy of the License at |
| 5 | // |
| 6 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 7 | // |
| 8 | // Unless required by applicable law or agreed to in writing, software |
| 9 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 11 | // See the License for the specific language governing permissions and |
| 12 | // limitations under the License. |
| 13 | |
| 14 | package model |
| 15 | |
| 16 | import ( |
| 17 | "encoding/json" |
| 18 | "fmt" |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 19 | "sort" |
| 20 | "strconv" |
| 21 | "strings" |
| 22 | ) |
| 23 | |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 24 | // ZeroSample is the pseudo zero-value of Sample used to signal a |
| 25 | // non-existing sample. It is a Sample with timestamp Earliest, value 0.0, |
| 26 | // and metric nil. Note that the natural zero value of Sample has a timestamp |
| 27 | // of 0, which is possible to appear in a real Sample and thus not suitable |
| 28 | // to signal a non-existing Sample. |
| 29 | var ZeroSample = Sample{Timestamp: Earliest} |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 30 | |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 31 | // Sample is a sample pair associated with a metric. A single sample must either |
| 32 | // define Value or Histogram but not both. Histogram == nil implies the Value |
| 33 | // field is used, otherwise it should be ignored. |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 34 | type Sample struct { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 35 | Metric Metric `json:"metric"` |
| 36 | Value SampleValue `json:"value"` |
| 37 | Timestamp Time `json:"timestamp"` |
| 38 | Histogram *SampleHistogram `json:"histogram"` |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 39 | } |
| 40 | |
| 41 | // Equal compares first the metrics, then the timestamp, then the value. The |
| 42 | // semantics of value equality is defined by SampleValue.Equal. |
| 43 | func (s *Sample) Equal(o *Sample) bool { |
| 44 | if s == o { |
| 45 | return true |
| 46 | } |
| 47 | |
| 48 | if !s.Metric.Equal(o.Metric) { |
| 49 | return false |
| 50 | } |
| 51 | if !s.Timestamp.Equal(o.Timestamp) { |
| 52 | return false |
| 53 | } |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 54 | if s.Histogram != nil { |
| 55 | return s.Histogram.Equal(o.Histogram) |
| 56 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 57 | return s.Value.Equal(o.Value) |
| 58 | } |
| 59 | |
| 60 | func (s Sample) String() string { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 61 | if s.Histogram != nil { |
| 62 | return fmt.Sprintf("%s => %s", s.Metric, SampleHistogramPair{ |
| 63 | Timestamp: s.Timestamp, |
| 64 | Histogram: s.Histogram, |
| 65 | }) |
| 66 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 67 | return fmt.Sprintf("%s => %s", s.Metric, SamplePair{ |
| 68 | Timestamp: s.Timestamp, |
| 69 | Value: s.Value, |
| 70 | }) |
| 71 | } |
| 72 | |
| 73 | // MarshalJSON implements json.Marshaler. |
| 74 | func (s Sample) MarshalJSON() ([]byte, error) { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 75 | if s.Histogram != nil { |
| 76 | v := struct { |
| 77 | Metric Metric `json:"metric"` |
| 78 | Histogram SampleHistogramPair `json:"histogram"` |
| 79 | }{ |
| 80 | Metric: s.Metric, |
| 81 | Histogram: SampleHistogramPair{ |
| 82 | Timestamp: s.Timestamp, |
| 83 | Histogram: s.Histogram, |
| 84 | }, |
| 85 | } |
| 86 | return json.Marshal(&v) |
| 87 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 88 | v := struct { |
| 89 | Metric Metric `json:"metric"` |
| 90 | Value SamplePair `json:"value"` |
| 91 | }{ |
| 92 | Metric: s.Metric, |
| 93 | Value: SamplePair{ |
| 94 | Timestamp: s.Timestamp, |
| 95 | Value: s.Value, |
| 96 | }, |
| 97 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 98 | return json.Marshal(&v) |
| 99 | } |
| 100 | |
| 101 | // UnmarshalJSON implements json.Unmarshaler. |
| 102 | func (s *Sample) UnmarshalJSON(b []byte) error { |
| 103 | v := struct { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 104 | Metric Metric `json:"metric"` |
| 105 | Value SamplePair `json:"value"` |
| 106 | Histogram SampleHistogramPair `json:"histogram"` |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 107 | }{ |
| 108 | Metric: s.Metric, |
| 109 | Value: SamplePair{ |
| 110 | Timestamp: s.Timestamp, |
| 111 | Value: s.Value, |
| 112 | }, |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 113 | Histogram: SampleHistogramPair{ |
| 114 | Timestamp: s.Timestamp, |
| 115 | Histogram: s.Histogram, |
| 116 | }, |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 117 | } |
| 118 | |
| 119 | if err := json.Unmarshal(b, &v); err != nil { |
| 120 | return err |
| 121 | } |
| 122 | |
| 123 | s.Metric = v.Metric |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 124 | if v.Histogram.Histogram != nil { |
| 125 | s.Timestamp = v.Histogram.Timestamp |
| 126 | s.Histogram = v.Histogram.Histogram |
| 127 | } else { |
| 128 | s.Timestamp = v.Value.Timestamp |
| 129 | s.Value = v.Value.Value |
| 130 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 131 | |
| 132 | return nil |
| 133 | } |
| 134 | |
| 135 | // Samples is a sortable Sample slice. It implements sort.Interface. |
| 136 | type Samples []*Sample |
| 137 | |
| 138 | func (s Samples) Len() int { |
| 139 | return len(s) |
| 140 | } |
| 141 | |
| 142 | // Less compares first the metrics, then the timestamp. |
| 143 | func (s Samples) Less(i, j int) bool { |
| 144 | switch { |
| 145 | case s[i].Metric.Before(s[j].Metric): |
| 146 | return true |
| 147 | case s[j].Metric.Before(s[i].Metric): |
| 148 | return false |
| 149 | case s[i].Timestamp.Before(s[j].Timestamp): |
| 150 | return true |
| 151 | default: |
| 152 | return false |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | func (s Samples) Swap(i, j int) { |
| 157 | s[i], s[j] = s[j], s[i] |
| 158 | } |
| 159 | |
| 160 | // Equal compares two sets of samples and returns true if they are equal. |
| 161 | func (s Samples) Equal(o Samples) bool { |
| 162 | if len(s) != len(o) { |
| 163 | return false |
| 164 | } |
| 165 | |
| 166 | for i, sample := range s { |
| 167 | if !sample.Equal(o[i]) { |
| 168 | return false |
| 169 | } |
| 170 | } |
| 171 | return true |
| 172 | } |
| 173 | |
| 174 | // SampleStream is a stream of Values belonging to an attached COWMetric. |
| 175 | type SampleStream struct { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 176 | Metric Metric `json:"metric"` |
| 177 | Values []SamplePair `json:"values"` |
| 178 | Histograms []SampleHistogramPair `json:"histograms"` |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | func (ss SampleStream) String() string { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 182 | valuesLength := len(ss.Values) |
| 183 | vals := make([]string, valuesLength+len(ss.Histograms)) |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 184 | for i, v := range ss.Values { |
| 185 | vals[i] = v.String() |
| 186 | } |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 187 | for i, v := range ss.Histograms { |
| 188 | vals[i+valuesLength] = v.String() |
| 189 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 190 | return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n")) |
| 191 | } |
| 192 | |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 193 | func (ss SampleStream) MarshalJSON() ([]byte, error) { |
| 194 | switch { |
| 195 | case len(ss.Histograms) > 0 && len(ss.Values) > 0: |
| 196 | v := struct { |
| 197 | Metric Metric `json:"metric"` |
| 198 | Values []SamplePair `json:"values"` |
| 199 | Histograms []SampleHistogramPair `json:"histograms"` |
| 200 | }{ |
| 201 | Metric: ss.Metric, |
| 202 | Values: ss.Values, |
| 203 | Histograms: ss.Histograms, |
| 204 | } |
| 205 | return json.Marshal(&v) |
| 206 | case len(ss.Histograms) > 0: |
| 207 | v := struct { |
| 208 | Metric Metric `json:"metric"` |
| 209 | Histograms []SampleHistogramPair `json:"histograms"` |
| 210 | }{ |
| 211 | Metric: ss.Metric, |
| 212 | Histograms: ss.Histograms, |
| 213 | } |
| 214 | return json.Marshal(&v) |
| 215 | default: |
| 216 | v := struct { |
| 217 | Metric Metric `json:"metric"` |
| 218 | Values []SamplePair `json:"values"` |
| 219 | }{ |
| 220 | Metric: ss.Metric, |
| 221 | Values: ss.Values, |
| 222 | } |
| 223 | return json.Marshal(&v) |
| 224 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 225 | } |
| 226 | |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 227 | func (ss *SampleStream) UnmarshalJSON(b []byte) error { |
| 228 | v := struct { |
| 229 | Metric Metric `json:"metric"` |
| 230 | Values []SamplePair `json:"values"` |
| 231 | Histograms []SampleHistogramPair `json:"histograms"` |
| 232 | }{ |
| 233 | Metric: ss.Metric, |
| 234 | Values: ss.Values, |
| 235 | Histograms: ss.Histograms, |
| 236 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 237 | |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 238 | if err := json.Unmarshal(b, &v); err != nil { |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 239 | return err |
| 240 | } |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 241 | |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 242 | ss.Metric = v.Metric |
| 243 | ss.Values = v.Values |
| 244 | ss.Histograms = v.Histograms |
| 245 | |
| 246 | return nil |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 247 | } |
| 248 | |
| 249 | // Scalar is a scalar value evaluated at the set timestamp. |
| 250 | type Scalar struct { |
| 251 | Value SampleValue `json:"value"` |
| 252 | Timestamp Time `json:"timestamp"` |
| 253 | } |
| 254 | |
| 255 | func (s Scalar) String() string { |
| 256 | return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp) |
| 257 | } |
| 258 | |
| 259 | // MarshalJSON implements json.Marshaler. |
| 260 | func (s Scalar) MarshalJSON() ([]byte, error) { |
| 261 | v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64) |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 262 | return json.Marshal([...]interface{}{s.Timestamp, v}) |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 263 | } |
| 264 | |
| 265 | // UnmarshalJSON implements json.Unmarshaler. |
| 266 | func (s *Scalar) UnmarshalJSON(b []byte) error { |
| 267 | var f string |
| 268 | v := [...]interface{}{&s.Timestamp, &f} |
| 269 | |
| 270 | if err := json.Unmarshal(b, &v); err != nil { |
| 271 | return err |
| 272 | } |
| 273 | |
| 274 | value, err := strconv.ParseFloat(f, 64) |
| 275 | if err != nil { |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 276 | return fmt.Errorf("error parsing sample value: %w", err) |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 277 | } |
| 278 | s.Value = SampleValue(value) |
| 279 | return nil |
| 280 | } |
| 281 | |
| 282 | // String is a string value evaluated at the set timestamp. |
| 283 | type String struct { |
| 284 | Value string `json:"value"` |
| 285 | Timestamp Time `json:"timestamp"` |
| 286 | } |
| 287 | |
| 288 | func (s *String) String() string { |
| 289 | return s.Value |
| 290 | } |
| 291 | |
| 292 | // MarshalJSON implements json.Marshaler. |
| 293 | func (s String) MarshalJSON() ([]byte, error) { |
| 294 | return json.Marshal([]interface{}{s.Timestamp, s.Value}) |
| 295 | } |
| 296 | |
| 297 | // UnmarshalJSON implements json.Unmarshaler. |
| 298 | func (s *String) UnmarshalJSON(b []byte) error { |
| 299 | v := [...]interface{}{&s.Timestamp, &s.Value} |
| 300 | return json.Unmarshal(b, &v) |
| 301 | } |
| 302 | |
| 303 | // Vector is basically only an alias for Samples, but the |
| 304 | // contract is that in a Vector, all Samples have the same timestamp. |
| 305 | type Vector []*Sample |
| 306 | |
| 307 | func (vec Vector) String() string { |
| 308 | entries := make([]string, len(vec)) |
| 309 | for i, s := range vec { |
| 310 | entries[i] = s.String() |
| 311 | } |
| 312 | return strings.Join(entries, "\n") |
| 313 | } |
| 314 | |
| 315 | func (vec Vector) Len() int { return len(vec) } |
| 316 | func (vec Vector) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] } |
| 317 | |
| 318 | // Less compares first the metrics, then the timestamp. |
| 319 | func (vec Vector) Less(i, j int) bool { |
| 320 | switch { |
| 321 | case vec[i].Metric.Before(vec[j].Metric): |
| 322 | return true |
| 323 | case vec[j].Metric.Before(vec[i].Metric): |
| 324 | return false |
| 325 | case vec[i].Timestamp.Before(vec[j].Timestamp): |
| 326 | return true |
| 327 | default: |
| 328 | return false |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | // Equal compares two sets of samples and returns true if they are equal. |
| 333 | func (vec Vector) Equal(o Vector) bool { |
| 334 | if len(vec) != len(o) { |
| 335 | return false |
| 336 | } |
| 337 | |
| 338 | for i, sample := range vec { |
| 339 | if !sample.Equal(o[i]) { |
| 340 | return false |
| 341 | } |
| 342 | } |
| 343 | return true |
| 344 | } |
| 345 | |
| 346 | // Matrix is a list of time series. |
| 347 | type Matrix []*SampleStream |
| 348 | |
| 349 | func (m Matrix) Len() int { return len(m) } |
| 350 | func (m Matrix) Less(i, j int) bool { return m[i].Metric.Before(m[j].Metric) } |
| 351 | func (m Matrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] } |
| 352 | |
| Abhay Kumar | a2ae599 | 2025-11-10 14:02:24 +0000 | [diff] [blame^] | 353 | func (m Matrix) String() string { |
| 354 | matCp := make(Matrix, len(m)) |
| 355 | copy(matCp, m) |
| khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 356 | sort.Sort(matCp) |
| 357 | |
| 358 | strs := make([]string, len(matCp)) |
| 359 | |
| 360 | for i, ss := range matCp { |
| 361 | strs[i] = ss.String() |
| 362 | } |
| 363 | |
| 364 | return strings.Join(strs, "\n") |
| 365 | } |