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  }