github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/selfmonitor/metrics_imp_v2.go (about) 1 // Copyright 2024 iLogtail Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package selfmonitor 16 17 import ( 18 "context" 19 "errors" 20 "strconv" 21 "sync" 22 "sync/atomic" 23 "time" 24 25 "github.com/alibaba/ilogtail/pkg/helper/math" 26 "github.com/alibaba/ilogtail/pkg/logger" 27 "github.com/alibaba/ilogtail/pkg/protocol" 28 ) 29 30 var ( 31 _ CounterMetric = (*cumulativeCounterImp)(nil) 32 _ CounterMetric = (*counterImp)(nil) 33 _ CounterMetric = (*averageImp)(nil) 34 _ GaugeMetric = (*gaugeImp)(nil) 35 _ LatencyMetric = (*latencyImp)(nil) 36 _ StringMetric = (*strMetricImp)(nil) 37 38 _ CounterMetric = (*errorNumericMetric)(nil) 39 _ GaugeMetric = (*errorNumericMetric)(nil) 40 _ LatencyMetric = (*errorNumericMetric)(nil) 41 _ StringMetric = (*errorStrMetric)(nil) 42 ) 43 44 func newMetric(metricType SelfMetricType, metricSet MetricSet, labelValues []string) Metric { 45 switch metricType { 46 case CumulativeCounterType: 47 return newCumulativeCounter(metricSet, labelValues) 48 case AverageType: 49 return newAverage(metricSet, labelValues) 50 case MaxType: 51 return newMax(metricSet, labelValues) 52 case CounterType: 53 return newDeltaCounter(metricSet, labelValues) 54 case GaugeType: 55 return newGauge(metricSet, labelValues) 56 case StringType: 57 return newStringMetric(metricSet, labelValues) 58 case LatencyType: 59 return newLatency(metricSet, labelValues) 60 } 61 return newErrorMetric(metricType, errors.New("invalid metric type")) 62 } 63 64 // ErrorMetrics always return error. 65 func newErrorMetric(metricType SelfMetricType, err error) Metric { 66 switch metricType { 67 case StringType: 68 return newErrorStringMetric(err) 69 default: 70 return newErrorNumericMetric(err) 71 } 72 } 73 74 // Deprecated: Use deltaImp instead. 75 // cumulativeCounterImp is a counter metric that can be incremented or decremented. 76 // It gets the cumulative value of the counter. 77 type cumulativeCounterImp struct { 78 value int64 79 Series 80 } 81 82 func newCumulativeCounter(ms MetricSet, labelValues []string) CounterMetric { 83 c := &cumulativeCounterImp{ 84 Series: newSeries(ms, labelValues), 85 } 86 return c 87 } 88 89 func (c *cumulativeCounterImp) Add(delta int64) { 90 atomic.AddInt64(&c.value, delta) 91 } 92 93 func (c *cumulativeCounterImp) Collect() MetricValue[float64] { 94 value := atomic.LoadInt64(&c.value) 95 return MetricValue[float64]{Name: c.Name(), Value: float64(value)} 96 } 97 98 func (c *cumulativeCounterImp) Clear() { 99 atomic.StoreInt64(&c.value, 0) 100 } 101 102 func (c *cumulativeCounterImp) Serialize(log *protocol.Log) { 103 metricValue := c.Collect() 104 c.Series.SerializeWithStr(log, metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 105 } 106 107 func (c *cumulativeCounterImp) Export() map[string]string { 108 metricValue := c.Collect() 109 return c.Series.Export(metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 110 } 111 112 func (c *cumulativeCounterImp) Type() SelfMetricType { 113 return CounterType 114 } 115 116 // delta is a counter metric that can be incremented or decremented. 117 // It gets the increased value in the last window. 118 type counterImp struct { 119 value int64 120 Series 121 } 122 123 func newDeltaCounter(ms MetricSet, labelValues []string) CounterMetric { 124 c := &counterImp{ 125 Series: newSeries(ms, labelValues), 126 } 127 return c 128 } 129 130 func (c *counterImp) Add(delta int64) { 131 atomic.AddInt64(&c.value, delta) 132 } 133 134 func (c *counterImp) Collect() MetricValue[float64] { 135 value := atomic.SwapInt64(&c.value, 0) 136 return MetricValue[float64]{Name: c.Name(), Value: float64(value)} 137 } 138 139 func (c *counterImp) Clear() { 140 atomic.StoreInt64(&c.value, 0) 141 } 142 143 func (c *counterImp) Serialize(log *protocol.Log) { 144 metricValue := c.Collect() 145 c.Series.SerializeWithStr(log, metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 146 } 147 148 func (c *counterImp) Export() map[string]string { 149 metricValue := c.Collect() 150 return c.Series.Export(metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 151 } 152 153 func (c *counterImp) Type() SelfMetricType { 154 return CounterType 155 } 156 157 // gauge is a metric that represents a single numerical value that can arbitrarily go up and down. 158 type gaugeImp struct { 159 value float64 160 Series 161 } 162 163 func newGauge(ms MetricSet, labelValues []string) GaugeMetric { 164 g := &gaugeImp{ 165 Series: newSeries(ms, labelValues), 166 } 167 return g 168 } 169 170 func (g *gaugeImp) Set(f float64) { 171 math.AtomicStoreFloat64(&g.value, f) 172 } 173 174 func (g *gaugeImp) Collect() MetricValue[float64] { 175 return MetricValue[float64]{Name: g.Name(), Value: math.AtomicLoadFloat64(&g.value)} 176 } 177 178 func (g *gaugeImp) Clear() { 179 math.AtomicStoreFloat64(&g.value, 0) 180 } 181 182 func (g *gaugeImp) Serialize(log *protocol.Log) { 183 metricValue := g.Collect() 184 g.Series.SerializeWithStr(log, metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 185 } 186 187 func (g *gaugeImp) Export() map[string]string { 188 metricValue := g.Collect() 189 return g.Series.Export(metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 190 } 191 192 func (g *gaugeImp) Type() SelfMetricType { 193 return GaugeType 194 } 195 196 // averageImp is a metric to compute the average value of a series of values in the last window. 197 // if there is no value added in the last window, the previous average value will be returned. 198 type averageImp struct { 199 sync.RWMutex 200 value int64 201 count int64 202 prevAvg float64 203 Series 204 } 205 206 func newAverage(ms MetricSet, labelValues []string) CounterMetric { 207 a := &averageImp{ 208 Series: newSeries(ms, labelValues), 209 } 210 return a 211 } 212 213 func (a *averageImp) Add(f int64) { 214 a.Lock() 215 defer a.Unlock() 216 a.value += f 217 a.count++ 218 } 219 220 func (a *averageImp) Collect() MetricValue[float64] { 221 a.RLock() 222 defer a.RUnlock() 223 if a.count == 0 { 224 return MetricValue[float64]{Name: a.Name(), Value: a.prevAvg} 225 } 226 avg := float64(a.value) / float64(a.count) 227 a.prevAvg, a.value, a.count = avg, 0, 0 228 return MetricValue[float64]{Name: a.Name(), Value: avg} 229 } 230 231 func (a *averageImp) Clear() { 232 a.Lock() 233 a.value = 0 234 a.count = 0 235 a.prevAvg = 0 236 a.Unlock() 237 } 238 239 func (a *averageImp) Serialize(log *protocol.Log) { 240 metricValue := a.Collect() 241 a.Series.SerializeWithStr(log, metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 242 } 243 244 func (a *averageImp) Export() map[string]string { 245 metricValue := a.Collect() 246 return a.Series.Export(metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 247 } 248 249 func (a *averageImp) Type() SelfMetricType { 250 return GaugeType 251 } 252 253 // maxImp is a metric to compute the max value of a series of values in the last window. 254 // if there is no value added in the last window, zero will be returned. 255 type maxImp struct { 256 sync.RWMutex 257 value float64 258 Series 259 } 260 261 func newMax(ms MetricSet, labelValues []string) GaugeMetric { 262 m := &maxImp{ 263 Series: newSeries(ms, labelValues), 264 } 265 return m 266 } 267 268 func (m *maxImp) Set(f float64) { 269 m.Lock() 270 defer m.Unlock() 271 if f > m.value { 272 m.value = f 273 } 274 } 275 276 func (m *maxImp) Collect() MetricValue[float64] { 277 m.RLock() 278 defer m.RUnlock() 279 metric := MetricValue[float64]{Name: m.Name(), Value: m.value} 280 m.value = 0 281 return metric 282 } 283 284 func (m *maxImp) Export() map[string]string { 285 metricValue := m.Collect() 286 return m.Series.Export(metricValue.Name, strconv.FormatFloat(metricValue.Value, 'f', 4, 64)) 287 } 288 289 func (m *maxImp) Type() SelfMetricType { 290 return GaugeType 291 } 292 293 // latencyImp is a metric to compute the average latency of a series of values in the last window. 294 type latencyImp struct { 295 sync.Mutex 296 count int64 297 latencySum float64 298 Series 299 } 300 301 func newLatency(ms MetricSet, labelValues []string) LatencyMetric { 302 l := &latencyImp{ 303 Series: newSeries(ms, labelValues), 304 } 305 return l 306 } 307 308 func (l *latencyImp) Observe(f float64) { 309 l.Lock() 310 defer l.Unlock() 311 l.count++ 312 l.latencySum += f 313 } 314 315 func (l *latencyImp) Record(d time.Duration) { 316 l.Observe(float64(d)) 317 } 318 319 func (l *latencyImp) Collect() MetricValue[float64] { 320 l.Lock() 321 defer l.Unlock() 322 if l.count == 0 { 323 return MetricValue[float64]{Name: l.Name(), Value: 0} 324 } 325 avg := l.latencySum / float64(l.count) 326 l.count, l.latencySum = 0, 0 327 return MetricValue[float64]{Name: l.Name(), Value: avg} 328 } 329 330 func (l *latencyImp) Clear() { 331 l.Lock() 332 defer l.Unlock() 333 l.count = 0 334 l.latencySum = 0 335 } 336 337 func (l *latencyImp) Serialize(log *protocol.Log) { 338 metricValue := l.Collect() 339 l.Series.SerializeWithStr(log, metricValue.Name, strconv.FormatFloat(metricValue.Value/1000, 'f', 4, 64)) // ns to us 340 } 341 342 func (l *latencyImp) Export() map[string]string { 343 metricValue := l.Collect() 344 return l.Series.Export(metricValue.Name, strconv.FormatFloat(metricValue.Value/1000, 'f', 4, 64)) // ns to us 345 } 346 347 func (l *latencyImp) Type() SelfMetricType { 348 return GaugeType 349 } 350 351 // strMetricImp is a metric that represents a single string value. 352 type strMetricImp struct { 353 sync.RWMutex 354 value string 355 Series 356 } 357 358 func newStringMetric(ms MetricSet, labelValues []string) StringMetric { 359 s := &strMetricImp{ 360 Series: newSeries(ms, labelValues), 361 } 362 return s 363 } 364 365 func (s *strMetricImp) Set(str string) { 366 s.Lock() 367 defer s.Unlock() 368 s.value = str 369 } 370 371 func (s *strMetricImp) Collect() MetricValue[string] { 372 s.RLock() 373 defer s.RUnlock() 374 return MetricValue[string]{Name: s.Name(), Value: s.value} 375 } 376 377 func (s *strMetricImp) Clear() { 378 s.Lock() 379 s.value = "" 380 s.Unlock() 381 } 382 383 func (s *strMetricImp) Serialize(log *protocol.Log) { 384 metricValue := s.Collect() 385 s.Series.SerializeWithStr(log, metricValue.Name, metricValue.Value) 386 } 387 388 func (s *strMetricImp) Export() map[string]string { 389 metricValue := s.Collect() 390 return s.Series.Export(metricValue.Name, metricValue.Value) 391 } 392 393 func (s *strMetricImp) Type() SelfMetricType { 394 return GaugeType 395 } 396 397 type Series struct { 398 MetricSet 399 labelValues []string 400 } 401 402 func newSeries(ms MetricSet, labelValues []string) Series { 403 var indexToStore []string 404 if labelValues != nil { 405 indexToStore = make([]string, len(labelValues)) 406 copy(indexToStore, labelValues) 407 } 408 409 return Series{ 410 MetricSet: ms, 411 labelValues: indexToStore, 412 } 413 } 414 415 func (s Series) SerializeWithStr(log *protocol.Log, metricName, metricValueStr string) { 416 log.Contents = append(log.Contents, 417 &protocol.Log_Content{Key: metricName, Value: metricValueStr}, 418 &protocol.Log_Content{Key: SelfMetricNameKey, Value: metricName}) 419 420 for _, v := range s.ConstLabels() { 421 log.Contents = append(log.Contents, &protocol.Log_Content{Key: v.Key, Value: v.Value}) 422 } 423 424 labelNames := s.LabelKeys() 425 for i, v := range s.labelValues { 426 log.Contents = append(log.Contents, &protocol.Log_Content{Key: labelNames[i], Value: v}) 427 } 428 } 429 430 func (s Series) Export(metricName, metricValue string) map[string]string { 431 ret := make(map[string]string, len(s.ConstLabels())+len(s.labelValues)+2) 432 ret[metricName] = metricValue 433 ret[SelfMetricNameKey] = metricName 434 435 for _, v := range s.ConstLabels() { 436 ret[v.Key] = v.Value 437 } 438 439 for i, v := range s.labelValues { 440 ret[s.LabelKeys()[i]] = v 441 } 442 443 return ret 444 } 445 446 /* 447 Following are the metrics returned when WithLabel encountered an error. 448 */ 449 type errorNumericMetric struct { 450 err error 451 } 452 453 func (e *errorNumericMetric) Add(f int64) { 454 logger.Warning(context.Background(), "METRIC_WITH_LABEL_ALARM", "add", e.err) 455 } 456 457 func (e *errorNumericMetric) Set(f float64) { 458 logger.Warning(context.Background(), "METRIC_WITH_LABEL_ALARM", "set", e.err) 459 } 460 461 func (e *errorNumericMetric) Observe(f float64) { 462 logger.Warning(context.Background(), "METRIC_WITH_LABEL_ALARM", "observe", e.err) 463 } 464 465 func (e *errorNumericMetric) Serialize(log *protocol.Log) {} 466 467 func (e *errorNumericMetric) Export() map[string]string { 468 return nil 469 } 470 471 func (e *errorNumericMetric) Type() SelfMetricType { 472 return CounterType 473 } 474 475 func (e *errorNumericMetric) Collect() MetricValue[float64] { 476 return MetricValue[float64]{Name: "", Value: 0} 477 } 478 479 func (e *errorNumericMetric) Clear() {} 480 481 func newErrorNumericMetric(err error) *errorNumericMetric { 482 return &errorNumericMetric{err: err} 483 } 484 485 type errorStrMetric struct { 486 errorNumericMetric 487 } 488 489 func (e errorStrMetric) Set(s string) { 490 logger.Warning(context.Background(), "METRIC_WITH_LABEL_ALARM", "set", e.err) 491 } 492 493 func (e errorStrMetric) Collect() MetricValue[string] { 494 return MetricValue[string]{Name: "", Value: ""} 495 } 496 497 func newErrorStringMetric(err error) StringMetric { 498 return &errorStrMetric{errorNumericMetric: errorNumericMetric{err: err}} 499 }